authorgravatar for jeroen-876@hotmail.comgero3 <jeroen-876@hotmail.com> 2026-06-07 04:43:39+02:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-06-07 04:43:39+02:00
log7a9f8dc9393ff4084da6c77d874d08e26897b3cb
tree4836617ffadabaa3ff357500edecda3ac7e0e8e7
parent6297afc66cb2dbe563af104dff91c1688e053674

fix division of subnormal float in softfloat targets (#32177)

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,7 +4,7 @@
44
5const std = @import("std");5const std = @import("std");
6const compiler_rt = @import("../compiler_rt.zig");6const compiler_rt = @import("../compiler_rt.zig");
7const symbol = @import("../compiler_rt.zig").symbol;7const symbol = compiler_rt.symbol;
88
9const normalize = compiler_rt.normalize;9const normalize = compiler_rt.normalize;
10const wideMultiply = compiler_rt.wideMultiply;10const wideMultiply = compiler_rt.wideMultiply;
...@@ -189,14 +189,13 @@ inline fn div(a: f64, b: f64) f64 {...@@ -189,14 +189,13 @@ inline fn div(a: f64, b: f64) f64 {
189189
190 const writtenExponent = quotientExponent +% exponentBias;190 const writtenExponent = quotientExponent +% exponentBias;
191191
192 const round = @intFromBool((residual << 1) >= bSignificand);
193
192 if (writtenExponent >= maxExponent) {194 if (writtenExponent >= maxExponent) {
193 // If we have overflowed the exponent, return infinity.195 // If we have overflowed the exponent, return infinity.
194 return @bitCast(infRep | quotientSign);196 return @bitCast(infRep | quotientSign);
195 } else if (writtenExponent < 1) {197 } else if (writtenExponent < 1) {
196 if (writtenExponent == 0) {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 var absResult = quotient & significandMask;199 var absResult = quotient & significandMask;
201 // Round.200 // Round.
202 absResult += round;201 absResult += round;
...@@ -205,11 +204,16 @@ inline fn div(a: f64, b: f64) f64 {...@@ -205,11 +204,16 @@ inline fn div(a: f64, b: f64) f64 {
205 return @bitCast(absResult | quotientSign);204 return @bitCast(absResult | quotientSign);
206 }205 }
207 }206 }
208 // Flush denormals to zero. In the future, it would be nice to add207
209 // code to round them correctly.208 const roundedQuotient = quotient +% round;
210 return @bitCast(quotientSign);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 } else {216 } else {
212 const round = @intFromBool((residual << 1) > bSignificand);
213 // Clear the implicit bit217 // Clear the implicit bit
214 var absResult = quotient & significandMask;218 var absResult = quotient & significandMask;
215 // Insert the exponent219 // Insert the exponent
lib/compiler_rt/divdf3_test.zig+33-2
...@@ -2,8 +2,15 @@...@@ -2,8 +2,15 @@
2//2//
3// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/test/builtins/Unit/divdf3_test.c3// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/test/builtins/Unit/divdf3_test.c
44
5const std = @import("std");
6const math = std.math;
7const testing = std.testing;
8
5const __divdf3 = @import("divdf3.zig").__divdf3;9const __divdf3 = @import("divdf3.zig").__divdf3;
6const testing = @import("std").testing;10
11const nanRep: u64 = @as(u64, @bitCast(math.nan(f64)));
12const infRep: u64 = @as(u64, @bitCast(math.inf(f64)));
13const negInfRep: u64 = @as(u64, @bitCast(-math.inf(f64)));
714
8fn compareResultD(result: f64, expected: u64) bool {15fn compareResultD(result: f64, expected: u64) bool {
9 const rep: u64 = @bitCast(result);16 const rep: u64 = @bitCast(result);
...@@ -12,7 +19,7 @@ fn compareResultD(result: f64, expected: u64) bool {...@@ -12,7 +19,7 @@ fn compareResultD(result: f64, expected: u64) bool {
12 return true;19 return true;
13 }20 }
14 // test other possible NaN representation(signal NaN)21 // test other possible NaN representation(signal NaN)
15 else if (expected == 0x7ff8000000000000) {22 else if (expected == nanRep) {
16 if ((rep & 0x7ff0000000000000) == 0x7ff0000000000000 and23 if ((rep & 0x7ff0000000000000) == 0x7ff0000000000000 and
17 (rep & 0xfffffffffffff) > 0)24 (rep & 0xfffffffffffff) > 0)
18 {25 {
...@@ -32,4 +39,28 @@ test "divdf3" {...@@ -32,4 +39,28 @@ test "divdf3" {
32 try test__divdf3(1.0, 3.0, 0x3fd5555555555555);39 try test__divdf3(1.0, 3.0, 0x3fd5555555555555);
33 try test__divdf3(4.450147717014403e-308, 2.0, 0x10000000000000);40 try test__divdf3(4.450147717014403e-308, 2.0, 0x10000000000000);
34 try test__divdf3(1.0, 0x1.fffffffffffffp-1, 0x3ff0000000000001);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,7 +5,7 @@
5const std = @import("std");5const std = @import("std");
66
7const compiler_rt = @import("../compiler_rt.zig");7const compiler_rt = @import("../compiler_rt.zig");
8const symbol = @import("../compiler_rt.zig").symbol;8const symbol = compiler_rt.symbol;
9const normalize = compiler_rt.normalize;9const normalize = compiler_rt.normalize;
1010
11comptime {11comptime {
...@@ -170,14 +170,14 @@ inline fn div(a: f32, b: f32) f32 {...@@ -170,14 +170,14 @@ inline fn div(a: f32, b: f32) f32 {
170170
171 const writtenExponent = quotientExponent +% exponentBias;171 const writtenExponent = quotientExponent +% exponentBias;
172172
173 const round = @intFromBool((residual << 1) >= bSignificand);
174
173 if (writtenExponent >= maxExponent) {175 if (writtenExponent >= maxExponent) {
174 // If we have overflowed the exponent, return infinity.176 // If we have overflowed the exponent, return infinity.
175 return @bitCast(infRep | quotientSign);177 return @bitCast(infRep | quotientSign);
176 } else if (writtenExponent < 1) {178 } else if (writtenExponent < 1) {
177 if (writtenExponent == 0) {179 if (writtenExponent == 0) {
178 // Check whether the rounded result is normal.180 // Check whether the rounded result is normal.
179 const round = @intFromBool((residual << 1) > bSignificand);
180 // Clear the implicit bit.
181 var absResult = quotient & significandMask;181 var absResult = quotient & significandMask;
182 // Round.182 // Round.
183 absResult += round;183 absResult += round;
...@@ -186,11 +186,16 @@ inline fn div(a: f32, b: f32) f32 {...@@ -186,11 +186,16 @@ inline fn div(a: f32, b: f32) f32 {
186 return @bitCast(absResult | quotientSign);186 return @bitCast(absResult | quotientSign);
187 }187 }
188 }188 }
189 // Flush denormals to zero. In the future, it would be nice to add189
190 // code to round them correctly.190 const roundedQuotient = quotient +% round;
191 return @bitCast(quotientSign);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 } else {198 } else {
193 const round = @intFromBool((residual << 1) > bSignificand);
194 // Clear the implicit bit199 // Clear the implicit bit
195 var absResult = quotient & significandMask;200 var absResult = quotient & significandMask;
196 // Insert the exponent201 // Insert the exponent
lib/compiler_rt/divsf3_test.zig+33-2
...@@ -2,8 +2,15 @@...@@ -2,8 +2,15 @@
2//2//
3// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/test/builtins/Unit/divsf3_test.c3// https://github.com/llvm/llvm-project/commit/d674d96bc56c0f377879d01c9d8dfdaaa7859cdb/compiler-rt/test/builtins/Unit/divsf3_test.c
44
5const std = @import("std");
6const math = std.math;
7const testing = std.testing;
8
5const __divsf3 = @import("divsf3.zig").__divsf3;9const __divsf3 = @import("divsf3.zig").__divsf3;
6const testing = @import("std").testing;10
11const nanRep: u32 = @as(u32, @bitCast(math.nan(f32)));
12const infRep: u32 = @as(u32, @bitCast(math.inf(f32)));
13const negInfRep: u32 = @as(u32, @bitCast(-math.inf(f32)));
714
8fn compareResultF(result: f32, expected: u32) bool {15fn compareResultF(result: f32, expected: u32) bool {
9 const rep: u32 = @bitCast(result);16 const rep: u32 = @bitCast(result);
...@@ -12,7 +19,7 @@ fn compareResultF(result: f32, expected: u32) bool {...@@ -12,7 +19,7 @@ fn compareResultF(result: f32, expected: u32) bool {
12 return true;19 return true;
13 }20 }
14 // test other possible NaN representation(signal NaN)21 // test other possible NaN representation(signal NaN)
15 else if (expected == 0x7fc00000) {22 else if (expected == nanRep) {
16 if ((rep & 0x7f800000) == 0x7f800000 and23 if ((rep & 0x7f800000) == 0x7f800000 and
17 (rep & 0x7fffff) > 0)24 (rep & 0x7fffff) > 0)
18 {25 {
...@@ -32,4 +39,28 @@ test "divsf3" {...@@ -32,4 +39,28 @@ test "divsf3" {
32 try test__divsf3(1.0, 3.0, 0x3EAAAAAB);39 try test__divsf3(1.0, 3.0, 0x3EAAAAAB);
33 try test__divsf3(2.3509887e-38, 2.0, 0x00800000);40 try test__divsf3(2.3509887e-38, 2.0, 0x00800000);
34 try test__divsf3(1.0, 0x1.fffffep-1, 0x3f800001);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,14 +30,19 @@ fn test__divtf3(a: f128, b: f128, expectedHi: u64, expectedLo: u64) !void {
30}30}
3131
32test "divtf3" {32test "divtf3" {
33 // NaN / any = NaN
34 try test__divtf3(math.nan(f128), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0);33 try test__divtf3(math.nan(f128), 0x1.23456789abcdefp+5, 0x7fff800000000000, 0);
35 // inf / any(except inf and nan) = inf34 try test__divtf3(0x1.23456789abcdefp+5, math.nan(f128), 0x7fff800000000000, 0);
36 try test__divtf3(math.inf(f128), 0x1.23456789abcdefp+5, 0x7fff000000000000, 0);35 try test__divtf3(math.inf(f128), 0x1.23456789abcdefp+5, 0x7fff000000000000, 0);
37 // inf / inf = nan36 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 try test__divtf3(math.inf(f128), math.inf(f128), 0x7fff800000000000, 0);39 try test__divtf3(math.inf(f128), math.inf(f128), 0x7fff800000000000, 0);
39 // inf / nan = nan40 try test__divtf3(0.0, 0.0, 0x7fff800000000000, 0);
40 try test__divtf3(math.inf(f128), math.nan(f128), 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);
4146
42 try test__divtf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.eedcbaba3a94546558237654321fp-1, 0x4004b0b72924d407, 0x0717e84356c6eba2);47 try test__divtf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.eedcbaba3a94546558237654321fp-1, 0x4004b0b72924d407, 0x0717e84356c6eba2);
43 try test__divtf3(0x1.a2b34c56d745382f9abf2c3dfeffp-50, 0x1.ed2c3ba15935332532287654321fp-9, 0x3fd5b2af3f828c9b, 0x40e51f64cde8b1f2);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,6 +4,10 @@ const testing = std.testing;
44
5const __divxf3 = @import("divxf3.zig").__divxf3;5const __divxf3 = @import("divxf3.zig").__divxf3;
66
7const nanRep: u80 = @as(u80, @bitCast(math.nan(f80)));
8const infRep: u80 = @as(u80, @bitCast(math.inf(f80)));
9const negInfRep: u80 = @as(u80, @bitCast(-math.inf(f80)));
10
7fn compareResult(result: f80, expected: u80) bool {11fn compareResult(result: f80, expected: u80) bool {
8 const rep: u80 = @bitCast(result);12 const rep: u80 = @bitCast(result);
913
...@@ -39,14 +43,19 @@ fn test__divxf3(a: f80, b: f80) !void {...@@ -39,14 +43,19 @@ fn test__divxf3(a: f80, b: f80) !void {
39}43}
4044
41test "divxf3" {45test "divxf3" {
42 // NaN / any = NaN46 try expect__divxf3_result(math.nan(f80), 0x1.23456789abcdefp+5, nanRep);
43 try expect__divxf3_result(math.nan(f80), 0x1.23456789abcdefp+5, 0x7fffC000000000000000);47 try expect__divxf3_result(0x1.23456789abcdefp+5, math.nan(f80), nanRep);
44 // inf / any(except inf and nan) = inf48 try expect__divxf3_result(math.inf(f80), 0x1.23456789abcdefp+5, infRep);
45 try expect__divxf3_result(math.inf(f80), 0x1.23456789abcdefp+5, 0x7fff8000000000000000);49 try expect__divxf3_result(-math.inf(f80), 0x1.23456789abcdefp+5, negInfRep);
46 // inf / inf = nan50 try expect__divxf3_result(0x1.23456789abcdefp+5, math.inf(f80), 0x0);
47 try expect__divxf3_result(math.inf(f80), math.inf(f80), 0x7fffC000000000000000);51 try expect__divxf3_result(0x1.23456789abcdefp+5, -math.inf(f80), 0x80000000000000000000);
48 // inf / nan = nan52 try expect__divxf3_result(math.inf(f80), math.inf(f80), nanRep);
49 try expect__divxf3_result(math.inf(f80), math.nan(f80), 0x7fffC000000000000000);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);
5059
51 try test__divxf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.eedcbaba3a94546558237654321fp-1);60 try test__divxf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.eedcbaba3a94546558237654321fp-1);
52 try test__divxf3(0x1.a2b34c56d745382f9abf2c3dfeffp-50, 0x1.ed2c3ba15935332532287654321fp-9);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,9 +151,9 @@ pub fn addCases(cases: *tests.LibcContext) void {
151 // cases.addLibcTestCase("math/asinhl.c", true, .{});151 // cases.addLibcTestCase("math/asinhl.c", true, .{});
152 cases.addLibcTestCase("math/asinl.c", true, .{});152 cases.addLibcTestCase("math/asinl.c", true, .{});
153 cases.addLibcTestCase("math/atan.c", true, .{});153 cases.addLibcTestCase("math/atan.c", true, .{});
154 // cases.addLibcTestCase("math/atan2.c", true, .{});154 cases.addLibcTestCase("math/atan2.c", true, .{});
155 // cases.addLibcTestCase("math/atan2f.c", true, .{});155 cases.addLibcTestCase("math/atan2f.c", true, .{});
156 // cases.addLibcTestCase("math/atan2l.c", true, .{});156 cases.addLibcTestCase("math/atan2l.c", true, .{});
157 cases.addLibcTestCase("math/atanf.c", true, .{});157 cases.addLibcTestCase("math/atanf.c", true, .{});
158 cases.addLibcTestCase("math/atanh.c", true, .{});158 cases.addLibcTestCase("math/atanh.c", true, .{});
159 cases.addLibcTestCase("math/atanhf.c", true, .{});159 cases.addLibcTestCase("math/atanhf.c", true, .{});