master
1const std = @import("../std.zig");
2const builtin = @import("builtin");
3const math = std.math;
4const expect = std.testing.expect;
5const expectEqual = std.testing.expectEqual;
6const expectApproxEqAbs = std.testing.expectApproxEqAbs;
7
8pub fn Modf(comptime T: type) type {
9 return struct {
10 fpart: T,
11 ipart: T,
12 };
13}
14
15/// Returns the integer and fractional floating-point numbers that sum to x. The sign of each
16/// result is the same as the sign of x.
17/// In comptime, may be used with comptime_float
18///
19/// Special Cases:
20/// - modf(+-inf) = +-inf, nan
21/// - modf(nan) = nan, nan
22pub fn modf(x: anytype) Modf(@TypeOf(x)) {
23 const ipart = @trunc(x);
24 return .{
25 .ipart = ipart,
26 .fpart = x - ipart,
27 };
28}
29
30test modf {
31 inline for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
32 const epsilon: comptime_float = @max(1e-6, math.floatEps(T));
33
34 var r: Modf(T) = undefined;
35
36 r = modf(@as(T, 1.0));
37 try expectEqual(1.0, r.ipart);
38 try expectEqual(0.0, r.fpart);
39
40 r = modf(@as(T, 0.34682));
41 try expectEqual(0.0, r.ipart);
42 try expectApproxEqAbs(@as(T, 0.34682), r.fpart, epsilon);
43
44 r = modf(@as(T, 2.54576));
45 try expectEqual(2.0, r.ipart);
46 try expectApproxEqAbs(0.54576, r.fpart, epsilon);
47
48 r = modf(@as(T, 3.9782));
49 try expectEqual(3.0, r.ipart);
50 try expectApproxEqAbs(0.9782, r.fpart, epsilon);
51 }
52}
53
54/// Generate a namespace of tests for modf on values of the given type
55fn ModfTests(comptime T: type) type {
56 return struct {
57 test "normal" {
58 const epsilon: comptime_float = @max(1e-6, math.floatEps(T));
59 var r: Modf(T) = undefined;
60
61 r = modf(@as(T, 1.0));
62 try expectEqual(1.0, r.ipart);
63 try expectEqual(0.0, r.fpart);
64
65 r = modf(@as(T, 0.34682));
66 try expectEqual(0.0, r.ipart);
67 try expectApproxEqAbs(0.34682, r.fpart, epsilon);
68
69 r = modf(@as(T, 3.97812));
70 try expectEqual(3.0, r.ipart);
71 // account for precision error
72 const expected_a: T = 3.97812 - @as(T, 3);
73 try expectApproxEqAbs(expected_a, r.fpart, epsilon);
74
75 r = modf(@as(T, 43874.3));
76 try expectEqual(43874.0, r.ipart);
77 // account for precision error
78 const expected_b: T = 43874.3 - @as(T, 43874.0);
79 try expectApproxEqAbs(expected_b, r.fpart, epsilon);
80
81 r = modf(@as(T, 1234.340780));
82 try expectEqual(1234.0, r.ipart);
83 // account for precision error
84 const expected_c: T = 1234.340780 - @as(T, 1234);
85 try expectApproxEqAbs(expected_c, r.fpart, epsilon);
86 }
87 test "vector" {
88 if (builtin.os.tag.isDarwin() and builtin.cpu.arch == .aarch64) return error.SkipZigTest;
89 if (builtin.cpu.arch == .s390x) return error.SkipZigTest;
90 if (comptime builtin.cpu.has(.loongarch, .lsx)) return error.SkipZigTest; // https://github.com/llvm/llvm-project/issues/159529
91
92 const widths = [_]comptime_int{ 1, 2, 3, 4, 8, 16 };
93
94 inline for (widths) |len| {
95 const V: type = @Vector(len, T);
96 var r: Modf(V) = undefined;
97
98 r = modf(@as(V, @splat(1.0)));
99 try expectEqual(@as(V, @splat(1.0)), r.ipart);
100 try expectEqual(@as(V, @splat(0.0)), r.fpart);
101
102 r = modf(@as(V, @splat(2.75)));
103 try expectEqual(@as(V, @splat(2.0)), r.ipart);
104 try expectEqual(@as(V, @splat(0.75)), r.fpart);
105
106 r = modf(@as(V, @splat(0.2)));
107 try expectEqual(@as(V, @splat(0.0)), r.ipart);
108 try expectEqual(@as(V, @splat(0.2)), r.fpart);
109
110 r = modf(std.simd.iota(T, len) + @as(V, @splat(0.5)));
111 try expectEqual(std.simd.iota(T, len), r.ipart);
112 try expectEqual(@as(V, @splat(0.5)), r.fpart);
113 }
114 }
115 test "inf" {
116 var r: Modf(T) = undefined;
117
118 r = modf(math.inf(T));
119 try expect(math.isPositiveInf(r.ipart) and math.isNan(r.fpart));
120
121 r = modf(-math.inf(T));
122 try expect(math.isNegativeInf(r.ipart) and math.isNan(r.fpart));
123 }
124 test "nan" {
125 const r: Modf(T) = modf(math.nan(T));
126 try expect(math.isNan(r.ipart) and math.isNan(r.fpart));
127 }
128 };
129}
130
131comptime {
132 for ([_]type{ f16, f32, f64, f80, f128 }) |T| {
133 _ = ModfTests(T);
134 }
135}