authorgravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-04-25 13:25:15-07:00
committergravatar for topolarity@tapscott.meCody Tapscott <topolarity@tapscott.me> 2022-04-25 17:21:05-07:00
log6c0114e04436404b1c59b66125733350b4be2f5b
tree39236e6a220f0ce1941caa8d8fd7130d9ef7a318
parentab658e32bdfa234b6c33f0718cac7a23fbd4074f

compiler_rt: Implement fmodx for f80


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 {
726726
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}
886891
887const fmodq = @import("compiler_rt/floatfmodq.zig").fmodq;892const fmodq = @import("compiler_rt/fmodq.zig").fmodq;
888fn fmodl(x: c_longdouble, y: c_longdouble) callconv(.C) c_longdouble {893const 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}
894894
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 {
314pub fn normalize(comptime T: type, significand: *std.meta.Int(.unsigned, @typeInfo(T).Float.bits)) i32 {314pub 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;
319318
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}
324323
325pub fn __aeabi_ddiv(a: f64, b: f64) callconv(.AAPCS) f64 {324pub fn __aeabi_ddiv(a: f64, b: f64) callconv(.AAPCS) f64 {
lib/std/special/compiler_rt/floatfmodq.zig deleted-126
...@@ -1,126 +0,0 @@
1const builtin = @import("builtin");
2const 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
6pub 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
124test {
125 _ = @import("floatfmodq_test.zig");
126}
lib/std/special/compiler_rt/floatfmodq_test.zig deleted-46
...@@ -1,46 +0,0 @@
1const std = @import("std");
2const fmodq = @import("floatfmodq.zig");
3const testing = std.testing;
4
5fn test_fmodq(a: f128, b: f128, exp: f128) !void {
6 const res = fmodq.fmodq(a, b);
7 try testing.expect(exp == res);
8}
9
10fn 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
17fn 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
24test "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 @@
1const builtin = @import("builtin");
2const 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
6pub 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
124test {
125 _ = @import("fmodq_test.zig");
126}
lib/std/special/compiler_rt/fmodq_test.zig created+52
...@@ -0,0 +1,52 @@
1const std = @import("std");
2const fmodq = @import("fmodq.zig");
3const testing = std.testing;
4
5fn test_fmodq(a: f128, b: f128, exp: f128) !void {
6 const res = fmodq.fmodq(a, b);
7 try testing.expect(exp == res);
8}
9
10fn 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
17fn 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
24test "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 @@
1const builtin = @import("builtin");
2const std = @import("std");
3const math = std.math;
4const 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
8pub 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
106test {
107 _ = @import("fmodx_test.zig");
108}
lib/std/special/compiler_rt/fmodx_test.zig created+51
...@@ -0,0 +1,51 @@
1const std = @import("std");
2const fmodx = @import("fmodx.zig");
3const testing = std.testing;
4
5fn test_fmodx(a: f80, b: f80, exp: f80) !void {
6 const res = fmodx.fmodx(a, b);
7 try testing.expect(exp == res);
8}
9
10fn 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
17fn 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
24test "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}