master
  1const std = @import("std");
  2const Int = std.meta.Int;
  3const math = std.math;
  4
  5pub 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
 57const endian = @import("builtin").cpu.arch.endian();
 58inline 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
 65pub 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            @Int(signedness, 32 * limbs_len),
 70            @bitCast(x[0..limbs_len].*),
 71        )),
 72        else => {},
 73    }
 74
 75    // sign implicit fraction round sticky
 76    const I = comptime @Int(
 77        signedness,
 78        @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
105test {
106    _ = @import("float_from_int_test.zig");
107}