1const builtin = @import("builtin");
2const std = @import("std");
3
4const c = std.c;
5const math = std.math;
6
7const testing = std.testing;
8const expect = testing.expect;
9const expectEqual = testing.expectEqual;
10const expectApproxEqAbs = testing.expectApproxEqAbs;
11const expectApproxEqRel = testing.expectApproxEqRel;
12
13fn testModf(comptime T: type) !void {
14 const f = switch (T) {
15 f32 => c.modff,
16 f64 => c.modf,
17 c_longdouble => c.modfl,
18 else => @compileError("modf not implemented for " ++ @typeName(T)),
19 };
20
21 var int: T = undefined;
22 const iptr = ∫
23 const eps_val: comptime_float = @max(1e-6, math.floatEps(T));
24
25 const normal_frac = f(@as(T, 1234.567), iptr);
26 // Account for precision error
27 const expected = 1234.567 - @as(T, 1234);
28 try expectApproxEqAbs(expected, normal_frac, eps_val);
29 try expectApproxEqRel(@as(T, 1234.0), iptr.*, eps_val);
30
31 // When `x` is a NaN, NaN is returned and `*iptr` is set to NaN
32 const nan_frac = f(math.nan(T), iptr);
33 try expect(math.isNan(nan_frac));
34 try expect(math.isNan(iptr.*));
35
36 // When `x` is positive infinity, +0 is returned and `*iptr` is set to
37 // positive infinity
38 const pos_zero_frac = f(math.inf(T), iptr);
39 try expectEqual(0.0, pos_zero_frac);
40 try expect(math.isPositiveInf(iptr.*));
41
42 // When `x` is negative infinity, -0 is returned and `*iptr` is set to
43 // negative infinity
44 const neg_zero_frac = f(-math.inf(T), iptr);
45 try expectEqual(-0.0, neg_zero_frac);
46 try expect(math.isNegativeInf(iptr.*));
47
48 // Return -0 when `x` is a negative integer
49 const nz_frac = f(@as(T, -1000.0), iptr);
50 try expectEqual(-0.0, nz_frac);
51 try expectEqual(@as(T, -1000.0), iptr.*);
52
53 // Return +0 when `x` is a positive integer
54 const pz_frac = f(@as(T, 1000.0), iptr);
55 try expectEqual(0.0, pz_frac);
56 try expectEqual(@as(T, 1000.0), iptr.*);
57}
58
59test "modf" {
60 try testModf(f64);
61}
62
63test "modff" {
64 if (builtin.cpu.arch == .x86 and builtin.target.os.tag == .windows and builtin.target.abi != .gnu) return; // mingw-only
65
66 try testModf(f32);
67}
68
69test "modfl" {
70 if (builtin.cpu.arch.isX86() and builtin.target.os.tag == .windows and builtin.target.abi != .gnu) return; // mingw-only
71 if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO: see https://codeberg.org/ziglang/zig/issues/30976
72
73 try testModf(c_longdouble);
74}
75
76fn testRintSpecial(comptime T: type) !void {
77 const f = switch (T) {
78 f32 => c.rintf,
79 f64 => c.rint,
80 c_longdouble => c.rintl,
81 else => @compileError("rint not implemented for" ++ @typeName(T)),
82 };
83
84 // For the special cases, x itself should be returned
85 try expectEqual(0.0, f(0.0));
86 try expectEqual(-0.0, f(-0.0));
87 try expectEqual(math.inf(T), f(math.inf(T)));
88 try expectEqual(-math.inf(T), f(-math.inf(T)));
89 try expect(math.isNan(f(math.nan(T))));
90}
91
92fn testRintNormal(comptime T: type) !void {
93 const f = switch (T) {
94 f32 => c.rintf,
95 f64 => c.rint,
96 c_longdouble => c.rintl,
97 else => @compileError("rint not implemented for" ++ @typeName(T)),
98 };
99
100 // Positive numbers round correctly
101 try expectEqual(@as(T, 42.0), f(42.2));
102 try expectEqual(@as(T, 42.0), f(41.8));
103 try expectEqual(@as(T, 16_777_216.0), f(16_777_215.6));
104
105 // Negative numbers round correctly
106 try expectEqual(@as(T, -6.0), f(-5.9));
107 try expectEqual(@as(T, -6.0), f(-6.1));
108 // TODO: negative `long double`s close to `-n.5` seem to round to `-n.5`
109 // instead of either `-n.0` or `-(n-1).0` on NetBSD. For example, this
110 // case would round to `-16_777_215.5`.
111 if (!(T == c_longdouble and builtin.target.os.tag == .netbsd)) {
112 try expectEqual(@as(T, -16_777_215.0), f(-16_777_215.4));
113 }
114
115 // No rounding needed test
116 try expectEqual(@as(T, 5.0), f(5.0));
117 try expectEqual(@as(T, -10.0), f(-10.0));
118 try expectEqual(@as(T, 0.0), f(0.0));
119
120 // Very large numbers return unchanged
121 const large: T = 9007199254740992.0; // 2^53
122 try expectEqual(large, f(large));
123 try expectEqual(-large, f(-large));
124
125 // Small positive numbers round to zero
126 try expectEqual(@as(T, 0.0), f(0.3));
127
128 // TODO: negative `long double`s close to `-n.5` seem to round to `-n.5`
129 // instead of either `-n.0` or `-(n-1).0` on NetBSD. For example, this
130 // case would round to `-0.5`.
131 if (!(T == c_longdouble and builtin.target.os.tag == .netbsd)) {
132 // Small negative numbers round to negative zero
133 try expectEqual(@as(T, -0.0), f(-0.3));
134 }
135
136 // Exact half rounds to nearest even (banker's rounding)
137 try expectEqual(@as(T, 2.0), f(2.5));
138 try expectEqual(@as(T, 4.0), f(3.5));
139}
140
141test "rintf.special" {
142 try testRintSpecial(f32);
143}
144
145test "rintf.normal" {
146 try testRintNormal(f32);
147}
148
149test "rint.special" {
150 try testRintSpecial(f64);
151}
152
153test "rint.normal" {
154 try testRintNormal(f64);
155}
156
157test "rintl.special" {
158 if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO: see https://codeberg.org/ziglang/zig/issues/30976
159
160 try testRintSpecial(c_longdouble);
161}
162
163test "rintl.normal" {
164 if (builtin.target.cpu.arch.isPowerPC()) return error.SkipZigTest; // TODO: see https://codeberg.org/ziglang/zig/issues/30976
165
166 try testRintNormal(c_longdouble);
167}