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 @@
44
55const std = @import("std");
66const compiler_rt = @import("../compiler_rt.zig");
7const symbol = @import("../compiler_rt.zig").symbol;
7const symbol = compiler_rt.symbol;
88
99const normalize = compiler_rt.normalize;
1010const wideMultiply = compiler_rt.wideMultiply;
......@@ -189,14 +189,13 @@ inline fn div(a: f64, b: f64) f64 {
189189
190190 const writtenExponent = quotientExponent +% exponentBias;
191191
192 const round = @intFromBool((residual << 1) >= bSignificand);
193
192194 if (writtenExponent >= maxExponent) {
193195 // If we have overflowed the exponent, return infinity.
194196 return @bitCast(infRep | quotientSign);
195197 } else if (writtenExponent < 1) {
196198 if (writtenExponent == 0) {
197 // Check whether the rounded result is normal.
198 const round = @intFromBool((residual << 1) > bSignificand);
199 // Clear the implicit bit.
200199 var absResult = quotient & significandMask;
201200 // Round.
202201 absResult += round;
......@@ -205,11 +204,16 @@ inline fn div(a: f64, b: f64) f64 {
205204 return @bitCast(absResult | quotientSign);
206205 }
207206 }
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);
211216 } else {
212 const round = @intFromBool((residual << 1) > bSignificand);
213217 // Clear the implicit bit
214218 var absResult = quotient & significandMask;
215219 // Insert the exponent
lib/compiler_rt/divdf3_test.zig+33-2
......@@ -2,8 +2,15 @@
22//
33// 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
59const __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
815fn compareResultD(result: f64, expected: u64) bool {
916 const rep: u64 = @bitCast(result);
......@@ -12,7 +19,7 @@ fn compareResultD(result: f64, expected: u64) bool {
1219 return true;
1320 }
1421 // test other possible NaN representation(signal NaN)
15 else if (expected == 0x7ff8000000000000) {
22 else if (expected == nanRep) {
1623 if ((rep & 0x7ff0000000000000) == 0x7ff0000000000000 and
1724 (rep & 0xfffffffffffff) > 0)
1825 {
......@@ -32,4 +39,28 @@ test "divdf3" {
3239 try test__divdf3(1.0, 3.0, 0x3fd5555555555555);
3340 try test__divdf3(4.450147717014403e-308, 2.0, 0x10000000000000);
3441 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);
3566}
lib/compiler_rt/divsf3.zig+12-7
......@@ -5,7 +5,7 @@
55const std = @import("std");
66
77const compiler_rt = @import("../compiler_rt.zig");
8const symbol = @import("../compiler_rt.zig").symbol;
8const symbol = compiler_rt.symbol;
99const normalize = compiler_rt.normalize;
1010
1111comptime {
......@@ -170,14 +170,14 @@ inline fn div(a: f32, b: f32) f32 {
170170
171171 const writtenExponent = quotientExponent +% exponentBias;
172172
173 const round = @intFromBool((residual << 1) >= bSignificand);
174
173175 if (writtenExponent >= maxExponent) {
174176 // If we have overflowed the exponent, return infinity.
175177 return @bitCast(infRep | quotientSign);
176178 } else if (writtenExponent < 1) {
177179 if (writtenExponent == 0) {
178180 // Check whether the rounded result is normal.
179 const round = @intFromBool((residual << 1) > bSignificand);
180 // Clear the implicit bit.
181181 var absResult = quotient & significandMask;
182182 // Round.
183183 absResult += round;
......@@ -186,11 +186,16 @@ inline fn div(a: f32, b: f32) f32 {
186186 return @bitCast(absResult | quotientSign);
187187 }
188188 }
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);
192198 } else {
193 const round = @intFromBool((residual << 1) > bSignificand);
194199 // Clear the implicit bit
195200 var absResult = quotient & significandMask;
196201 // Insert the exponent
lib/compiler_rt/divsf3_test.zig+33-2
......@@ -2,8 +2,15 @@
22//
33// 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
59const __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
815fn compareResultF(result: f32, expected: u32) bool {
916 const rep: u32 = @bitCast(result);
......@@ -12,7 +19,7 @@ fn compareResultF(result: f32, expected: u32) bool {
1219 return true;
1320 }
1421 // test other possible NaN representation(signal NaN)
15 else if (expected == 0x7fc00000) {
22 else if (expected == nanRep) {
1623 if ((rep & 0x7f800000) == 0x7f800000 and
1724 (rep & 0x7fffff) > 0)
1825 {
......@@ -32,4 +39,28 @@ test "divsf3" {
3239 try test__divsf3(1.0, 3.0, 0x3EAAAAAB);
3340 try test__divsf3(2.3509887e-38, 2.0, 0x00800000);
3441 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);
3566}
lib/compiler_rt/divtf3_test.zig+10-5
......@@ -30,14 +30,19 @@ fn test__divtf3(a: f128, b: f128, expectedHi: u64, expectedLo: u64) !void {
3030}
3131
3232test "divtf3" {
33 // NaN / any = NaN
3433 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);
3635 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);
3839 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);
4146
4247 try test__divtf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.eedcbaba3a94546558237654321fp-1, 0x4004b0b72924d407, 0x0717e84356c6eba2);
4348 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;
44
55const __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
711fn compareResult(result: f80, expected: u80) bool {
812 const rep: u80 = @bitCast(result);
913
......@@ -39,14 +43,19 @@ fn test__divxf3(a: f80, b: f80) !void {
3943}
4044
4145test "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);
5059
5160 try test__divxf3(0x1.a23b45362464523375893ab4cdefp+5, 0x1.eedcbaba3a94546558237654321fp-1);
5261 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 {
151151 // cases.addLibcTestCase("math/asinhl.c", true, .{});
152152 cases.addLibcTestCase("math/asinl.c", true, .{});
153153 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, .{});
157157 cases.addLibcTestCase("math/atanf.c", true, .{});
158158 cases.addLibcTestCase("math/atanh.c", true, .{});
159159 cases.addLibcTestCase("math/atanhf.c", true, .{});