Commit 64bf8bb146

mlugg <mlugg@mlugg.co.uk>
2025-07-31 11:56:49
std: stop relying on precision-losing coercions
1 parent e664bf4
Changed files (5)
lib/std/math/gamma.zig
@@ -189,19 +189,19 @@ fn series(comptime T: type, abs: T) T {
         2.5066282746310002701649081771338373386264310793408,
     };
     const denominator = [_]T{
-        0,
-        39916800,
-        120543840,
-        150917976,
-        105258076,
-        45995730,
-        13339535,
-        2637558,
-        357423,
-        32670,
-        1925,
-        66,
-        1,
+        0.0,
+        39916800.0,
+        120543840.0,
+        150917976.0,
+        105258076.0,
+        45995730.0,
+        13339535.0,
+        2637558.0,
+        357423.0,
+        32670.0,
+        1925.0,
+        66.0,
+        1.0,
     };
     var num: T = 0;
     var den: T = 0;
@@ -244,9 +244,9 @@ const expectApproxEqRel = std.testing.expectApproxEqRel;
 test gamma {
     inline for (&.{ f32, f64 }) |T| {
         const eps = @sqrt(std.math.floatEps(T));
-        try expectApproxEqRel(@as(T, 120), gamma(T, 6), eps);
-        try expectApproxEqRel(@as(T, 362880), gamma(T, 10), eps);
-        try expectApproxEqRel(@as(T, 6402373705728000), gamma(T, 19), eps);
+        try expectApproxEqRel(@as(T, 120.0), gamma(T, 6), eps);
+        try expectApproxEqRel(@as(T, 362880.0), gamma(T, 10), eps);
+        try expectApproxEqRel(@as(T, 6402373705728000.0), gamma(T, 19), eps);
 
         try expectApproxEqRel(@as(T, 332.7590766955334570), gamma(T, 0.003), eps);
         try expectApproxEqRel(@as(T, 1.377260301981044573), gamma(T, 0.654), eps);
lib/std/math/modf.zig
@@ -74,7 +74,7 @@ fn ModfTests(comptime T: type) type {
             r = modf(@as(T, 43874.3));
             try expectEqual(43874.0, r.ipart);
             // account for precision error
-            const expected_b: T = 43874.3 - @as(T, 43874);
+            const expected_b: T = 43874.3 - @as(T, 43874.0);
             try expectApproxEqAbs(expected_b, r.fpart, epsilon);
 
             r = modf(@as(T, 1234.340780));
lib/std/math/pow.zig
@@ -192,8 +192,8 @@ fn isOddInteger(x: f64) bool {
 }
 
 test isOddInteger {
-    try expect(isOddInteger(math.maxInt(i64) * 2) == false);
-    try expect(isOddInteger(math.maxInt(i64) * 2 + 1) == false);
+    try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2)) == false);
+    try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2 + 1)) == false);
     try expect(isOddInteger(1 << 53) == false);
     try expect(isOddInteger(12.0) == false);
     try expect(isOddInteger(15.0) == true);
lib/std/zon/parse.zig
@@ -2774,11 +2774,11 @@ test "std.zon parse float" {
 
     // Test big integers
     try std.testing.expectEqual(
-        @as(f32, 36893488147419103231),
+        @as(f32, 36893488147419103231.0),
         try fromSlice(f32, gpa, "36893488147419103231", null, .{}),
     );
     try std.testing.expectEqual(
-        @as(f32, -36893488147419103231),
+        @as(f32, -36893488147419103231.0),
         try fromSlice(f32, gpa, "-36893488147419103231", null, .{}),
     );
     try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSlice(
@@ -2788,7 +2788,7 @@ test "std.zon parse float" {
         null,
         .{},
     ));
-    try std.testing.expectEqual(@as(f32, 0x1ffffffffffffffff), try fromSlice(
+    try std.testing.expectEqual(@as(f32, @floatFromInt(0x1ffffffffffffffff)), try fromSlice(
         f32,
         gpa,
         "0x1ffffffffffffffff",
lib/std/math.zig
@@ -1345,11 +1345,15 @@ pub fn lossyCast(comptime T: type, value: anytype) T {
                     }
                 },
                 .float, .comptime_float => {
+                    // In extreme cases, we probably need a language enhancement to be able to
+                    // specify a rounding mode here to prevent `@intFromFloat` panics.
+                    const max: @TypeOf(value) = @floatFromInt(maxInt(T));
+                    const min: @TypeOf(value) = @floatFromInt(minInt(T));
                     if (isNan(value)) {
                         return 0;
-                    } else if (value >= maxInt(T)) {
+                    } else if (value >= max) {
                         return maxInt(T);
-                    } else if (value <= minInt(T)) {
+                    } else if (value <= min) {
                         return minInt(T);
                     } else {
                         return @intFromFloat(value);
@@ -1366,7 +1370,7 @@ test lossyCast {
     try testing.expect(lossyCast(i16, 70000.0) == @as(i16, 32767));
     try testing.expect(lossyCast(u32, @as(i16, -255)) == @as(u32, 0));
     try testing.expect(lossyCast(i9, @as(u32, 200)) == @as(i9, 200));
-    try testing.expect(lossyCast(u32, @as(f32, maxInt(u32))) == maxInt(u32));
+    try testing.expect(lossyCast(u32, @as(f32, @floatFromInt(maxInt(u32)))) == maxInt(u32));
     try testing.expect(lossyCast(u32, nan(f32)) == 0);
 }