float_from_int.zig (4111B) - Raw
1 const std = @import("std"); 2 const Int = std.meta.Int; 3 const math = std.math; 4 5 pub fn floatFromInt(comptime T: type, x: anytype) T { 6 if (x == 0) return 0; 7 8 // Various constants whose values follow from the type parameters. 9 // Any reasonable optimizer will fold and propagate all of these. 10 const Z = Int(.unsigned, @bitSizeOf(@TypeOf(x))); 11 const uT = Int(.unsigned, @bitSizeOf(T)); 12 const inf = math.inf(T); 13 const float_bits = @bitSizeOf(T); 14 const int_bits = @bitSizeOf(@TypeOf(x)); 15 const exp_bits = math.floatExponentBits(T); 16 const fractional_bits = math.floatFractionalBits(T); 17 const exp_bias = math.maxInt(Int(.unsigned, exp_bits - 1)); 18 const implicit_bit = if (T != f80) @as(uT, 1) << fractional_bits else 0; 19 const max_exp = exp_bias; 20 21 // Sign 22 const abs_val = @abs(x); 23 const sign_bit = if (x < 0) @as(uT, 1) << (float_bits - 1) else 0; 24 var result: uT = sign_bit; 25 26 // Compute significand 27 const exp = int_bits - @clz(abs_val) - 1; 28 if (int_bits <= fractional_bits or exp <= fractional_bits) { 29 const shift_amt = fractional_bits - @as(math.Log2Int(uT), @intCast(exp)); 30 31 // Shift up result to line up with the significand - no rounding required 32 result = @as(uT, @intCast(abs_val)) << shift_amt; 33 result ^= implicit_bit; // Remove implicit integer bit 34 } else { 35 const shift_amt: math.Log2Int(Z) = @intCast(exp - fractional_bits); 36 const exact_tie: bool = @ctz(abs_val) == shift_amt - 1; 37 38 // Shift down result and remove implicit integer bit 39 result = @as(uT, @intCast((abs_val >> (shift_amt - 1)))) ^ (implicit_bit << 1); 40 41 // Round result, including round-to-even for exact ties 42 result = ((result + 1) >> 1) & ~@as(uT, @intFromBool(exact_tie)); 43 } 44 45 // Compute exponent 46 if ((int_bits > max_exp) and (exp > max_exp)) // If exponent too large, overflow to infinity 47 return @bitCast(sign_bit | @as(uT, @bitCast(inf))); 48 49 result += (@as(uT, exp) + exp_bias) << math.floatMantissaBits(T); 50 51 // If the result included a carry, we need to restore the explicit integer bit 52 if (T == f80) result |= 1 << fractional_bits; 53 54 return @bitCast(sign_bit | result); 55 } 56 57 const endian = @import("builtin").cpu.arch.endian(); 58 inline fn limb(limbs: []const u32, index: usize) u32 { 59 return switch (endian) { 60 .little => limbs[index], 61 .big => limbs[limbs.len - 1 - index], 62 }; 63 } 64 65 pub inline fn floatFromBigInt(comptime T: type, comptime signedness: std.builtin.Signedness, x: []const u32) T { 66 switch (x.len) { 67 0 => return 0, 68 inline 1...4 => |limbs_len| return @floatFromInt(@as( 69 @Type(.{ .int = .{ .signedness = signedness, .bits = 32 * limbs_len } }), 70 @bitCast(x[0..limbs_len].*), 71 )), 72 else => {}, 73 } 74 75 // sign implicit fraction round sticky 76 const I = comptime @Type(.{ .int = .{ 77 .signedness = signedness, 78 .bits = @as(u16, @intFromBool(signedness == .signed)) + 1 + math.floatFractionalBits(T) + 1 + 1, 79 } }); 80 81 const clrsb = clrsb: { 82 var clsb: usize = 0; 83 const sign_bits: u32 = switch (signedness) { 84 .signed => @bitCast(@as(i32, @bitCast(limb(x, x.len - 1))) >> 31), 85 .unsigned => 0, 86 }; 87 for (0..x.len) |limb_index| { 88 const l = limb(x, x.len - 1 - limb_index) ^ sign_bits; 89 clsb += @clz(l); 90 if (l != 0) break; 91 } 92 break :clrsb clsb - @intFromBool(signedness == .signed); 93 }; 94 const active_bits = 32 * x.len - clrsb; 95 const exponent = active_bits -| @bitSizeOf(I); 96 const exponent_limb = exponent / 32; 97 const sticky = for (0..exponent_limb) |limb_index| { 98 if (limb(x, limb_index) != 0) break true; 99 } else limb(x, exponent_limb) & ((@as(u32, 1) << @truncate(exponent)) - 1) != 0; 100 return math.ldexp(@as(T, @floatFromInt( 101 std.mem.readPackedIntNative(I, std.mem.sliceAsBytes(x), exponent) | @intFromBool(sticky), 102 )), @intCast(exponent)); 103 } 104 105 test { 106 _ = @import("float_from_int_test.zig"); 107 }