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