| 1 | const std = @import("../std.zig"); |
| 2 | const math = std.math; |
| 3 | const expect = std.testing.expect; |
| 4 | const expectEqual = std.testing.expectEqual; |
| 5 | const expectApproxEqAbs = std.testing.expectApproxEqAbs; |
| 6 | |
| 7 | pub 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 |
| 21 | pub fn modf(x: anytype) Modf(@TypeOf(x)) { |
| 22 | const ipart = @trunc(x); |
| 23 | return .{ |
| 24 | .ipart = ipart, |
| 25 | .fpart = x - ipart, |
| 26 | }; |
| 27 | } |
| 28 | |
| 29 | test 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 |
| 54 | fn 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 | |
| 126 | comptime { |
| 127 | for ([_]type{ f16, f32, f64, f80, f128 }) |T| { |
| 128 | _ = ModfTests(T); |
| 129 | } |
| 130 | } |