Commit 0d7359ca9b
Changed files (4)
test
behavior
src/Sema.zig
@@ -9642,9 +9642,29 @@ fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A
fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
const inst_data = sema.code.instructions.items(.data)[inst].pl_node;
- const src = inst_data.src();
- // TODO don't forget the safety check!
- return sema.fail(block, src, "TODO: Sema.zirFloatToInt", .{});
+ const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data;
+ const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node };
+ const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node };
+ const dest_ty = try sema.resolveType(block, ty_src, extra.lhs);
+ const operand = sema.resolveInst(extra.rhs);
+ const operand_ty = sema.typeOf(operand);
+
+ _ = try sema.checkIntType(block, ty_src, dest_ty);
+ try sema.checkFloatType(block, operand_src, operand_ty);
+
+ if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| {
+ const target = sema.mod.getTarget();
+ const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) {
+ error.FloatCannotFit => {
+ return sema.fail(block, operand_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty });
+ },
+ else => |e| return e,
+ };
+ return sema.addConstant(dest_ty, result_val);
+ }
+
+ try sema.requireRuntimeBlock(block, operand_src);
+ return block.addTyOp(.float_to_int, dest_ty, operand);
}
fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref {
@@ -12434,7 +12454,13 @@ fn coerceNum(
if (val.floatHasFraction()) {
return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val, dest_ty });
}
- return sema.fail(block, inst_src, "TODO float to int", .{});
+ const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) {
+ error.FloatCannotFit => {
+ return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty });
+ },
+ else => |e| return e,
+ };
+ return try sema.addConstant(dest_ty, result_val);
},
.Int, .ComptimeInt => {
if (!val.intFitsInType(dest_ty, target)) {
src/value.zig
@@ -1929,6 +1929,52 @@ pub const Value = extern union {
}
}
+ pub fn floatToInt(val: Value, arena: *Allocator, dest_ty: Type, target: Target) error{ FloatCannotFit, OutOfMemory }!Value {
+ const Limb = std.math.big.Limb;
+
+ var value = val.toFloat(f64); // TODO: f128 ?
+ if (std.math.isNan(value) or std.math.isInf(value)) {
+ return error.FloatCannotFit;
+ }
+
+ const isNegative = std.math.signbit(value);
+ value = std.math.fabs(value);
+
+ const floored = std.math.floor(value);
+
+ var rational = try std.math.big.Rational.init(arena);
+ defer rational.deinit();
+ rational.setFloat(f64, floored) catch |err| switch (err) {
+ error.NonFiniteFloat => unreachable,
+ error.OutOfMemory => return error.OutOfMemory,
+ };
+
+ // The float is reduced in rational.setFloat, so we assert that denominator is equal to one
+ const bigOne = std.math.big.int.Const{ .limbs = &.{1}, .positive = true };
+ assert(rational.q.toConst().eqAbs(bigOne));
+
+ const result_limbs = try arena.dupe(Limb, rational.p.toConst().limbs);
+ const result = if (isNegative)
+ try Value.Tag.int_big_negative.create(arena, result_limbs)
+ else
+ try Value.Tag.int_big_positive.create(arena, result_limbs);
+
+ if (result.intFitsInType(dest_ty, target)) {
+ return result;
+ } else {
+ return error.FloatCannotFit;
+ }
+ }
+
+ fn calcLimbLenFloat(scalar: anytype) usize {
+ if (scalar == 0) {
+ return 1;
+ }
+
+ const w_value = std.math.fabs(scalar);
+ return @divFloor(@floatToInt(std.math.big.Limb, std.math.log2(w_value)), @typeInfo(std.math.big.Limb).Int.bits) + 1;
+ }
+
/// Supports both floats and ints; handles undefined.
pub fn numberAddWrap(
lhs: Value,
test/behavior/cast.zig
@@ -107,6 +107,28 @@ test "comptime_int @intToFloat" {
}
}
+test "@floatToInt" {
+ try testFloatToInts();
+ comptime try testFloatToInts();
+}
+
+fn testFloatToInts() !void {
+ const x = @as(i32, 1e4);
+ try expect(x == 10000);
+ const y = @floatToInt(i32, @as(f32, 1e4));
+ try expect(y == 10000);
+ try expectFloatToInt(f16, 255.1, u8, 255);
+ try expectFloatToInt(f16, 127.2, i8, 127);
+ try expectFloatToInt(f16, -128.2, i8, -128);
+ try expectFloatToInt(f32, 255.1, u8, 255);
+ try expectFloatToInt(f32, 127.2, i8, 127);
+ try expectFloatToInt(f32, -128.2, i8, -128);
+}
+
+fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
+ try expect(@floatToInt(I, f) == i);
+}
+
test "implicit cast from [*]T to ?*c_void" {
var a = [_]u8{ 3, 2, 1 };
var runtime_zero: usize = 0;
test/behavior/cast_stage1.zig
@@ -191,29 +191,6 @@ fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 {
};
}
-test "@floatToInt" {
- try testFloatToInts();
- comptime try testFloatToInts();
-}
-
-fn testFloatToInts() !void {
- const x = @as(i32, 1e4);
- try expect(x == 10000);
- const y = @floatToInt(i32, @as(f32, 1e4));
- try expect(y == 10000);
- try expectFloatToInt(f16, 255.1, u8, 255);
- try expectFloatToInt(f16, 127.2, i8, 127);
- try expectFloatToInt(f16, -128.2, i8, -128);
- try expectFloatToInt(f32, 255.1, u8, 255);
- try expectFloatToInt(f32, 127.2, i8, 127);
- try expectFloatToInt(f32, -128.2, i8, -128);
- try expectFloatToInt(comptime_int, 1234, i16, 1234);
-}
-
-fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
- try expect(@floatToInt(I, f) == i);
-}
-
test "cast u128 to f128 and back" {
comptime try testCast128();
try testCast128();
@@ -664,6 +641,13 @@ test "comptime float casts" {
const b = @floatToInt(comptime_int, 2);
try expect(b == 2);
try expect(@TypeOf(b) == comptime_int);
+
+ try expectFloatToInt(comptime_int, 1234, i16, 1234);
+ try expectFloatToInt(comptime_float, 12.3, comptime_int, 12);
+}
+
+fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void {
+ try expect(@floatToInt(I, f) == i);
}
test "cast from ?[*]T to ??[*]T" {