authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-07-31 10:56:49+01:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-07-31 10:57:04+01:00
log64bf8bb146099b51d74635a1f116a913e442bcf4
tree2f9cba090e0fdea54b9bffdbd76e5c729756464d
parente664bf4d81e9266ee4749b5da88cab4554499bf6
signaturelock-open Commit is signed but in an unrecognized format.

std: stop relying on precision-losing coercions


5 files changed, 29 insertions(+), 25 deletions(-)

lib/std/math.zig+7-3
...@@ -1345,11 +1345,15 @@ pub fn lossyCast(comptime T: type, value: anytype) T {...@@ -1345,11 +1345,15 @@ pub fn lossyCast(comptime T: type, value: anytype) T {
1345 }1345 }
1346 },1346 },
1347 .float, .comptime_float => {1347 .float, .comptime_float => {
1348 // In extreme cases, we probably need a language enhancement to be able to
1349 // specify a rounding mode here to prevent `@intFromFloat` panics.
1350 const max: @TypeOf(value) = @floatFromInt(maxInt(T));
1351 const min: @TypeOf(value) = @floatFromInt(minInt(T));
1348 if (isNan(value)) {1352 if (isNan(value)) {
1349 return 0;1353 return 0;
1350 } else if (value >= maxInt(T)) {1354 } else if (value >= max) {
1351 return maxInt(T);1355 return maxInt(T);
1352 } else if (value <= minInt(T)) {1356 } else if (value <= min) {
1353 return minInt(T);1357 return minInt(T);
1354 } else {1358 } else {
1355 return @intFromFloat(value);1359 return @intFromFloat(value);
...@@ -1366,7 +1370,7 @@ test lossyCast {...@@ -1366,7 +1370,7 @@ test lossyCast {
1366 try testing.expect(lossyCast(i16, 70000.0) == @as(i16, 32767));1370 try testing.expect(lossyCast(i16, 70000.0) == @as(i16, 32767));
1367 try testing.expect(lossyCast(u32, @as(i16, -255)) == @as(u32, 0));1371 try testing.expect(lossyCast(u32, @as(i16, -255)) == @as(u32, 0));
1368 try testing.expect(lossyCast(i9, @as(u32, 200)) == @as(i9, 200));1372 try testing.expect(lossyCast(i9, @as(u32, 200)) == @as(i9, 200));
1369 try testing.expect(lossyCast(u32, @as(f32, maxInt(u32))) == maxInt(u32));1373 try testing.expect(lossyCast(u32, @as(f32, @floatFromInt(maxInt(u32)))) == maxInt(u32));
1370 try testing.expect(lossyCast(u32, nan(f32)) == 0);1374 try testing.expect(lossyCast(u32, nan(f32)) == 0);
1371}1375}
13721376
lib/std/math/gamma.zig+16-16
...@@ -189,19 +189,19 @@ fn series(comptime T: type, abs: T) T {...@@ -189,19 +189,19 @@ fn series(comptime T: type, abs: T) T {
189 2.5066282746310002701649081771338373386264310793408,189 2.5066282746310002701649081771338373386264310793408,
190 };190 };
191 const denominator = [_]T{191 const denominator = [_]T{
192 0,192 0.0,
193 39916800,193 39916800.0,
194 120543840,194 120543840.0,
195 150917976,195 150917976.0,
196 105258076,196 105258076.0,
197 45995730,197 45995730.0,
198 13339535,198 13339535.0,
199 2637558,199 2637558.0,
200 357423,200 357423.0,
201 32670,201 32670.0,
202 1925,202 1925.0,
203 66,203 66.0,
204 1,204 1.0,
205 };205 };
206 var num: T = 0;206 var num: T = 0;
207 var den: T = 0;207 var den: T = 0;
...@@ -244,9 +244,9 @@ const expectApproxEqRel = std.testing.expectApproxEqRel;...@@ -244,9 +244,9 @@ const expectApproxEqRel = std.testing.expectApproxEqRel;
244test gamma {244test gamma {
245 inline for (&.{ f32, f64 }) |T| {245 inline for (&.{ f32, f64 }) |T| {
246 const eps = @sqrt(std.math.floatEps(T));246 const eps = @sqrt(std.math.floatEps(T));
247 try expectApproxEqRel(@as(T, 120), gamma(T, 6), eps);247 try expectApproxEqRel(@as(T, 120.0), gamma(T, 6), eps);
248 try expectApproxEqRel(@as(T, 362880), gamma(T, 10), eps);248 try expectApproxEqRel(@as(T, 362880.0), gamma(T, 10), eps);
249 try expectApproxEqRel(@as(T, 6402373705728000), gamma(T, 19), eps);249 try expectApproxEqRel(@as(T, 6402373705728000.0), gamma(T, 19), eps);
250250
251 try expectApproxEqRel(@as(T, 332.7590766955334570), gamma(T, 0.003), eps);251 try expectApproxEqRel(@as(T, 332.7590766955334570), gamma(T, 0.003), eps);
252 try expectApproxEqRel(@as(T, 1.377260301981044573), gamma(T, 0.654), eps);252 try expectApproxEqRel(@as(T, 1.377260301981044573), gamma(T, 0.654), eps);
lib/std/math/modf.zig+1-1
...@@ -74,7 +74,7 @@ fn ModfTests(comptime T: type) type {...@@ -74,7 +74,7 @@ fn ModfTests(comptime T: type) type {
74 r = modf(@as(T, 43874.3));74 r = modf(@as(T, 43874.3));
75 try expectEqual(43874.0, r.ipart);75 try expectEqual(43874.0, r.ipart);
76 // account for precision error76 // account for precision error
77 const expected_b: T = 43874.3 - @as(T, 43874);77 const expected_b: T = 43874.3 - @as(T, 43874.0);
78 try expectApproxEqAbs(expected_b, r.fpart, epsilon);78 try expectApproxEqAbs(expected_b, r.fpart, epsilon);
7979
80 r = modf(@as(T, 1234.340780));80 r = modf(@as(T, 1234.340780));
lib/std/math/pow.zig+2-2
...@@ -192,8 +192,8 @@ fn isOddInteger(x: f64) bool {...@@ -192,8 +192,8 @@ fn isOddInteger(x: f64) bool {
192}192}
193193
194test isOddInteger {194test isOddInteger {
195 try expect(isOddInteger(math.maxInt(i64) * 2) == false);195 try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2)) == false);
196 try expect(isOddInteger(math.maxInt(i64) * 2 + 1) == false);196 try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2 + 1)) == false);
197 try expect(isOddInteger(1 << 53) == false);197 try expect(isOddInteger(1 << 53) == false);
198 try expect(isOddInteger(12.0) == false);198 try expect(isOddInteger(12.0) == false);
199 try expect(isOddInteger(15.0) == true);199 try expect(isOddInteger(15.0) == true);
lib/std/zon/parse.zig+3-3
...@@ -2774,11 +2774,11 @@ test "std.zon parse float" {...@@ -2774,11 +2774,11 @@ test "std.zon parse float" {
27742774
2775 // Test big integers2775 // Test big integers
2776 try std.testing.expectEqual(2776 try std.testing.expectEqual(
2777 @as(f32, 36893488147419103231),2777 @as(f32, 36893488147419103231.0),
2778 try fromSlice(f32, gpa, "36893488147419103231", null, .{}),2778 try fromSlice(f32, gpa, "36893488147419103231", null, .{}),
2779 );2779 );
2780 try std.testing.expectEqual(2780 try std.testing.expectEqual(
2781 @as(f32, -36893488147419103231),2781 @as(f32, -36893488147419103231.0),
2782 try fromSlice(f32, gpa, "-36893488147419103231", null, .{}),2782 try fromSlice(f32, gpa, "-36893488147419103231", null, .{}),
2783 );2783 );
2784 try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSlice(2784 try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSlice(
...@@ -2788,7 +2788,7 @@ test "std.zon parse float" {...@@ -2788,7 +2788,7 @@ test "std.zon parse float" {
2788 null,2788 null,
2789 .{},2789 .{},
2790 ));2790 ));
2791 try std.testing.expectEqual(@as(f32, 0x1ffffffffffffffff), try fromSlice(2791 try std.testing.expectEqual(@as(f32, @floatFromInt(0x1ffffffffffffffff)), try fromSlice(
2792 f32,2792 f32,
2793 gpa,2793 gpa,
2794 "0x1ffffffffffffffff",2794 "0x1ffffffffffffffff",