| author | |
| committer | |
| log | 6c0114e04436404b1c59b66125733350b4be2f5b |
| tree | 39236e6a220f0ce1941caa8d8fd7130d9ef7a318 |
| parent | ab658e32bdfa234b6c33f0718cac7a23fbd4074f |
8 files changed, 347 insertions(+), 183 deletions(-)
lib/std/special/compiler_rt.zig+7-7| ... | @@ -726,6 +726,11 @@ comptime { | ... | @@ -726,6 +726,11 @@ comptime { |
| 726 | 726 | ||
| 727 | if (!is_test) { | 727 | if (!is_test) { |
| 728 | @export(fmodl, .{ .name = "fmodl", .linkage = linkage }); | 728 | @export(fmodl, .{ .name = "fmodl", .linkage = linkage }); |
| 729 | if (long_double_is_f80) { | ||
| 730 | @export(fmodl, .{ .name = "fmodx", .linkage = linkage }); | ||
| 731 | } else { | ||
| 732 | @export(fmodx, .{ .name = "fmodx", .linkage = linkage }); | ||
| 733 | } | ||
| 729 | if (long_double_is_f128) { | 734 | if (long_double_is_f128) { |
| 730 | @export(fmodl, .{ .name = "fmodq", .linkage = linkage }); | 735 | @export(fmodl, .{ .name = "fmodq", .linkage = linkage }); |
| 731 | } else { | 736 | } else { |
| ... | @@ -884,13 +889,8 @@ fn ceill(x: c_longdouble) callconv(.C) c_longdouble { | ... | @@ -884,13 +889,8 @@ fn ceill(x: c_longdouble) callconv(.C) c_longdouble { |
| 884 | return math.ceil(x); | 889 | return math.ceil(x); |
| 885 | } | 890 | } |
| 886 | 891 | ||
| 887 | const fmodq = @import("compiler_rt/floatfmodq.zig").fmodq; | 892 | const fmodq = @import("compiler_rt/fmodq.zig").fmodq; |
| 888 | fn fmodl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble { | 893 | const fmodx = @import("compiler_rt/fmodx.zig").fmodx; |
| 889 | if (!long_double_is_f128) { | ||
| 890 | @panic("TODO implement this"); | ||
| 891 | } | ||
| 892 | return @floatCast(c_longdouble, fmodq(x, y)); | ||
| 893 | } | ||
| 894 | 894 | ||
| 895 | // Avoid dragging in the runtime safety mechanisms into this .o file, | 895 | // Avoid dragging in the runtime safety mechanisms into this .o file, |
| 896 | // unless we're trying to test this file. | 896 | // unless we're trying to test this file. |
lib/std/special/compiler_rt/divdf3.zig+3-4| ... | @@ -314,12 +314,11 @@ pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { | ... | @@ -314,12 +314,11 @@ pub fn wideMultiply(comptime Z: type, a: Z, b: Z, hi: *Z, lo: *Z) void { |
| 314 | pub fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T).Float.bits)) i32 { | 314 | pub fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T).Float.bits)) i32 { |
| 315 | @setRuntimeSafety(builtin.is_test); | 315 | @setRuntimeSafety(builtin.is_test); |
| 316 | const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); | 316 | const Z = std.meta.Int(.unsigned, @typeInfo(T).Float.bits); |
| 317 | const significandBits = std.math.floatMantissaBits(T); | 317 | const integerBit = @as(Z, 1) << std.math.floatFractionalBits(T); |
| 318 | const implicitBit = @as(Z, 1) << significandBits; | ||
| 319 | 318 | ||
| 320 | const shift = @clz(Z, significand.*) - @clz(Z, implicitBit); | 319 | const shift = @clz(Z, significand.*) - @clz(Z, integerBit); |
| 321 | significand.* <<= @intCast(std.math.Log2Int(Z), shift); | 320 | significand.* <<= @intCast(std.math.Log2Int(Z), shift); |
| 322 | return 1 - shift; | 321 | return @as(i32, 1) - shift; |
| 323 | } | 322 | } |
| 324 | 323 | ||
| 325 | pub fn __aeabi_ddiv(a: f64, b: f64) callconv(.AAPCS) f64 { | 324 | pub fn __aeabi_ddiv(a: f64, b: f64) callconv(.AAPCS) f64 { |
lib/std/special/compiler_rt/floatfmodq.zig deleted-126| ... | @@ -1,126 +0,0 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("std"); | ||
| 3 | |||
| 4 | // fmodq - floating modulo large, returns the remainder of division for f128 types | ||
| 5 | // Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits | ||
| 6 | pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { | ||
| 7 | @setRuntimeSafety(builtin.is_test); | ||
| 8 | var amod = a; | ||
| 9 | var bmod = b; | ||
| 10 | const aPtr_u64 = @ptrCast([*]u64, &amod); | ||
| 11 | const bPtr_u64 = @ptrCast([*]u64, &bmod); | ||
| 12 | const aPtr_u16 = @ptrCast([*]u16, &amod); | ||
| 13 | const bPtr_u16 = @ptrCast([*]u16, &bmod); | ||
| 14 | |||
| 15 | const exp_and_sign_index = comptime switch (builtin.target.cpu.arch.endian()) { | ||
| 16 | .Little => 7, | ||
| 17 | .Big => 0, | ||
| 18 | }; | ||
| 19 | const low_index = comptime switch (builtin.target.cpu.arch.endian()) { | ||
| 20 | .Little => 0, | ||
| 21 | .Big => 1, | ||
| 22 | }; | ||
| 23 | const high_index = comptime switch (builtin.target.cpu.arch.endian()) { | ||
| 24 | .Little => 1, | ||
| 25 | .Big => 0, | ||
| 26 | }; | ||
| 27 | |||
| 28 | const signA = aPtr_u16[exp_and_sign_index] & 0x8000; | ||
| 29 | var expA = @intCast(i32, (aPtr_u16[exp_and_sign_index] & 0x7fff)); | ||
| 30 | var expB = bPtr_u16[exp_and_sign_index] & 0x7fff; | ||
| 31 | |||
| 32 | // There are 3 cases where the answer is undefined, check for: | ||
| 33 | // - fmodq(val, 0) | ||
| 34 | // - fmodq(val, NaN) | ||
| 35 | // - fmodq(inf, val) | ||
| 36 | // The sign on checked values does not matter. | ||
| 37 | // Doing (a * b) / (a * b) procudes undefined results | ||
| 38 | // because the three cases always produce undefined calculations: | ||
| 39 | // - 0 / 0 | ||
| 40 | // - val * NaN | ||
| 41 | // - inf / inf | ||
| 42 | if (b == 0 or std.math.isNan(b) or expA == 0x7fff) { | ||
| 43 | return (a * b) / (a * b); | ||
| 44 | } | ||
| 45 | |||
| 46 | // Remove the sign from both | ||
| 47 | aPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expA)); | ||
| 48 | bPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expB)); | ||
| 49 | if (amod <= bmod) { | ||
| 50 | if (amod == bmod) { | ||
| 51 | return 0 * a; | ||
| 52 | } | ||
| 53 | return a; | ||
| 54 | } | ||
| 55 | |||
| 56 | if (expA == 0) { | ||
| 57 | amod *= 0x1p120; | ||
| 58 | expA = aPtr_u16[exp_and_sign_index] -% 120; | ||
| 59 | } | ||
| 60 | |||
| 61 | if (expB == 0) { | ||
| 62 | bmod *= 0x1p120; | ||
| 63 | expB = bPtr_u16[exp_and_sign_index] -% 120; | ||
| 64 | } | ||
| 65 | |||
| 66 | // OR in extra non-stored mantissa digit | ||
| 67 | var highA: u64 = (aPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48; | ||
| 68 | var highB: u64 = (bPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48; | ||
| 69 | var lowA: u64 = aPtr_u64[low_index]; | ||
| 70 | var lowB: u64 = bPtr_u64[low_index]; | ||
| 71 | |||
| 72 | while (expA > expB) : (expA -= 1) { | ||
| 73 | var high = highA -% highB; | ||
| 74 | var low = lowA -% lowB; | ||
| 75 | if (lowA < lowB) { | ||
| 76 | high = highA -% 1; | ||
| 77 | } | ||
| 78 | if (high >> 63 == 0) { | ||
| 79 | if ((high | low) == 0) { | ||
| 80 | return 0 * a; | ||
| 81 | } | ||
| 82 | highA = 2 *% high + (low >> 63); | ||
| 83 | lowA = 2 *% low; | ||
| 84 | } else { | ||
| 85 | highA = 2 *% highA + (lowA >> 63); | ||
| 86 | lowA = 2 *% lowA; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | var high = highA -% highB; | ||
| 91 | var low = lowA -% lowB; | ||
| 92 | if (lowA < lowB) { | ||
| 93 | high -= 1; | ||
| 94 | } | ||
| 95 | if (high >> 63 == 0) { | ||
| 96 | if ((high | low) == 0) { | ||
| 97 | return 0 * a; | ||
| 98 | } | ||
| 99 | highA = high; | ||
| 100 | lowA = low; | ||
| 101 | } | ||
| 102 | |||
| 103 | while (highA >> 48 == 0) { | ||
| 104 | highA = 2 *% highA + (lowA >> 63); | ||
| 105 | lowA = 2 *% lowA; | ||
| 106 | expA = expA - 1; | ||
| 107 | } | ||
| 108 | |||
| 109 | // Overwrite the current amod with the values in highA and lowA | ||
| 110 | aPtr_u64[high_index] = highA; | ||
| 111 | aPtr_u64[low_index] = lowA; | ||
| 112 | |||
| 113 | // Combine the exponent with the sign, normalize if happend to be denormalized | ||
| 114 | if (expA <= 0) { | ||
| 115 | aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, (expA +% 120))) | signA; | ||
| 116 | amod *= 0x1p-120; | ||
| 117 | } else { | ||
| 118 | aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, expA)) | signA; | ||
| 119 | } | ||
| 120 | |||
| 121 | return amod; | ||
| 122 | } | ||
| 123 | |||
| 124 | test { | ||
| 125 | _ = @import("floatfmodq_test.zig"); | ||
| 126 | } | ||
lib/std/special/compiler_rt/floatfmodq_test.zig deleted-46| ... | @@ -1,46 +0,0 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const fmodq = @import("floatfmodq.zig"); | ||
| 3 | const testing = std.testing; | ||
| 4 | |||
| 5 | fn test_fmodq(a: f128, b: f128, exp: f128) !void { | ||
| 6 | const res = fmodq.fmodq(a, b); | ||
| 7 | try testing.expect(exp == res); | ||
| 8 | } | ||
| 9 | |||
| 10 | fn test_fmodq_nans() !void { | ||
| 11 | try testing.expect(std.math.isNan(fmodq.fmodq(1.0, std.math.nan(f128)))); | ||
| 12 | try testing.expect(std.math.isNan(fmodq.fmodq(1.0, -std.math.nan(f128)))); | ||
| 13 | try testing.expect(std.math.isNan(fmodq.fmodq(std.math.nan(f128), 1.0))); | ||
| 14 | try testing.expect(std.math.isNan(fmodq.fmodq(-std.math.nan(f128), 1.0))); | ||
| 15 | } | ||
| 16 | |||
| 17 | fn test_fmodq_infs() !void { | ||
| 18 | try testing.expect(fmodq.fmodq(1.0, std.math.inf(f128)) == 1.0); | ||
| 19 | try testing.expect(fmodq.fmodq(1.0, -std.math.inf(f128)) == 1.0); | ||
| 20 | try testing.expect(std.math.isNan(fmodq.fmodq(std.math.inf(f128), 1.0))); | ||
| 21 | try testing.expect(std.math.isNan(fmodq.fmodq(-std.math.inf(f128), 1.0))); | ||
| 22 | } | ||
| 23 | |||
| 24 | test "fmodq" { | ||
| 25 | try test_fmodq(6.8, 4.0, 2.8); | ||
| 26 | try test_fmodq(6.8, -4.0, 2.8); | ||
| 27 | try test_fmodq(-6.8, 4.0, -2.8); | ||
| 28 | try test_fmodq(-6.8, -4.0, -2.8); | ||
| 29 | try test_fmodq(3.0, 2.0, 1.0); | ||
| 30 | try test_fmodq(-5.0, 3.0, -2.0); | ||
| 31 | try test_fmodq(3.0, 2.0, 1.0); | ||
| 32 | try test_fmodq(1.0, 2.0, 1.0); | ||
| 33 | try test_fmodq(0.0, 1.0, 0.0); | ||
| 34 | try test_fmodq(-0.0, 1.0, -0.0); | ||
| 35 | try test_fmodq(7046119.0, 5558362.0, 1487757.0); | ||
| 36 | try test_fmodq(9010357.0, 1957236.0, 1181413.0); | ||
| 37 | |||
| 38 | // Denormals | ||
| 39 | const a: f128 = 0xedcb34a235253948765432134674p-16494; | ||
| 40 | const b: f128 = 0x5d2e38791cfbc0737402da5a9518p-16494; | ||
| 41 | const exp: f128 = 0x336ec3affb2db8618e4e7d5e1c44p-16494; | ||
| 42 | try test_fmodq(a, b, exp); | ||
| 43 | |||
| 44 | try test_fmodq_nans(); | ||
| 45 | try test_fmodq_infs(); | ||
| 46 | } | ||
lib/std/special/compiler_rt/fmodq.zig created+126| ... | @@ -0,0 +1,126 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("std"); | ||
| 3 | |||
| 4 | // fmodq - floating modulo large, returns the remainder of division for f128 types | ||
| 5 | // Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits | ||
| 6 | pub fn fmodq(a: f128, b: f128) callconv(.C) f128 { | ||
| 7 | @setRuntimeSafety(builtin.is_test); | ||
| 8 | var amod = a; | ||
| 9 | var bmod = b; | ||
| 10 | const aPtr_u64 = @ptrCast([*]u64, &amod); | ||
| 11 | const bPtr_u64 = @ptrCast([*]u64, &bmod); | ||
| 12 | const aPtr_u16 = @ptrCast([*]u16, &amod); | ||
| 13 | const bPtr_u16 = @ptrCast([*]u16, &bmod); | ||
| 14 | |||
| 15 | const exp_and_sign_index = comptime switch (builtin.target.cpu.arch.endian()) { | ||
| 16 | .Little => 7, | ||
| 17 | .Big => 0, | ||
| 18 | }; | ||
| 19 | const low_index = comptime switch (builtin.target.cpu.arch.endian()) { | ||
| 20 | .Little => 0, | ||
| 21 | .Big => 1, | ||
| 22 | }; | ||
| 23 | const high_index = comptime switch (builtin.target.cpu.arch.endian()) { | ||
| 24 | .Little => 1, | ||
| 25 | .Big => 0, | ||
| 26 | }; | ||
| 27 | |||
| 28 | const signA = aPtr_u16[exp_and_sign_index] & 0x8000; | ||
| 29 | var expA = @intCast(i32, (aPtr_u16[exp_and_sign_index] & 0x7fff)); | ||
| 30 | var expB = @intCast(i32, (bPtr_u16[exp_and_sign_index] & 0x7fff)); | ||
| 31 | |||
| 32 | // There are 3 cases where the answer is undefined, check for: | ||
| 33 | // - fmodq(val, 0) | ||
| 34 | // - fmodq(val, NaN) | ||
| 35 | // - fmodq(inf, val) | ||
| 36 | // The sign on checked values does not matter. | ||
| 37 | // Doing (a * b) / (a * b) procudes undefined results | ||
| 38 | // because the three cases always produce undefined calculations: | ||
| 39 | // - 0 / 0 | ||
| 40 | // - val * NaN | ||
| 41 | // - inf / inf | ||
| 42 | if (b == 0 or std.math.isNan(b) or expA == 0x7fff) { | ||
| 43 | return (a * b) / (a * b); | ||
| 44 | } | ||
| 45 | |||
| 46 | // Remove the sign from both | ||
| 47 | aPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expA)); | ||
| 48 | bPtr_u16[exp_and_sign_index] = @bitCast(u16, @intCast(i16, expB)); | ||
| 49 | if (amod <= bmod) { | ||
| 50 | if (amod == bmod) { | ||
| 51 | return 0 * a; | ||
| 52 | } | ||
| 53 | return a; | ||
| 54 | } | ||
| 55 | |||
| 56 | if (expA == 0) { | ||
| 57 | amod *= 0x1p120; | ||
| 58 | expA = @as(i32, aPtr_u16[exp_and_sign_index]) - 120; | ||
| 59 | } | ||
| 60 | |||
| 61 | if (expB == 0) { | ||
| 62 | bmod *= 0x1p120; | ||
| 63 | expB = @as(i32, bPtr_u16[exp_and_sign_index]) - 120; | ||
| 64 | } | ||
| 65 | |||
| 66 | // OR in extra non-stored mantissa digit | ||
| 67 | var highA: u64 = (aPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48; | ||
| 68 | var highB: u64 = (bPtr_u64[high_index] & (std.math.maxInt(u64) >> 16)) | 1 << 48; | ||
| 69 | var lowA: u64 = aPtr_u64[low_index]; | ||
| 70 | var lowB: u64 = bPtr_u64[low_index]; | ||
| 71 | |||
| 72 | while (expA > expB) : (expA -= 1) { | ||
| 73 | var high = highA -% highB; | ||
| 74 | var low = lowA -% lowB; | ||
| 75 | if (lowA < lowB) { | ||
| 76 | high -%= 1; | ||
| 77 | } | ||
| 78 | if (high >> 63 == 0) { | ||
| 79 | if ((high | low) == 0) { | ||
| 80 | return 0 * a; | ||
| 81 | } | ||
| 82 | highA = 2 *% high + (low >> 63); | ||
| 83 | lowA = 2 *% low; | ||
| 84 | } else { | ||
| 85 | highA = 2 *% highA + (lowA >> 63); | ||
| 86 | lowA = 2 *% lowA; | ||
| 87 | } | ||
| 88 | } | ||
| 89 | |||
| 90 | var high = highA -% highB; | ||
| 91 | var low = lowA -% lowB; | ||
| 92 | if (lowA < lowB) { | ||
| 93 | high -= 1; | ||
| 94 | } | ||
| 95 | if (high >> 63 == 0) { | ||
| 96 | if ((high | low) == 0) { | ||
| 97 | return 0 * a; | ||
| 98 | } | ||
| 99 | highA = high; | ||
| 100 | lowA = low; | ||
| 101 | } | ||
| 102 | |||
| 103 | while (highA >> 48 == 0) { | ||
| 104 | highA = 2 *% highA + (lowA >> 63); | ||
| 105 | lowA = 2 *% lowA; | ||
| 106 | expA = expA - 1; | ||
| 107 | } | ||
| 108 | |||
| 109 | // Overwrite the current amod with the values in highA and lowA | ||
| 110 | aPtr_u64[high_index] = highA; | ||
| 111 | aPtr_u64[low_index] = lowA; | ||
| 112 | |||
| 113 | // Combine the exponent with the sign, normalize if happend to be denormalized | ||
| 114 | if (expA <= 0) { | ||
| 115 | aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, (expA +% 120))) | signA; | ||
| 116 | amod *= 0x1p-120; | ||
| 117 | } else { | ||
| 118 | aPtr_u16[exp_and_sign_index] = @truncate(u16, @bitCast(u32, expA)) | signA; | ||
| 119 | } | ||
| 120 | |||
| 121 | return amod; | ||
| 122 | } | ||
| 123 | |||
| 124 | test { | ||
| 125 | _ = @import("fmodq_test.zig"); | ||
| 126 | } | ||
lib/std/special/compiler_rt/fmodq_test.zig created+52| ... | @@ -0,0 +1,52 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const fmodq = @import("fmodq.zig"); | ||
| 3 | const testing = std.testing; | ||
| 4 | |||
| 5 | fn test_fmodq(a: f128, b: f128, exp: f128) !void { | ||
| 6 | const res = fmodq.fmodq(a, b); | ||
| 7 | try testing.expect(exp == res); | ||
| 8 | } | ||
| 9 | |||
| 10 | fn test_fmodq_nans() !void { | ||
| 11 | try testing.expect(std.math.isNan(fmodq.fmodq(1.0, std.math.nan(f128)))); | ||
| 12 | try testing.expect(std.math.isNan(fmodq.fmodq(1.0, -std.math.nan(f128)))); | ||
| 13 | try testing.expect(std.math.isNan(fmodq.fmodq(std.math.nan(f128), 1.0))); | ||
| 14 | try testing.expect(std.math.isNan(fmodq.fmodq(-std.math.nan(f128), 1.0))); | ||
| 15 | } | ||
| 16 | |||
| 17 | fn test_fmodq_infs() !void { | ||
| 18 | try testing.expect(fmodq.fmodq(1.0, std.math.inf(f128)) == 1.0); | ||
| 19 | try testing.expect(fmodq.fmodq(1.0, -std.math.inf(f128)) == 1.0); | ||
| 20 | try testing.expect(std.math.isNan(fmodq.fmodq(std.math.inf(f128), 1.0))); | ||
| 21 | try testing.expect(std.math.isNan(fmodq.fmodq(-std.math.inf(f128), 1.0))); | ||
| 22 | } | ||
| 23 | |||
| 24 | test "fmodq" { | ||
| 25 | try test_fmodq(6.8, 4.0, 2.8); | ||
| 26 | try test_fmodq(6.8, -4.0, 2.8); | ||
| 27 | try test_fmodq(-6.8, 4.0, -2.8); | ||
| 28 | try test_fmodq(-6.8, -4.0, -2.8); | ||
| 29 | try test_fmodq(3.0, 2.0, 1.0); | ||
| 30 | try test_fmodq(-5.0, 3.0, -2.0); | ||
| 31 | try test_fmodq(3.0, 2.0, 1.0); | ||
| 32 | try test_fmodq(1.0, 2.0, 1.0); | ||
| 33 | try test_fmodq(0.0, 1.0, 0.0); | ||
| 34 | try test_fmodq(-0.0, 1.0, -0.0); | ||
| 35 | try test_fmodq(7046119.0, 5558362.0, 1487757.0); | ||
| 36 | try test_fmodq(9010357.0, 1957236.0, 1181413.0); | ||
| 37 | try test_fmodq(5192296858534827628530496329220095, 10.0, 5.0); | ||
| 38 | try test_fmodq(5192296858534827628530496329220095, 922337203681230954775807, 220474884073715748246157); | ||
| 39 | |||
| 40 | // Denormals | ||
| 41 | const a1: f128 = 0xedcb34a235253948765432134674p-16494; | ||
| 42 | const b1: f128 = 0x5d2e38791cfbc0737402da5a9518p-16494; | ||
| 43 | const exp1: f128 = 0x336ec3affb2db8618e4e7d5e1c44p-16494; | ||
| 44 | try test_fmodq(a1, b1, exp1); | ||
| 45 | const a2: f128 = 0x0.7654_3210_fdec_ba98_7654_3210_fdecp-16382; | ||
| 46 | const b2: f128 = 0x0.0012_fdac_bdef_1234_fdec_3222_1111p-16382; | ||
| 47 | const exp2: f128 = 0x0.0001_aecd_9d66_4a6e_67b7_d7d0_a901p-16382; | ||
| 48 | try test_fmodq(a2, b2, exp2); | ||
| 49 | |||
| 50 | try test_fmodq_nans(); | ||
| 51 | try test_fmodq_infs(); | ||
| 52 | } | ||
lib/std/special/compiler_rt/fmodx.zig created+108| ... | @@ -0,0 +1,108 @@ | ||
| 1 | const builtin = @import("builtin"); | ||
| 2 | const std = @import("std"); | ||
| 3 | const math = std.math; | ||
| 4 | const normalize = @import("divdf3.zig").normalize; | ||
| 5 | |||
| 6 | // fmodx - floating modulo large, returns the remainder of division for f80 types | ||
| 7 | // Logic and flow heavily inspired by MUSL fmodl for 113 mantissa digits | ||
| 8 | pub fn fmodx(a: f80, b: f80) callconv(.C) f80 { | ||
| 9 | @setRuntimeSafety(builtin.is_test); | ||
| 10 | |||
| 11 | const T = f80; | ||
| 12 | const Z = std.meta.Int(.unsigned, @bitSizeOf(T)); | ||
| 13 | |||
| 14 | const significandBits = math.floatMantissaBits(T); | ||
| 15 | const fractionalBits = math.floatFractionalBits(T); | ||
| 16 | const exponentBits = math.floatExponentBits(T); | ||
| 17 | |||
| 18 | const signBit = (@as(Z, 1) << (significandBits + exponentBits)); | ||
| 19 | const maxExponent = ((1 << exponentBits) - 1); | ||
| 20 | |||
| 21 | var aRep = @bitCast(Z, a); | ||
| 22 | var bRep = @bitCast(Z, b); | ||
| 23 | |||
| 24 | const signA = aRep & signBit; | ||
| 25 | var expA = @intCast(i32, (@bitCast(Z, a) >> significandBits) & maxExponent); | ||
| 26 | var expB = @intCast(i32, (@bitCast(Z, b) >> significandBits) & maxExponent); | ||
| 27 | |||
| 28 | // There are 3 cases where the answer is undefined, check for: | ||
| 29 | // - fmodx(val, 0) | ||
| 30 | // - fmodx(val, NaN) | ||
| 31 | // - fmodx(inf, val) | ||
| 32 | // The sign on checked values does not matter. | ||
| 33 | // Doing (a * b) / (a * b) procudes undefined results | ||
| 34 | // because the three cases always produce undefined calculations: | ||
| 35 | // - 0 / 0 | ||
| 36 | // - val * NaN | ||
| 37 | // - inf / inf | ||
| 38 | if (b == 0 or math.isNan(b) or expA == maxExponent) { | ||
| 39 | return (a * b) / (a * b); | ||
| 40 | } | ||
| 41 | |||
| 42 | // Remove the sign from both | ||
| 43 | aRep &= ~signBit; | ||
| 44 | bRep &= ~signBit; | ||
| 45 | if (aRep <= bRep) { | ||
| 46 | if (aRep == bRep) { | ||
| 47 | return 0 * a; | ||
| 48 | } | ||
| 49 | return a; | ||
| 50 | } | ||
| 51 | |||
| 52 | if (expA == 0) expA = normalize(f80, &aRep); | ||
| 53 | if (expB == 0) expB = normalize(f80, &bRep); | ||
| 54 | |||
| 55 | var highA: u64 = 0; | ||
| 56 | var highB: u64 = 0; | ||
| 57 | var lowA: u64 = @truncate(u64, aRep); | ||
| 58 | var lowB: u64 = @truncate(u64, bRep); | ||
| 59 | |||
| 60 | while (expA > expB) : (expA -= 1) { | ||
| 61 | var high = highA -% highB; | ||
| 62 | var low = lowA -% lowB; | ||
| 63 | if (lowA < lowB) { | ||
| 64 | high -%= 1; | ||
| 65 | } | ||
| 66 | if (high >> 63 == 0) { | ||
| 67 | if ((high | low) == 0) { | ||
| 68 | return 0 * a; | ||
| 69 | } | ||
| 70 | highA = 2 *% high + (low >> 63); | ||
| 71 | lowA = 2 *% low; | ||
| 72 | } else { | ||
| 73 | highA = 2 *% highA + (lowA >> 63); | ||
| 74 | lowA = 2 *% lowA; | ||
| 75 | } | ||
| 76 | } | ||
| 77 | |||
| 78 | var high = highA -% highB; | ||
| 79 | var low = lowA -% lowB; | ||
| 80 | if (lowA < lowB) { | ||
| 81 | high -%= 1; | ||
| 82 | } | ||
| 83 | if (high >> 63 == 0) { | ||
| 84 | if ((high | low) == 0) { | ||
| 85 | return 0 * a; | ||
| 86 | } | ||
| 87 | highA = high; | ||
| 88 | lowA = low; | ||
| 89 | } | ||
| 90 | |||
| 91 | while ((lowA >> fractionalBits) == 0) { | ||
| 92 | lowA = 2 *% lowA; | ||
| 93 | expA = expA - 1; | ||
| 94 | } | ||
| 95 | |||
| 96 | // Combine the exponent with the sign and significand, normalize if happened to be denormalized | ||
| 97 | if (expA < -fractionalBits) { | ||
| 98 | return @bitCast(T, signA); | ||
| 99 | } else if (expA <= 0) { | ||
| 100 | return @bitCast(T, (lowA >> @intCast(math.Log2Int(u64), 1 - expA)) | signA); | ||
| 101 | } else { | ||
| 102 | return @bitCast(T, lowA | (@as(Z, @intCast(u16, expA)) << significandBits) | signA); | ||
| 103 | } | ||
| 104 | } | ||
| 105 | |||
| 106 | test { | ||
| 107 | _ = @import("fmodx_test.zig"); | ||
| 108 | } | ||
lib/std/special/compiler_rt/fmodx_test.zig created+51| ... | @@ -0,0 +1,51 @@ | ||
| 1 | const std = @import("std"); | ||
| 2 | const fmodx = @import("fmodx.zig"); | ||
| 3 | const testing = std.testing; | ||
| 4 | |||
| 5 | fn test_fmodx(a: f80, b: f80, exp: f80) !void { | ||
| 6 | const res = fmodx.fmodx(a, b); | ||
| 7 | try testing.expect(exp == res); | ||
| 8 | } | ||
| 9 | |||
| 10 | fn test_fmodx_nans() !void { | ||
| 11 | try testing.expect(std.math.isNan(fmodx.fmodx(1.0, std.math.nan(f80)))); | ||
| 12 | try testing.expect(std.math.isNan(fmodx.fmodx(1.0, -std.math.nan(f80)))); | ||
| 13 | try testing.expect(std.math.isNan(fmodx.fmodx(std.math.nan(f80), 1.0))); | ||
| 14 | try testing.expect(std.math.isNan(fmodx.fmodx(-std.math.nan(f80), 1.0))); | ||
| 15 | } | ||
| 16 | |||
| 17 | fn test_fmodx_infs() !void { | ||
| 18 | try testing.expect(fmodx.fmodx(1.0, std.math.inf(f80)) == 1.0); | ||
| 19 | try testing.expect(fmodx.fmodx(1.0, -std.math.inf(f80)) == 1.0); | ||
| 20 | try testing.expect(std.math.isNan(fmodx.fmodx(std.math.inf(f80), 1.0))); | ||
| 21 | try testing.expect(std.math.isNan(fmodx.fmodx(-std.math.inf(f80), 1.0))); | ||
| 22 | } | ||
| 23 | |||
| 24 | test "fmodx" { | ||
| 25 | try test_fmodx(6.4, 4.0, 2.4); | ||
| 26 | try test_fmodx(6.4, -4.0, 2.4); | ||
| 27 | try test_fmodx(-6.4, 4.0, -2.4); | ||
| 28 | try test_fmodx(-6.4, -4.0, -2.4); | ||
| 29 | try test_fmodx(3.0, 2.0, 1.0); | ||
| 30 | try test_fmodx(-5.0, 3.0, -2.0); | ||
| 31 | try test_fmodx(3.0, 2.0, 1.0); | ||
| 32 | try test_fmodx(1.0, 2.0, 1.0); | ||
| 33 | try test_fmodx(0.0, 1.0, 0.0); | ||
| 34 | try test_fmodx(-0.0, 1.0, -0.0); | ||
| 35 | try test_fmodx(7046119.0, 5558362.0, 1487757.0); | ||
| 36 | try test_fmodx(9010357.0, 1957236.0, 1181413.0); | ||
| 37 | try test_fmodx(9223372036854775807, 10.0, 7.0); | ||
| 38 | |||
| 39 | // Denormals | ||
| 40 | const a1: f80 = 0x0.76e5_9a51_1a92_9ca4p-16381; | ||
| 41 | const b1: f80 = 0x0.2e97_1c3c_8e7d_e03ap-16381; | ||
| 42 | const exp1: f80 = 0x0.19b7_61d7_fd96_dc30p-16381; | ||
| 43 | try test_fmodx(a1, b1, exp1); | ||
| 44 | const a2: f80 = 0x0.76e5_9a51_1a92_9ca4p-16381; | ||
| 45 | const b2: f80 = 0x0.0e97_1c3c_8e7d_e03ap-16381; | ||
| 46 | const exp2: f80 = 0x0.022c_b86c_a6a3_9ad4p-16381; | ||
| 47 | try test_fmodx(a2, b2, exp2); | ||
| 48 | |||
| 49 | try test_fmodx_nans(); | ||
| 50 | try test_fmodx_infs(); | ||
| 51 | } | ||