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}