| author | |
| committer | |
| log | 04d7b491b436f67e51a17ba6fb49de862fa7bb8c |
| tree | 822bb5cf73747ba4c82aea49ecc4fc777a9c5979 |
| parent | 982c387753c33a9eb42349c109fc4a6ed0675165 |
| parent | 64bf8bb146099b51d74635a1f116a913e442bcf4 |
| signature |
Sema: compile error on lossy int to float coercion7 files changed, 68 insertions(+), 36 deletions(-)
lib/std/math.zig+7-3| ... | ... | @@ -1345,11 +1345,15 @@ pub fn lossyCast(comptime T: type, value: anytype) T { |
| 1345 | 1345 | } |
| 1346 | 1346 | }, |
| 1347 | 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 | 1352 | if (isNan(value)) { |
| 1349 | 1353 | return 0; |
| 1350 | } else if (value >= maxInt(T)) { | |
| 1354 | } else if (value >= max) { | |
| 1351 | 1355 | return maxInt(T); |
| 1352 | } else if (value <= minInt(T)) { | |
| 1356 | } else if (value <= min) { | |
| 1353 | 1357 | return minInt(T); |
| 1354 | 1358 | } else { |
| 1355 | 1359 | return @intFromFloat(value); |
| ... | ... | @@ -1366,7 +1370,7 @@ test lossyCast { |
| 1366 | 1370 | try testing.expect(lossyCast(i16, 70000.0) == @as(i16, 32767)); |
| 1367 | 1371 | try testing.expect(lossyCast(u32, @as(i16, -255)) == @as(u32, 0)); |
| 1368 | 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 | 1374 | try testing.expect(lossyCast(u32, nan(f32)) == 0); |
| 1371 | 1375 | } |
| 1372 | 1376 |
lib/std/math/gamma.zig+16-16| ... | ... | @@ -189,19 +189,19 @@ fn series(comptime T: type, abs: T) T { |
| 189 | 189 | 2.5066282746310002701649081771338373386264310793408, |
| 190 | 190 | }; |
| 191 | 191 | const denominator = [_]T{ |
| 192 | 0, | |
| 193 | 39916800, | |
| 194 | 120543840, | |
| 195 | 150917976, | |
| 196 | 105258076, | |
| 197 | 45995730, | |
| 198 | 13339535, | |
| 199 | 2637558, | |
| 200 | 357423, | |
| 201 | 32670, | |
| 202 | 1925, | |
| 203 | 66, | |
| 204 | 1, | |
| 192 | 0.0, | |
| 193 | 39916800.0, | |
| 194 | 120543840.0, | |
| 195 | 150917976.0, | |
| 196 | 105258076.0, | |
| 197 | 45995730.0, | |
| 198 | 13339535.0, | |
| 199 | 2637558.0, | |
| 200 | 357423.0, | |
| 201 | 32670.0, | |
| 202 | 1925.0, | |
| 203 | 66.0, | |
| 204 | 1.0, | |
| 205 | 205 | }; |
| 206 | 206 | var num: T = 0; |
| 207 | 207 | var den: T = 0; |
| ... | ... | @@ -244,9 +244,9 @@ const expectApproxEqRel = std.testing.expectApproxEqRel; |
| 244 | 244 | test gamma { |
| 245 | 245 | inline for (&.{ f32, f64 }) |T| { |
| 246 | 246 | const eps = @sqrt(std.math.floatEps(T)); |
| 247 | try expectApproxEqRel(@as(T, 120), gamma(T, 6), eps); | |
| 248 | try expectApproxEqRel(@as(T, 362880), gamma(T, 10), eps); | |
| 249 | try expectApproxEqRel(@as(T, 6402373705728000), gamma(T, 19), eps); | |
| 247 | try expectApproxEqRel(@as(T, 120.0), gamma(T, 6), eps); | |
| 248 | try expectApproxEqRel(@as(T, 362880.0), gamma(T, 10), eps); | |
| 249 | try expectApproxEqRel(@as(T, 6402373705728000.0), gamma(T, 19), eps); | |
| 250 | 250 | |
| 251 | 251 | try expectApproxEqRel(@as(T, 332.7590766955334570), gamma(T, 0.003), eps); |
| 252 | 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 | 74 | r = modf(@as(T, 43874.3)); |
| 75 | 75 | try expectEqual(43874.0, r.ipart); |
| 76 | 76 | // 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 | 78 | try expectApproxEqAbs(expected_b, r.fpart, epsilon); |
| 79 | 79 | |
| 80 | 80 | r = modf(@as(T, 1234.340780)); |
lib/std/math/pow.zig+2-2| ... | ... | @@ -192,8 +192,8 @@ fn isOddInteger(x: f64) bool { |
| 192 | 192 | } |
| 193 | 193 | |
| 194 | 194 | test isOddInteger { |
| 195 | try expect(isOddInteger(math.maxInt(i64) * 2) == false); | |
| 196 | try expect(isOddInteger(math.maxInt(i64) * 2 + 1) == false); | |
| 195 | try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2)) == false); | |
| 196 | try expect(isOddInteger(@floatFromInt(math.maxInt(i64) * 2 + 1)) == false); | |
| 197 | 197 | try expect(isOddInteger(1 << 53) == false); |
| 198 | 198 | try expect(isOddInteger(12.0) == false); |
| 199 | 199 | try expect(isOddInteger(15.0) == true); |
lib/std/zon/parse.zig+3-3| ... | ... | @@ -2774,11 +2774,11 @@ test "std.zon parse float" { |
| 2774 | 2774 | |
| 2775 | 2775 | // Test big integers |
| 2776 | 2776 | try std.testing.expectEqual( |
| 2777 | @as(f32, 36893488147419103231), | |
| 2777 | @as(f32, 36893488147419103231.0), | |
| 2778 | 2778 | try fromSlice(f32, gpa, "36893488147419103231", null, .{}), |
| 2779 | 2779 | ); |
| 2780 | 2780 | try std.testing.expectEqual( |
| 2781 | @as(f32, -36893488147419103231), | |
| 2781 | @as(f32, -36893488147419103231.0), | |
| 2782 | 2782 | try fromSlice(f32, gpa, "-36893488147419103231", null, .{}), |
| 2783 | 2783 | ); |
| 2784 | 2784 | try std.testing.expectEqual(@as(f128, 0x1ffffffffffffffff), try fromSlice( |
| ... | ... | @@ -2788,7 +2788,7 @@ test "std.zon parse float" { |
| 2788 | 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 | 2792 | f32, |
| 2793 | 2793 | gpa, |
| 2794 | 2794 | "0x1ffffffffffffffff", |
src/Sema.zig+30-11| ... | ... | @@ -28745,17 +28745,36 @@ fn coerceExtra( |
| 28745 | 28745 | break :int; |
| 28746 | 28746 | }; |
| 28747 | 28747 | const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema); |
| 28748 | // TODO implement this compile error | |
| 28749 | //const int_again_val = try result_val.intFromFloat(sema.arena, inst_ty); | |
| 28750 | //if (!int_again_val.eql(val, inst_ty, zcu)) { | |
| 28751 | // return sema.fail( | |
| 28752 | // block, | |
| 28753 | // inst_src, | |
| 28754 | // "type '{f}' cannot represent integer value '{f}'", | |
| 28755 | // .{ dest_ty.fmt(pt), val }, | |
| 28756 | // ); | |
| 28757 | //} | |
| 28758 | return Air.internedToRef(result_val.toIntern()); | |
| 28748 | const fits: bool = switch (ip.indexToKey(result_val.toIntern())) { | |
| 28749 | else => unreachable, | |
| 28750 | .undef => true, | |
| 28751 | .float => |float| fits: { | |
| 28752 | var buffer: InternPool.Key.Int.Storage.BigIntSpace = undefined; | |
| 28753 | const operand_big_int = val.toBigInt(&buffer, zcu); | |
| 28754 | switch (float.storage) { | |
| 28755 | inline else => |x| { | |
| 28756 | if (!std.math.isFinite(x)) break :fits false; | |
| 28757 | var result_big_int: std.math.big.int.Mutable = .{ | |
| 28758 | .limbs = try sema.arena.alloc(std.math.big.Limb, std.math.big.int.calcLimbLen(x)), | |
| 28759 | .len = undefined, | |
| 28760 | .positive = undefined, | |
| 28761 | }; | |
| 28762 | switch (result_big_int.setFloat(x, .nearest_even)) { | |
| 28763 | .inexact => break :fits false, | |
| 28764 | .exact => {}, | |
| 28765 | } | |
| 28766 | break :fits result_big_int.toConst().eql(operand_big_int); | |
| 28767 | }, | |
| 28768 | } | |
| 28769 | }, | |
| 28770 | }; | |
| 28771 | if (!fits) return sema.fail( | |
| 28772 | block, | |
| 28773 | inst_src, | |
| 28774 | "type '{f}' cannot represent integer value '{f}'", | |
| 28775 | .{ dest_ty.fmt(pt), val.fmtValue(pt) }, | |
| 28776 | ); | |
| 28777 | return .fromValue(result_val); | |
| 28759 | 28778 | }, |
| 28760 | 28779 | else => {}, |
| 28761 | 28780 | }, |
test/cases/compile_errors/int_to_float_coercion_loses_precision.zig created+9| ... | ... | @@ -0,0 +1,9 @@ |
| 1 | export fn foo() void { | |
| 2 | const int: u16 = 65535; | |
| 3 | const float: f16 = int; | |
| 4 | _ = float; | |
| 5 | } | |
| 6 | ||
| 7 | // error | |
| 8 | // | |
| 9 | // :3:24: error: type 'f16' cannot represent integer value '65535' |