mirror of
https://codeberg.org/ziglang/zig.git
synced 2025-12-06 05:44:20 +00:00
Sema: Allow small integer types to coerce to floats
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 #18614
This commit is contained in:
parent
a38220376e
commit
464fcac802
6 changed files with 150 additions and 2 deletions
|
|
@ -3449,6 +3449,16 @@ void do_a_thing(struct Foo *foo) {
|
||||||
</p>
|
</p>
|
||||||
{#code|test_integer_widening.zig#}
|
{#code|test_integer_widening.zig#}
|
||||||
|
|
||||||
|
{#header_close#}
|
||||||
|
{#header_open|Type Coercion: Int to Float#}
|
||||||
|
<p>
|
||||||
|
{#link|Integers} coerce to {#link|Floats} if every possible integer value can be stored in the float
|
||||||
|
without rounding (i.e. the integer's precision does not exceed the float's significand precision).
|
||||||
|
Larger integer types that cannot be safely coerced must be explicitly casted with {#link|@floatFromInt#}.
|
||||||
|
</p>
|
||||||
|
{#code|test_int_to_float_coercion.zig#}
|
||||||
|
{#code|test_failed_int_to_float_coercion.zig#}
|
||||||
|
|
||||||
{#header_close#}
|
{#header_close#}
|
||||||
{#header_open|Type Coercion: Float to Int#}
|
{#header_open|Type Coercion: Float to Int#}
|
||||||
<p>
|
<p>
|
||||||
|
|
|
||||||
8
doc/langref/test_failed_into_to_float_coercion.zig
Normal file
8
doc/langref/test_failed_into_to_float_coercion.zig
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
test "integer type is too large for implicit cast to float" {
|
||||||
|
var int: u25 = 123;
|
||||||
|
_ = ∫
|
||||||
|
const float: f32 = int;
|
||||||
|
_ = float;
|
||||||
|
}
|
||||||
|
|
||||||
|
// test_error=
|
||||||
12
doc/langref/test_int_to_float_coercion.zig
Normal file
12
doc/langref/test_int_to_float_coercion.zig
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const expectEqual = std.testing.expectEqual;
|
||||||
|
|
||||||
|
test "implicit integer to float" {
|
||||||
|
var int: u8 = 123;
|
||||||
|
_ = ∫
|
||||||
|
const float: f32 = int;
|
||||||
|
const int_from_float: u8 = @intFromFloat(float);
|
||||||
|
try expectEqual(int, int_from_float);
|
||||||
|
}
|
||||||
|
|
||||||
|
// test
|
||||||
18
src/Sema.zig
18
src/Sema.zig
|
|
@ -28477,7 +28477,7 @@ pub fn coerce(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const CoersionError = CompileError || error{
|
const CoercionError = CompileError || error{
|
||||||
/// When coerce is called recursively, this error should be returned instead of using `fail`
|
/// When coerce is called recursively, this error should be returned instead of using `fail`
|
||||||
/// to ensure correct types in compile errors.
|
/// to ensure correct types in compile errors.
|
||||||
NotCoercible,
|
NotCoercible,
|
||||||
|
|
@ -28516,7 +28516,7 @@ fn coerceExtra(
|
||||||
inst: Air.Inst.Ref,
|
inst: Air.Inst.Ref,
|
||||||
inst_src: LazySrcLoc,
|
inst_src: LazySrcLoc,
|
||||||
opts: CoerceOpts,
|
opts: CoerceOpts,
|
||||||
) CoersionError!Air.Inst.Ref {
|
) CoercionError!Air.Inst.Ref {
|
||||||
if (dest_ty.isGenericPoison()) return inst;
|
if (dest_ty.isGenericPoison()) return inst;
|
||||||
const pt = sema.pt;
|
const pt = sema.pt;
|
||||||
const zcu = pt.zcu;
|
const zcu = pt.zcu;
|
||||||
|
|
@ -28943,6 +28943,20 @@ fn coerceExtra(
|
||||||
if (!opts.report_err) return error.NotCoercible;
|
if (!opts.report_err) return error.NotCoercible;
|
||||||
return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float });
|
return sema.failWithNeededComptime(block, inst_src, .{ .simple = .casted_to_comptime_float });
|
||||||
}
|
}
|
||||||
|
const int_info = inst_ty.intInfo(zcu);
|
||||||
|
const int_precision = int_info.bits - @intFromBool(int_info.signedness == .signed);
|
||||||
|
const float_precision: u8 = switch (dest_ty.toIntern()) {
|
||||||
|
.f16_type => 11,
|
||||||
|
.f32_type => 24,
|
||||||
|
.f64_type => 53,
|
||||||
|
.f80_type => 64,
|
||||||
|
.f128_type => 113,
|
||||||
|
else => unreachable,
|
||||||
|
};
|
||||||
|
if (int_precision <= float_precision) {
|
||||||
|
try sema.requireRuntimeBlock(block, inst_src, null);
|
||||||
|
return block.addTyOp(.float_from_int, dest_ty, inst);
|
||||||
|
}
|
||||||
break :int;
|
break :int;
|
||||||
};
|
};
|
||||||
const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema);
|
const result_val = try val.floatFromIntAdvanced(sema.arena, inst_ty, dest_ty, pt, .sema);
|
||||||
|
|
|
||||||
|
|
@ -157,6 +157,58 @@ test "@floatFromInt(f80)" {
|
||||||
try comptime S.doTheTest(i256);
|
try comptime S.doTheTest(i256);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "type coercion from int to float" {
|
||||||
|
const check = struct {
|
||||||
|
// Check that an integer value can be coerced to a float type and
|
||||||
|
// then converted back to the original value without rounding issues.
|
||||||
|
fn value(Float: type, int: anytype) !void {
|
||||||
|
const float: Float = int;
|
||||||
|
const Int = @TypeOf(int);
|
||||||
|
try std.testing.expectEqual(int, @as(Int, @intFromFloat(float)));
|
||||||
|
if (Float != f80) { // https://codeberg.org/ziglang/zig/issues/30035
|
||||||
|
try std.testing.expectEqual(int, @as(Int, @intFromFloat(@ceil(float))));
|
||||||
|
try std.testing.expectEqual(int, @as(Int, @intFromFloat(@floor(float))));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Exhaustively check that all possible values of the integer type can
|
||||||
|
// safely be coerced to the float type.
|
||||||
|
fn allValues(Float: type, Int: type) !void {
|
||||||
|
var int: Int = std.math.minInt(Int);
|
||||||
|
while (int < std.math.maxInt(Int)) : (int += 1)
|
||||||
|
try value(Float, int);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the min and max values of the integer type can safely be
|
||||||
|
// coerced to the float type.
|
||||||
|
fn edgeValues(Float: type, Int: type) !void {
|
||||||
|
var int: Int = std.math.minInt(Int);
|
||||||
|
try value(Float, int);
|
||||||
|
int = std.math.maxInt(Int);
|
||||||
|
try value(Float, int);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
try check.allValues(f16, u11);
|
||||||
|
try check.allValues(f16, i12);
|
||||||
|
|
||||||
|
try check.edgeValues(f32, u24);
|
||||||
|
try check.edgeValues(f32, i25);
|
||||||
|
|
||||||
|
try check.edgeValues(f64, u53);
|
||||||
|
try check.edgeValues(f64, i54);
|
||||||
|
|
||||||
|
try check.edgeValues(f80, u64);
|
||||||
|
try check.edgeValues(f80, i65);
|
||||||
|
|
||||||
|
try check.edgeValues(f128, u113);
|
||||||
|
try check.edgeValues(f128, i114);
|
||||||
|
|
||||||
|
// Basic sanity check that the coercions work for vectors too.
|
||||||
|
const int_vec: @Vector(2, u24) = @splat(123);
|
||||||
|
try check.value(@Vector(2, f32), int_vec);
|
||||||
|
}
|
||||||
|
|
||||||
test "@intFromFloat" {
|
test "@intFromFloat" {
|
||||||
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_arm) return error.SkipZigTest; // TODO
|
||||||
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
if (builtin.zig_backend == .stage2_sparc64) return error.SkipZigTest; // TODO
|
||||||
|
|
|
||||||
52
test/cases/compile_errors/coerce_int_to_float.zig
Normal file
52
test/cases/compile_errors/coerce_int_to_float.zig
Normal file
|
|
@ -0,0 +1,52 @@
|
||||||
|
// Test that integer types above a certain size will not coerce to a float.
|
||||||
|
|
||||||
|
fn testCoerce(Float: type, Int: type) void {
|
||||||
|
var i: Int = 0;
|
||||||
|
_ = &i;
|
||||||
|
_ = @as(Float, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
export fn entry() void {
|
||||||
|
testCoerce(f16, u11); // Okay
|
||||||
|
testCoerce(f16, u12); // Too big
|
||||||
|
|
||||||
|
testCoerce(f16, i12);
|
||||||
|
testCoerce(f16, i13);
|
||||||
|
|
||||||
|
testCoerce(f32, u24);
|
||||||
|
testCoerce(f32, u25);
|
||||||
|
|
||||||
|
testCoerce(f32, i25);
|
||||||
|
testCoerce(f32, i26);
|
||||||
|
|
||||||
|
testCoerce(f64, u53);
|
||||||
|
testCoerce(f64, u54);
|
||||||
|
|
||||||
|
testCoerce(f64, i54);
|
||||||
|
testCoerce(f64, i55);
|
||||||
|
|
||||||
|
testCoerce(f80, u64);
|
||||||
|
testCoerce(f80, u65);
|
||||||
|
|
||||||
|
testCoerce(f80, i65);
|
||||||
|
testCoerce(f80, i66);
|
||||||
|
|
||||||
|
testCoerce(f128, u113);
|
||||||
|
testCoerce(f128, u114);
|
||||||
|
|
||||||
|
testCoerce(f128, i114);
|
||||||
|
testCoerce(f128, i115);
|
||||||
|
}
|
||||||
|
|
||||||
|
// error
|
||||||
|
//
|
||||||
|
// :6:20: error: expected type 'f16', found 'u12'
|
||||||
|
// :6:20: error: expected type 'f16', found 'i13'
|
||||||
|
// :6:20: error: expected type 'f32', found 'u25'
|
||||||
|
// :6:20: error: expected type 'f32', found 'i26'
|
||||||
|
// :6:20: error: expected type 'f64', found 'u54'
|
||||||
|
// :6:20: error: expected type 'f64', found 'i55'
|
||||||
|
// :6:20: error: expected type 'f80', found 'u65'
|
||||||
|
// :6:20: error: expected type 'f80', found 'i66'
|
||||||
|
// :6:20: error: expected type 'f128', found 'u114'
|
||||||
|
// :6:20: error: expected type 'f128', found 'i115'
|
||||||
Loading…
Add table
Reference in a new issue