| author | |
| committer | |
| log | 7a9f8dc9393ff4084da6c77d874d08e26897b3cb |
| tree | 4836617ffadabaa3ff357500edecda3ac7e0e8e7 |
| parent | 6297afc66cb2dbe563af104dff91c1688e053674 |
Adds tests and a fix to make sure compiler_rt handles division correctly from now on.
Fixes #35283
Reviewed-on: https://codeberg.org/ziglang/zig/pulls/32177
Reviewed-by: Andrew Kelley <andrew@ziglang.org>7 files changed, 120 insertions(+), 35 deletions(-)
lib/compiler_rt/divdf3.zig+12-8| ... | ... | @@ -4,7 +4,7 @@ |
| 4 | 4 | |
| 5 | 5 | const std = @import("std"); |
| 6 | 6 | const compiler_rt = @import("../compiler_rt.zig"); |
| 7 | const symbol = @import("../compiler_rt.zig").symbol; | |
| 7 | const symbol = compiler_rt.symbol; | |
| 8 | 8 | |
| 9 | 9 | const normalize = compiler_rt.normalize; |
| 10 | 10 | const wideMultiply = compiler_rt.wideMultiply; |
| ... | ... | @@ -189,14 +189,13 @@ inline fn div(a: f64, b: f64) f64 { |
| 189 | 189 | |
| 190 | 190 | const writtenExponent = quotientExponent +% exponentBias; |
| 191 | 191 | |
| 192 | const round = @intFromBool((residual << 1) >= bSignificand); | |
| 193 | ||
| 192 | 194 | if (writtenExponent >= maxExponent) { |
| 193 | 195 | // If we have overflowed the exponent, return infinity. |
| 194 | 196 | return @bitCast(infRep | quotientSign); |
| 195 | 197 | } else if (writtenExponent < 1) { |
| 196 | 198 | if (writtenExponent == 0) { |
| 197 | // Check whether the rounded result is normal. | |
| 198 | const round = @intFromBool((residual << 1) > bSignificand); | |
| 199 | // Clear the implicit bit. | |
| 200 | 199 | var absResult = quotient & significandMask; |
| 201 | 200 | // Round. |
| 202 | 201 | absResult += round; |
| ... | ... | @@ -205,11 +204,16 @@ inline fn div(a: f64, b: f64) f64 { |
| 205 | 204 | return @bitCast(absResult | quotientSign); |
| 206 | 205 | } |
| 207 | 206 | } |
| 208 | // Flush denormals to zero. In the future, it would be nice to add | |
| 209 | // code to round them correctly. | |
| 210 | return @bitCast(quotientSign); | |
| 207 | ||
| 208 | const roundedQuotient = quotient +% round; | |
| 209 | const shiftAmount: u32 = @intCast(1 - writtenExponent); | |
| 210 | if (shiftAmount > significandBits + 1) { | |
| 211 | return @bitCast(quotientSign); | |
| 212 | } | |
| 213 | ||
| 214 | const denormQuotient = roundedQuotient >> @as(std.math.Log2Int(Z), @intCast(shiftAmount)); | |
| 215 | return @bitCast((denormQuotient & significandMask) | quotientSign); | |
| 211 | 216 | } else { |
| 212 | const round = @intFromBool((residual << 1) > bSignificand); | |
| 213 | 217 | // Clear the implicit bit |
| 214 | 218 | var absResult = quotient & significandMask; |
| 215 | 219 | // Insert the exponent |
lib/compiler_rt/divdf3_test.zig+33-2| ... | ... | @@ -2,8 +2,15 @@ |
| 2 | 2 | // |
| 3 | 3 | // https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/test/builtins/Unit/divdf3_test.c |
| 4 | 4 | |
| 5 | const std = @import("std"); | |
| 6 | const math = std.math; | |
| 7 | const testing = std.testing; | |
| 8 | ||
| 5 | 9 | const __divdf3 = @import("divdf3.zig").__divdf3; |
| 6 | const testing = @import("std").testing; | |
| 10 | ||
| 11 | const nanRep: u64 = @as(u64, @bitCast(math.nan(f64))); | |
| 12 | const infRep: u64 = @as(u64, @bitCast(math.inf(f64))); | |
| 13 | const negInfRep: u64 = @as(u64, @bitCast(-math.inf(f64))); | |
| 7 | 14 | |
| 8 | 15 | fn compareResultD(result: f64, expected: u64) bool { |
| 9 | 16 | const rep: u64 = @bitCast(result); |
| ... | ... | @@ -12,7 +19,7 @@ fn compareResultD(result: f64, expected: u64) bool { |
| 12 | 19 | return true; |
| 13 | 20 | } |
| 14 | 21 | // test other possible NaN representation(signal NaN) |
| 15 | else if (expected == 0x7ff8000000000000) { | |
| 22 | else if (expected == nanRep) { | |
| 16 | 23 | if ((rep & 0x7ff0000000000000) == 0x7ff0000000000000 and |
| 17 | 24 | (rep & 0xfffffffffffff) > 0) |
| 18 | 25 | { |
| ... | ... | @@ -32,4 +39,28 @@ test "divdf3" { |
| 32 | 39 | try test__divdf3(1.0, 3.0, 0x3fd5555555555555); |
| 33 | 40 | try test__divdf3(4.450147717014403e-308, 2.0, 0x10000000000000); |
| 34 | 41 | try test__divdf3(1.0, 0x1.fffffffffffffp-1, 0x3ff0000000000001); |
| 42 | ||
| 43 | try test__divdf3(math.nan(f64), 1.0, nanRep); | |
| 44 | try test__divdf3(1.0, math.nan(f64), nanRep); | |
| 45 | ||
| 46 | try test__divdf3(math.inf(f64), 1.0, infRep); | |
| 47 | try test__divdf3(-math.inf(f64), 1.0, negInfRep); | |
| 48 | try test__divdf3(1.0, math.inf(f64), 0x0000000000000000); | |
| 49 | try test__divdf3(1.0, -math.inf(f64), 0x8000000000000000); | |
| 50 | ||
| 51 | try test__divdf3(math.inf(f64), math.inf(f64), nanRep); | |
| 52 | try test__divdf3(0.0, 0.0, nanRep); | |
| 53 | try test__divdf3(-0.0, 0.0, nanRep); | |
| 54 | ||
| 55 | try test__divdf3(0.0, 1.0, 0x0000000000000000); | |
| 56 | try test__divdf3(-0.0, 1.0, 0x8000000000000000); | |
| 57 | try test__divdf3(1.0, 0.0, infRep); | |
| 58 | try test__divdf3(1.0, -0.0, negInfRep); | |
| 59 | ||
| 60 | try test__divdf3(0x1p-1022, 0x1p52, 0x0000000000000001); | |
| 61 | try test__divdf3(-0x1p-1022, 0x1p52, 0x8000000000000001); | |
| 62 | try test__divdf3(0x1p-1022, -0x1p52, 0x8000000000000001); | |
| 63 | ||
| 64 | try test__divdf3(1.0, 0x1p1023, 0x0008000000000000); | |
| 65 | try test__divdf3(-1.0, 0x1p1023, 0x8008000000000000); | |
| 35 | 66 | } |
lib/compiler_rt/divsf3.zig+12-7| ... | ... | @@ -5,7 +5,7 @@ |
| 5 | 5 | const std = @import("std"); |
| 6 | 6 | |
| 7 | 7 | const compiler_rt = @import("../compiler_rt.zig"); |
| 8 | const symbol = @import("../compiler_rt.zig").symbol; | |
| 8 | const symbol = compiler_rt.symbol; | |
| 9 | 9 | const normalize = compiler_rt.normalize; |
| 10 | 10 | |
| 11 | 11 | comptime { |
| ... | ... | @@ -170,14 +170,14 @@ inline fn div(a: f32, b: f32) f32 { |
| 170 | 170 | |
| 171 | 171 | const writtenExponent = quotientExponent +% exponentBias; |
| 172 | 172 | |
| 173 | const round = @intFromBool((residual << 1) >= bSignificand); | |
| 174 | ||
| 173 | 175 | if (writtenExponent >= maxExponent) { |
| 174 | 176 | // If we have overflowed the exponent, return infinity. |
| 175 | 177 | return @bitCast(infRep | quotientSign); |
| 176 | 178 | } else if (writtenExponent < 1) { |
| 177 | 179 | if (writtenExponent == 0) { |
| 178 | 180 | // Check whether the rounded result is normal. |
| 179 | const round = @intFromBool((residual << 1) > bSignificand); | |
| 180 | // Clear the implicit bit. | |
| 181 | 181 | var absResult = quotient & significandMask; |
| 182 | 182 | // Round. |
| 183 | 183 | absResult += round; |
| ... | ... | @@ -186,11 +186,16 @@ inline fn div(a: f32, b: f32) f32 { |
| 186 | 186 | return @bitCast(absResult | quotientSign); |
| 187 | 187 | } |
| 188 | 188 | } |
| 189 | // Flush denormals to zero. In the future, it would be nice to add | |
| 190 | // code to round them correctly. | |
| 191 | return @bitCast(quotientSign); | |
| 189 | ||
| 190 | const roundedQuotient = quotient +% round; | |
| 191 | const shiftAmount: u32 = @intCast(1 - writtenExponent); | |
| 192 | if (shiftAmount > significandBits + 1) { | |
| 193 | return @bitCast(quotientSign); | |
| 194 | } | |
| 195 | ||
| 196 | const denormQuotient = roundedQuotient >> @as(std.math.Log2Int(Z), @intCast(shiftAmount)); | |
| 197 | return @bitCast((denormQuotient & significandMask) | quotientSign); | |
| 192 | 198 | } else { |
| 193 | const round = @intFromBool((residual << 1) > bSignificand); | |
| 194 | 199 | // Clear the implicit bit |
| 195 | 200 | var absResult = quotient & significandMask; |
| 196 | 201 | // Insert the exponent |
lib/compiler_rt/divsf3_test.zig+33-2| ... | ... | @@ -2,8 +2,15 @@ |
| 2 | 2 | // |
| 3 | 3 | // https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/test/builtins/Unit/divsf3_test.c |
| 4 | 4 | |
| 5 | const std = @import("std"); | |
| 6 | const math = std.math; | |
| 7 | const testing = std.testing; | |
| 8 | ||
| 5 | 9 | const __divsf3 = @import("divsf3.zig").__divsf3; |
| 6 | const testing = @import("std").testing; | |
| 10 | ||
| 11 | const nanRep: u32 = @as(u32, @bitCast(math.nan(f32))); | |
| 12 | const infRep: u32 = @as(u32, @bitCast(math.inf(f32))); | |
| 13 | const negInfRep: u32 = @as(u32, @bitCast(-math.inf(f32))); | |
| 7 | 14 | |
| 8 | 15 | fn compareResultF(result: f32, expected: u32) bool { |
| 9 | 16 | const rep: u32 = @bitCast(result); |
| ... | ... | @@ -12,7 +19,7 @@ fn compareResultF(result: f32, expected: u32) bool { |
| 12 | 19 | return true; |
| 13 | 20 | } |
| 14 | 21 | // test other possible NaN representation(signal NaN) |
| 15 | else if (expected == 0x7fc00000) { | |
| 22 | else if (expected == nanRep) { | |
| 16 | 23 | if ((rep & 0x7f800000) == 0x7f800000 and |
| 17 | 24 | (rep & 0x7fffff) > 0) |
| 18 | 25 | { |
| ... | ... | @@ -32,4 +39,28 @@ test "divsf3" { |
| 32 | 39 | try test__divsf3(1.0, 3.0, 0x3EAAAAAB); |
| 33 | 40 | try test__divsf3(2.3509887e-38, 2.0, 0x00800000); |
| 34 | 41 | try test__divsf3(1.0, 0x1.fffffep-1, 0x3f800001); |
| 42 | ||
| 43 | try test__divsf3(math.nan(f32), 1.0, nanRep); | |
| 44 | try test__divsf3(1.0, math.nan(f32), nanRep); | |
| 45 | ||
| 46 | try test__divsf3(math.inf(f32), 1.0, infRep); | |
| 47 | try test__divsf3(-math.inf(f32), 1.0, negInfRep); | |
| 48 | try test__divsf3(1.0, math.inf(f32), 0x00000000); | |
| 49 | try test__divsf3(1.0, -math.inf(f32), 0x80000000); | |
| 50 | ||
| 51 | try test__divsf3(math.inf(f32), math.inf(f32), nanRep); | |
| 52 | try test__divsf3(0.0, 0.0, nanRep); | |
| 53 | try test__divsf3(-0.0, 0.0, nanRep); | |
| 54 | ||
| 55 | try test__divsf3(0.0, 1.0, 0x00000000); | |
| 56 | try test__divsf3(-0.0, 1.0, 0x80000000); | |
| 57 | try test__divsf3(1.0, 0.0, infRep); | |
| 58 | try test__divsf3(1.0, -0.0, negInfRep); | |
| 59 | ||
| 60 | try test__divsf3(0x1p-126, 0x1p23, 0x00000001); | |
| 61 | try test__divsf3(-0x1p-126, 0x1p23, 0x80000001); | |
| 62 | try test__divsf3(0x1p-126, -0x1p23, 0x80000001); | |
| 63 | ||
| 64 | try test__divsf3(1.0, 0x1p127, 0x00400000); | |
| 65 | try test__divsf3(-1.0, 0x1p127, 0x80400000); | |
| 35 | 66 | } |
lib/compiler_rt/divtf3_test.zig+10-5| ... | ... | @@ -30,14 +30,19 @@ fn test__divtf3(a: f128, b: f128, expectedHi: u64, expectedLo: u64) !void { |
| 30 | 30 | } |
| 31 | 31 | |
| 32 | 32 | test "divtf3" { |
| 33 | // NaN / any = NaN | |
| 34 | 33 | try test__divtf3(math.nan(f128), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0); |
| 35 | // inf / any(except inf and nan) = inf | |
| 34 | try test__divtf3(0x1.23456789abcdefp+5, math.nan(f128), 0x7fff800000000000, 0); | |
| 36 | 35 | try test__divtf3(math.inf(f128), 0x1.23456789abcdefp+5, 0x7fff000000000000, 0); |
| 37 | // inf / inf = nan | |
| 36 | try test__divtf3(-math.inf(f128), 0x1.23456789abcdefp+5, 0xffff000000000000, 0); | |
| 37 | try test__divtf3(0x1.23456789abcdefp+5, math.inf(f128), 0, 0); | |
| 38 | try test__divtf3(0x1.23456789abcdefp+5, -math.inf(f128), 0x8000000000000000, 0); | |
| 38 | 39 | try test__divtf3(math.inf(f128), math.inf(f128), 0x7fff800000000000, 0); |
| 39 | // inf / nan = nan | |
| 40 | try test__divtf3(math.inf(f128), math.nan(f128), 0x7fff800000000000, 0); | |
| 40 | try test__divtf3(0.0, 0.0, 0x7fff800000000000, 0); | |
| 41 | try test__divtf3(-0.0, 0.0, 0x7fff800000000000, 0); | |
| 42 | try test__divtf3(0.0, 1.0, 0, 0); | |
| 43 | try test__divtf3(-0.0, 1.0, 0x8000000000000000, 0); | |
| 44 | try test__divtf3(1.0, 0.0, 0x7fff000000000000, 0); | |
| 45 | try test__divtf3(1.0, -0.0, 0xffff000000000000, 0); | |
| 41 | 46 | |
| 42 | 47 | try test__divtf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.eedcbaba3a94546558237654321fp-1, 0x4004b0b72924d407, 0x0717e84356c6eba2); |
| 43 | 48 | try test__divtf3(0x1.a2b34c56d745382f9abf2c3dfeffp-50, 0x1.ed2c3ba15935332532287654321fp-9, 0x3fd5b2af3f828c9b, 0x40e51f64cde8b1f2); |
lib/compiler_rt/divxf3_test.zig+17-8| ... | ... | @@ -4,6 +4,10 @@ const testing = std.testing; |
| 4 | 4 | |
| 5 | 5 | const __divxf3 = @import("divxf3.zig").__divxf3; |
| 6 | 6 | |
| 7 | const nanRep: u80 = @as(u80, @bitCast(math.nan(f80))); | |
| 8 | const infRep: u80 = @as(u80, @bitCast(math.inf(f80))); | |
| 9 | const negInfRep: u80 = @as(u80, @bitCast(-math.inf(f80))); | |
| 10 | ||
| 7 | 11 | fn compareResult(result: f80, expected: u80) bool { |
| 8 | 12 | const rep: u80 = @bitCast(result); |
| 9 | 13 | |
| ... | ... | @@ -39,14 +43,19 @@ fn test__divxf3(a: f80, b: f80) !void { |
| 39 | 43 | } |
| 40 | 44 | |
| 41 | 45 | test "divxf3" { |
| 42 | // NaN / any = NaN | |
| 43 | try expect__divxf3_result(math.nan(f80), 0x1.23456789abcdefp+5, 0x7fffC000000000000000); | |
| 44 | // inf / any(except inf and nan) = inf | |
| 45 | try expect__divxf3_result(math.inf(f80), 0x1.23456789abcdefp+5, 0x7fff8000000000000000); | |
| 46 | // inf / inf = nan | |
| 47 | try expect__divxf3_result(math.inf(f80), math.inf(f80), 0x7fffC000000000000000); | |
| 48 | // inf / nan = nan | |
| 49 | try expect__divxf3_result(math.inf(f80), math.nan(f80), 0x7fffC000000000000000); | |
| 46 | try expect__divxf3_result(math.nan(f80), 0x1.23456789abcdefp+5, nanRep); | |
| 47 | try expect__divxf3_result(0x1.23456789abcdefp+5, math.nan(f80), nanRep); | |
| 48 | try expect__divxf3_result(math.inf(f80), 0x1.23456789abcdefp+5, infRep); | |
| 49 | try expect__divxf3_result(-math.inf(f80), 0x1.23456789abcdefp+5, negInfRep); | |
| 50 | try expect__divxf3_result(0x1.23456789abcdefp+5, math.inf(f80), 0x0); | |
| 51 | try expect__divxf3_result(0x1.23456789abcdefp+5, -math.inf(f80), 0x80000000000000000000); | |
| 52 | try expect__divxf3_result(math.inf(f80), math.inf(f80), nanRep); | |
| 53 | try expect__divxf3_result(0.0, 0.0, nanRep); | |
| 54 | try expect__divxf3_result(-0.0, 0.0, nanRep); | |
| 55 | try expect__divxf3_result(0.0, 1.0, 0x0); | |
| 56 | try expect__divxf3_result(-0.0, 1.0, 0x80000000000000000000); | |
| 57 | try expect__divxf3_result(1.0, 0.0, infRep); | |
| 58 | try expect__divxf3_result(1.0, -0.0, negInfRep); | |
| 50 | 59 | |
| 51 | 60 | try test__divxf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.eedcbaba3a94546558237654321fp-1); |
| 52 | 61 | try test__divxf3(0x1.a2b34c56d745382f9abf2c3dfeffp-50, 0x1.ed2c3ba15935332532287654321fp-9); |
test/libc.zig+3-3| ... | ... | @@ -151,9 +151,9 @@ pub fn addCases(cases: *tests.LibcContext) void { |
| 151 | 151 | // cases.addLibcTestCase("math/asinhl.c", true, .{}); |
| 152 | 152 | cases.addLibcTestCase("math/asinl.c", true, .{}); |
| 153 | 153 | cases.addLibcTestCase("math/atan.c", true, .{}); |
| 154 | // cases.addLibcTestCase("math/atan2.c", true, .{}); | |
| 155 | // cases.addLibcTestCase("math/atan2f.c", true, .{}); | |
| 156 | // cases.addLibcTestCase("math/atan2l.c", true, .{}); | |
| 154 | cases.addLibcTestCase("math/atan2.c", true, .{}); | |
| 155 | cases.addLibcTestCase("math/atan2f.c", true, .{}); | |
| 156 | cases.addLibcTestCase("math/atan2l.c", true, .{}); | |
| 157 | 157 | cases.addLibcTestCase("math/atanf.c", true, .{}); |
| 158 | 158 | cases.addLibcTestCase("math/atanh.c", true, .{}); |
| 159 | 159 | cases.addLibcTestCase("math/atanhf.c", true, .{}); |