mirror of
https://github.com/ziglang/zig.git
synced 2026-02-12 20:37:54 +00:00
stage2: Implement Sema.floatToInt (#10097)
This commit is contained in:
parent
c77698d69e
commit
0d7359ca9b
34
src/Sema.zig
34
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)) {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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" {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user