zig

fork of https://codeberg.org/ziglang/zig
Log | Files | Refs | README | LICENSE

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 }