| author | |
| committer | |
| log | 484cc15366eb2654be07e449c69ff9b4d04d5973 |
| tree | e0acce4416e3f6fd78c08cd5906d0284dc94ba26 |
| parent | 5082e85de95d491b52fbff2c64cfd5b7190ddc10 |
If the float can store all possible values of the integer without
rounding, coercion is allowed. The integer's precision must be less than
or equal to the float's significand precision.
Closes #186146 files changed, 151 insertions(+), 2 deletions(-)
doc/langref.html.in+10| ... | ... | @@ -3449,6 +3449,16 @@ void do_a_thing(struct Foo *foo) { |
| 3449 | 3449 | </p> |
| 3450 | 3450 | {#code|test_integer_widening.zig#} |
| 3451 | 3451 | |
| 3452 | {#header_close#} | |
| 3453 | {#header_open|Type Coercion: Int to Float#} | |
| 3454 | <p> | |
| 3455 | {#link|Integers#} coerce to {#link|Floats#} if every possible integer value can be stored in the float | |
| 3456 | without rounding (i.e. the integer's precision does not exceed the float's significand precision). | |
| 3457 | Larger integer types that cannot be safely coerced must be explicitly casted with {#link|@floatFromInt#}. | |
| 3458 | </p> | |
| 3459 | {#code|test_int_to_float_coercion.zig#} | |
| 3460 | {#code|test_failed_int_to_float_coercion.zig#} | |
| 3461 | ||
| 3452 | 3462 | {#header_close#} |
| 3453 | 3463 | {#header_open|Type Coercion: Float to Int#} |
| 3454 | 3464 | <p> |
doc/langref/test_failed_int_to_float_coercion.zig created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | test "integer type is too large for implicit cast to float" { | |
| 2 | var int: u25 = 123; | |
| 3 | _ = &int; | |
| 4 | const float: f32 = int; | |
| 5 | _ = float; | |
| 6 | } | |
| 7 | ||
| 8 | // test_error= |
doc/langref/test_int_to_float_coercion.zig created+12| ... | ... | @@ -0,0 +1,12 @@ |
| 1 | const std = @import("std"); | |
| 2 | const expectEqual = std.testing.expectEqual; | |
| 3 | ||
| 4 | test "implicit integer to float" { | |
| 5 | var int: u8 = 123; | |
| 6 | _ = &int; | |
| 7 | const float: f32 = int; | |
| 8 | const int_from_float: u8 = @intFromFloat(float); | |
| 9 | try expectEqual(int, int_from_float); | |
| 10 | } | |
| 11 | ||
| 12 | // test |
src/Sema.zig+16-2| ... | ... | @@ -28715,7 +28715,7 @@ pub fn coerce( |
| 28715 | 28715 | }; |
| 28716 | 28716 | } |
| 28717 | 28717 | |
| 28718 | const CoersionError = CompileError || error{ | |
| 28718 | const CoercionError = CompileError || error{ | |
| 28719 | 28719 | /// When coerce is called recursively, this error should be returned instead of using `fail` |
| 28720 | 28720 | /// to ensure correct types in compile errors. |
| 28721 | 28721 | NotCoercible, |
| ... | ... | @@ -28754,7 +28754,7 @@ fn coerceExtra( |
| 28754 | 28754 | inst: Air.Inst.Ref, |
| 28755 | 28755 | inst_src: LazySrcLoc, |
| 28756 | 28756 | opts: CoerceOpts, |
| 28757 | ) CoersionError!Air.Inst.Ref { | |
| 28757 | ) CoercionError!Air.Inst.Ref { | |
| 28758 | 28758 | const pt = sema.pt; |
| 28759 | 28759 | const zcu = pt.zcu; |
| 28760 | 28760 | const comp = zcu.comp; |
| ... | ... | @@ -29186,6 +29186,20 @@ fn coerceExtra( |
| 29186 | 29186 | if (!opts.report_err) return error.NotCoercible; |
| 29187 | 29187 | return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float }); |
| 29188 | 29188 | } |
| 29189 | const int_info = inst_ty.intInfo(zcu); | |
| 29190 | const int_precision = int_info.bits - @intFromBool(int_info.signedness == .signed); | |
| 29191 | const float_precision: u8 = switch (dest_ty.toIntern()) { | |
| 29192 | .f16_type => 11, | |
| 29193 | .f32_type => 24, | |
| 29194 | .f64_type => 53, | |
| 29195 | .f80_type => 64, | |
| 29196 | .f128_type => 113, | |
| 29197 | else => unreachable, | |
| 29198 | }; | |
| 29199 | if (int_precision <= float_precision) { | |
| 29200 | try sema.requireRuntimeBlock(block, inst_src, null); | |
| 29201 | return block.addTyOp(.float_from_int, dest_ty, inst); | |
| 29202 | } | |
| 29189 | 29203 | break :int; |
| 29190 | 29204 | }; |
| 29191 | 29205 | const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema); |
test/behavior/cast.zig+53| ... | ... | @@ -157,6 +157,59 @@ test "@floatFromInt(f80)" { |
| 157 | 157 | try comptime S.doTheTest(i256); |
| 158 | 158 | } |
| 159 | 159 | |
| 160 | test "type coercion from int to float" { | |
| 161 | const check = struct { | |
| 162 | // Check that an integer value can be coerced to a float type and | |
| 163 | // then converted back to the original value without rounding issues. | |
| 164 | fn value(Float: type, int: anytype) !void { | |
| 165 | const float: Float = int; | |
| 166 | const Int = @TypeOf(int); | |
| 167 | try std.testing.expectEqual(int, @as(Int, @intFromFloat(float))); | |
| 168 | try std.testing.expectEqual(int, @as(Int, @intFromFloat(@ceil(float)))); | |
| 169 | try std.testing.expectEqual(int, @as(Int, @intFromFloat(@floor(float)))); | |
| 170 | } | |
| 171 | ||
| 172 | // Exhaustively check that all possible values of the integer type can | |
| 173 | // safely be coerced to the float type. | |
| 174 | fn allValues(Float: type, Int: type) !void { | |
| 175 | var int: Int = std.math.minInt(Int); | |
| 176 | while (int < std.math.maxInt(Int)) : (int += 1) | |
| 177 | try value(Float, int); | |
| 178 | } | |
| 179 | ||
| 180 | // Check that the min and max values of the integer type can safely be | |
| 181 | // coerced to the float type. | |
| 182 | fn edgeValues(Float: type, Int: type) !void { | |
| 183 | var int: Int = std.math.minInt(Int); | |
| 184 | try value(Float, int); | |
| 185 | int = std.math.maxInt(Int); | |
| 186 | try value(Float, int); | |
| 187 | } | |
| 188 | }; | |
| 189 | ||
| 190 | try check.allValues(f16, u11); | |
| 191 | try check.allValues(f16, i12); | |
| 192 | ||
| 193 | try check.edgeValues(f32, u24); | |
| 194 | try check.edgeValues(f32, i25); | |
| 195 | ||
| 196 | try check.edgeValues(f64, u53); | |
| 197 | try check.edgeValues(f64, i54); | |
| 198 | ||
| 199 | try check.edgeValues(f80, u64); | |
| 200 | try check.edgeValues(f80, i65); | |
| 201 | ||
| 202 | try check.edgeValues(f128, u113); | |
| 203 | try check.edgeValues(f128, i114); | |
| 204 | ||
| 205 | if (builtin.zig_backend == .stage2_aarch64) return error.SkipZigTest; | |
| 206 | if (builtin.zig_backend == .stage2_wasm) return error.SkipZigTest; | |
| 207 | ||
| 208 | // Basic sanity check that the coercions work for vectors too. | |
| 209 | const int_vec: @Vector(2, u24) = @splat(123); | |
| 210 | try check.value(@Vector(2, f32), int_vec); | |
| 211 | } | |
| 212 | ||
| 160 | 213 | test "@intFromFloat" { |
| 161 | 214 | if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO |
| 162 | 215 | if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO |
test/cases/compile_errors/coerce_int_to_float.zig created+52| ... | ... | @@ -0,0 +1,52 @@ |
| 1 | // Test that integer types above a certain size will not coerce to a float. | |
| 2 | ||
| 3 | fn testCoerce(Float: type, Int: type) void { | |
| 4 | var i: Int = 0; | |
| 5 | _ = &i; | |
| 6 | _ = @as(Float, i); | |
| 7 | } | |
| 8 | ||
| 9 | export fn entry() void { | |
| 10 | testCoerce(f16, u11); // Okay | |
| 11 | testCoerce(f16, u12); // Too big | |
| 12 | ||
| 13 | testCoerce(f16, i12); | |
| 14 | testCoerce(f16, i13); | |
| 15 | ||
| 16 | testCoerce(f32, u24); | |
| 17 | testCoerce(f32, u25); | |
| 18 | ||
| 19 | testCoerce(f32, i25); | |
| 20 | testCoerce(f32, i26); | |
| 21 | ||
| 22 | testCoerce(f64, u53); | |
| 23 | testCoerce(f64, u54); | |
| 24 | ||
| 25 | testCoerce(f64, i54); | |
| 26 | testCoerce(f64, i55); | |
| 27 | ||
| 28 | testCoerce(f80, u64); | |
| 29 | testCoerce(f80, u65); | |
| 30 | ||
| 31 | testCoerce(f80, i65); | |
| 32 | testCoerce(f80, i66); | |
| 33 | ||
| 34 | testCoerce(f128, u113); | |
| 35 | testCoerce(f128, u114); | |
| 36 | ||
| 37 | testCoerce(f128, i114); | |
| 38 | testCoerce(f128, i115); | |
| 39 | } | |
| 40 | ||
| 41 | // error | |
| 42 | // | |
| 43 | // :6:20: error: expected type 'f16', found 'u12' | |
| 44 | // :6:20: error: expected type 'f16', found 'i13' | |
| 45 | // :6:20: error: expected type 'f32', found 'u25' | |
| 46 | // :6:20: error: expected type 'f32', found 'i26' | |
| 47 | // :6:20: error: expected type 'f64', found 'u54' | |
| 48 | // :6:20: error: expected type 'f64', found 'i55' | |
| 49 | // :6:20: error: expected type 'f80', found 'u65' | |
| 50 | // :6:20: error: expected type 'f80', found 'i66' | |
| 51 | // :6:20: error: expected type 'f128', found 'u114' | |
| 52 | // :6:20: error: expected type 'f128', found 'i115' |