diff options
| -rw-r--r-- | src/Sema.zig | 34 | ||||
| -rw-r--r-- | src/value.zig | 46 | ||||
| -rw-r--r-- | test/behavior/cast.zig | 22 | ||||
| -rw-r--r-- | test/behavior/cast_stage1.zig | 30 |
4 files changed, 105 insertions, 27 deletions
diff --git a/src/Sema.zig b/src/Sema.zig index 300d319167..ac1bd8b421 100644 --- a/src/Sema.zig +++ b/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)) { diff --git a/src/value.zig b/src/value.zig index 2e3f1bc4b8..4b571891f4 100644 --- a/src/value.zig +++ b/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, diff --git a/test/behavior/cast.zig b/test/behavior/cast.zig index 040ddafd78..83230c64d0 100644 --- a/test/behavior/cast.zig +++ b/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; diff --git a/test/behavior/cast_stage1.zig b/test/behavior/cast_stage1.zig index f6bf975011..157a830f3c 100644 --- a/test/behavior/cast_stage1.zig +++ b/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" { |
