| author | |
| committer | |
| log | 0d7359ca9b7e1c88d62ce3ecc6542584fe5df489 |
| tree | 259335fd862ce44db56c716e5de3080e26cbe80a |
| parent | c77698d69ea5c3e09b1b9404f634e512627ad321 |
| signature |
4 files changed, 105 insertions(+), 27 deletions(-)
src/Sema.zig+30-4| ... | @@ -9642,9 +9642,29 @@ fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A | ... | @@ -9642,9 +9642,29 @@ fn zirFrameSize(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!A |
| 9642 | 9642 | ||
| 9643 | fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 9643 | fn zirFloatToInt(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| 9644 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; | 9644 | const inst_data = sema.code.instructions.items(.data)[inst].pl_node; |
| 9645 | const src = inst_data.src(); | 9645 | const extra = sema.code.extraData(Zir.Inst.Bin, inst_data.payload_index).data; |
| 9646 | // TODO don't forget the safety check! | 9646 | const ty_src: LazySrcLoc = .{ .node_offset_builtin_call_arg0 = inst_data.src_node }; |
| 9647 | return sema.fail(block, src, "TODO: Sema.zirFloatToInt", .{}); | 9647 | const operand_src: LazySrcLoc = .{ .node_offset_builtin_call_arg1 = inst_data.src_node }; |
| 9648 | const dest_ty = try sema.resolveType(block, ty_src, extra.lhs); | ||
| 9649 | const operand = sema.resolveInst(extra.rhs); | ||
| 9650 | const operand_ty = sema.typeOf(operand); | ||
| 9651 | |||
| 9652 | _ = try sema.checkIntType(block, ty_src, dest_ty); | ||
| 9653 | try sema.checkFloatType(block, operand_src, operand_ty); | ||
| 9654 | |||
| 9655 | if (try sema.resolveMaybeUndefVal(block, operand_src, operand)) |val| { | ||
| 9656 | const target = sema.mod.getTarget(); | ||
| 9657 | const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) { | ||
| 9658 | error.FloatCannotFit => { | ||
| 9659 | return sema.fail(block, operand_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty }); | ||
| 9660 | }, | ||
| 9661 | else => |e| return e, | ||
| 9662 | }; | ||
| 9663 | return sema.addConstant(dest_ty, result_val); | ||
| 9664 | } | ||
| 9665 | |||
| 9666 | try sema.requireRuntimeBlock(block, operand_src); | ||
| 9667 | return block.addTyOp(.float_to_int, dest_ty, operand); | ||
| 9648 | } | 9668 | } |
| 9649 | 9669 | ||
| 9650 | fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { | 9670 | fn zirIntToFloat(sema: *Sema, block: *Block, inst: Zir.Inst.Index) CompileError!Air.Inst.Ref { |
| ... | @@ -12434,7 +12454,13 @@ fn coerceNum( | ... | @@ -12434,7 +12454,13 @@ fn coerceNum( |
| 12434 | if (val.floatHasFraction()) { | 12454 | if (val.floatHasFraction()) { |
| 12435 | return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val, dest_ty }); | 12455 | return sema.fail(block, inst_src, "fractional component prevents float value {} from coercion to type '{}'", .{ val, dest_ty }); |
| 12436 | } | 12456 | } |
| 12437 | return sema.fail(block, inst_src, "TODO float to int", .{}); | 12457 | const result_val = val.floatToInt(sema.arena, dest_ty, target) catch |err| switch (err) { |
| 12458 | error.FloatCannotFit => { | ||
| 12459 | return sema.fail(block, inst_src, "integer value {d} cannot be stored in type '{}'", .{ std.math.floor(val.toFloat(f64)), dest_ty }); | ||
| 12460 | }, | ||
| 12461 | else => |e| return e, | ||
| 12462 | }; | ||
| 12463 | return try sema.addConstant(dest_ty, result_val); | ||
| 12438 | }, | 12464 | }, |
| 12439 | .Int, .ComptimeInt => { | 12465 | .Int, .ComptimeInt => { |
| 12440 | if (!val.intFitsInType(dest_ty, target)) { | 12466 | if (!val.intFitsInType(dest_ty, target)) { |
src/value.zig+46| ... | @@ -1929,6 +1929,52 @@ pub const Value = extern union { | ... | @@ -1929,6 +1929,52 @@ pub const Value = extern union { |
| 1929 | } | 1929 | } |
| 1930 | } | 1930 | } |
| 1931 | 1931 | ||
| 1932 | pub fn floatToInt(val: Value, arena: *Allocator, dest_ty: Type, target: Target) error{ FloatCannotFit, OutOfMemory }!Value { | ||
| 1933 | const Limb = std.math.big.Limb; | ||
| 1934 | |||
| 1935 | var value = val.toFloat(f64); // TODO: f128 ? | ||
| 1936 | if (std.math.isNan(value) or std.math.isInf(value)) { | ||
| 1937 | return error.FloatCannotFit; | ||
| 1938 | } | ||
| 1939 | |||
| 1940 | const isNegative = std.math.signbit(value); | ||
| 1941 | value = std.math.fabs(value); | ||
| 1942 | |||
| 1943 | const floored = std.math.floor(value); | ||
| 1944 | |||
| 1945 | var rational = try std.math.big.Rational.init(arena); | ||
| 1946 | defer rational.deinit(); | ||
| 1947 | rational.setFloat(f64, floored) catch |err| switch (err) { | ||
| 1948 | error.NonFiniteFloat => unreachable, | ||
| 1949 | error.OutOfMemory => return error.OutOfMemory, | ||
| 1950 | }; | ||
| 1951 | |||
| 1952 | // The float is reduced in rational.setFloat, so we assert that denominator is equal to one | ||
| 1953 | const bigOne = std.math.big.int.Const{ .limbs = &.{1}, .positive = true }; | ||
| 1954 | assert(rational.q.toConst().eqAbs(bigOne)); | ||
| 1955 | |||
| 1956 | const result_limbs = try arena.dupe(Limb, rational.p.toConst().limbs); | ||
| 1957 | const result = if (isNegative) | ||
| 1958 | try Value.Tag.int_big_negative.create(arena, result_limbs) | ||
| 1959 | else | ||
| 1960 | try Value.Tag.int_big_positive.create(arena, result_limbs); | ||
| 1961 | |||
| 1962 | if (result.intFitsInType(dest_ty, target)) { | ||
| 1963 | return result; | ||
| 1964 | } else { | ||
| 1965 | return error.FloatCannotFit; | ||
| 1966 | } | ||
| 1967 | } | ||
| 1968 | |||
| 1969 | fn calcLimbLenFloat(scalar: anytype) usize { | ||
| 1970 | if (scalar == 0) { | ||
| 1971 | return 1; | ||
| 1972 | } | ||
| 1973 | |||
| 1974 | const w_value = std.math.fabs(scalar); | ||
| 1975 | return @divFloor(@floatToInt(std.math.big.Limb, std.math.log2(w_value)), @typeInfo(std.math.big.Limb).Int.bits) + 1; | ||
| 1976 | } | ||
| 1977 | |||
| 1932 | /// Supports both floats and ints; handles undefined. | 1978 | /// Supports both floats and ints; handles undefined. |
| 1933 | pub fn numberAddWrap( | 1979 | pub fn numberAddWrap( |
| 1934 | lhs: Value, | 1980 | lhs: Value, |
test/behavior/cast.zig+22| ... | @@ -107,6 +107,28 @@ test "comptime_int @intToFloat" { | ... | @@ -107,6 +107,28 @@ test "comptime_int @intToFloat" { |
| 107 | } | 107 | } |
| 108 | } | 108 | } |
| 109 | 109 | ||
| 110 | test "@floatToInt" { | ||
| 111 | try testFloatToInts(); | ||
| 112 | comptime try testFloatToInts(); | ||
| 113 | } | ||
| 114 | |||
| 115 | fn testFloatToInts() !void { | ||
| 116 | const x = @as(i32, 1e4); | ||
| 117 | try expect(x == 10000); | ||
| 118 | const y = @floatToInt(i32, @as(f32, 1e4)); | ||
| 119 | try expect(y == 10000); | ||
| 120 | try expectFloatToInt(f16, 255.1, u8, 255); | ||
| 121 | try expectFloatToInt(f16, 127.2, i8, 127); | ||
| 122 | try expectFloatToInt(f16, -128.2, i8, -128); | ||
| 123 | try expectFloatToInt(f32, 255.1, u8, 255); | ||
| 124 | try expectFloatToInt(f32, 127.2, i8, 127); | ||
| 125 | try expectFloatToInt(f32, -128.2, i8, -128); | ||
| 126 | } | ||
| 127 | |||
| 128 | fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { | ||
| 129 | try expect(@floatToInt(I, f) == i); | ||
| 130 | } | ||
| 131 | |||
| 110 | test "implicit cast from [*]T to ?*c_void" { | 132 | test "implicit cast from [*]T to ?*c_void" { |
| 111 | var a = [_]u8{ 3, 2, 1 }; | 133 | var a = [_]u8{ 3, 2, 1 }; |
| 112 | var runtime_zero: usize = 0; | 134 | var runtime_zero: usize = 0; |
test/behavior/cast_stage1.zig+7-23| ... | @@ -191,29 +191,6 @@ fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 { | ... | @@ -191,29 +191,6 @@ fn testPeerErrorAndArray2(x: u8) anyerror![]const u8 { |
| 191 | }; | 191 | }; |
| 192 | } | 192 | } |
| 193 | 193 | ||
| 194 | test "@floatToInt" { | ||
| 195 | try testFloatToInts(); | ||
| 196 | comptime try testFloatToInts(); | ||
| 197 | } | ||
| 198 | |||
| 199 | fn testFloatToInts() !void { | ||
| 200 | const x = @as(i32, 1e4); | ||
| 201 | try expect(x == 10000); | ||
| 202 | const y = @floatToInt(i32, @as(f32, 1e4)); | ||
| 203 | try expect(y == 10000); | ||
| 204 | try expectFloatToInt(f16, 255.1, u8, 255); | ||
| 205 | try expectFloatToInt(f16, 127.2, i8, 127); | ||
| 206 | try expectFloatToInt(f16, -128.2, i8, -128); | ||
| 207 | try expectFloatToInt(f32, 255.1, u8, 255); | ||
| 208 | try expectFloatToInt(f32, 127.2, i8, 127); | ||
| 209 | try expectFloatToInt(f32, -128.2, i8, -128); | ||
| 210 | try expectFloatToInt(comptime_int, 1234, i16, 1234); | ||
| 211 | } | ||
| 212 | |||
| 213 | fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { | ||
| 214 | try expect(@floatToInt(I, f) == i); | ||
| 215 | } | ||
| 216 | |||
| 217 | test "cast u128 to f128 and back" { | 194 | test "cast u128 to f128 and back" { |
| 218 | comptime try testCast128(); | 195 | comptime try testCast128(); |
| 219 | try testCast128(); | 196 | try testCast128(); |
| ... | @@ -664,6 +641,13 @@ test "comptime float casts" { | ... | @@ -664,6 +641,13 @@ test "comptime float casts" { |
| 664 | const b = @floatToInt(comptime_int, 2); | 641 | const b = @floatToInt(comptime_int, 2); |
| 665 | try expect(b == 2); | 642 | try expect(b == 2); |
| 666 | try expect(@TypeOf(b) == comptime_int); | 643 | try expect(@TypeOf(b) == comptime_int); |
| 644 | |||
| 645 | try expectFloatToInt(comptime_int, 1234, i16, 1234); | ||
| 646 | try expectFloatToInt(comptime_float, 12.3, comptime_int, 12); | ||
| 647 | } | ||
| 648 | |||
| 649 | fn expectFloatToInt(comptime F: type, f: F, comptime I: type, i: I) !void { | ||
| 650 | try expect(@floatToInt(I, f) == i); | ||
| 667 | } | 651 | } |
| 668 | 652 | ||
| 669 | test "cast from ?[*]T to ??[*]T" { | 653 | test "cast from ?[*]T to ??[*]T" { |