master
  1const std = @import("std");
  2const Int = std.meta.Int;
  3const math = std.math;
  4const Log2Int = math.Log2Int;
  5
  6pub inline fn intFromFloat(comptime I: type, a: anytype) I {
  7    const F = @TypeOf(a);
  8    const float_bits = @typeInfo(F).float.bits;
  9    const int_bits = @typeInfo(I).int.bits;
 10    const rep_t = Int(.unsigned, float_bits);
 11    const sig_bits = math.floatMantissaBits(F);
 12    const exp_bits = math.floatExponentBits(F);
 13    const fractional_bits = math.floatFractionalBits(F);
 14
 15    const implicit_bit = if (F != f80) (@as(rep_t, 1) << sig_bits) else 0;
 16    const max_exp = (1 << (exp_bits - 1));
 17    const exp_bias = max_exp - 1;
 18    const sig_mask = (@as(rep_t, 1) << sig_bits) - 1;
 19
 20    // Break a into sign, exponent, significand
 21    const a_rep: rep_t = @bitCast(a);
 22    const negative = (a_rep >> (float_bits - 1)) != 0;
 23    const exponent = @as(i32, @intCast((a_rep << 1) >> (sig_bits + 1))) - exp_bias;
 24    const significand: rep_t = (a_rep & sig_mask) | implicit_bit;
 25
 26    // If the exponent is negative, the result rounds to zero.
 27    if (exponent < 0) return 0;
 28
 29    // If the value is too large for the integer type, saturate.
 30    switch (@typeInfo(I).int.signedness) {
 31        .unsigned => {
 32            if (negative) return 0;
 33            if (@as(c_uint, @intCast(exponent)) >= @min(int_bits, max_exp)) return math.maxInt(I);
 34        },
 35        .signed => if (@as(c_uint, @intCast(exponent)) >= @min(int_bits - 1, max_exp)) {
 36            return if (negative) math.minInt(I) else math.maxInt(I);
 37        },
 38    }
 39
 40    // If 0 <= exponent < sig_bits, right shift to get the result.
 41    // Otherwise, shift left.
 42    var result: I = undefined;
 43    if (exponent < fractional_bits) {
 44        result = @intCast(significand >> @intCast(fractional_bits - exponent));
 45    } else {
 46        result = @as(I, @intCast(significand)) << @intCast(exponent - fractional_bits);
 47    }
 48
 49    if ((@typeInfo(I).int.signedness == .signed) and negative)
 50        return ~result +% 1;
 51    return result;
 52}
 53
 54pub inline fn bigIntFromFloat(comptime signedness: std.builtin.Signedness, result: []u32, a: anytype) void {
 55    switch (result.len) {
 56        0 => return,
 57        inline 1...4 => |limbs_len| {
 58            result[0..limbs_len].* = @bitCast(@as(
 59                @Int(signedness, 32 * limbs_len),
 60                @intFromFloat(a),
 61            ));
 62            return;
 63        },
 64        else => {},
 65    }
 66
 67    // sign implicit fraction
 68    const significand_bits = 1 + math.floatFractionalBits(@TypeOf(a));
 69    const I = @Int(signedness, @as(u16, @intFromBool(signedness == .signed)) + significand_bits);
 70
 71    const parts = math.frexp(a);
 72    const significand_bits_adjusted_to_handle_smin = @as(i32, significand_bits) +
 73        @intFromBool(signedness == .signed and parts.exponent == 32 * result.len);
 74    const exponent: usize = @intCast(@max(parts.exponent - significand_bits_adjusted_to_handle_smin, 0));
 75    const int: I = @intFromFloat(switch (exponent) {
 76        0 => a,
 77        else => math.ldexp(parts.significand, significand_bits_adjusted_to_handle_smin),
 78    });
 79    switch (signedness) {
 80        .signed => {
 81            const endian = @import("builtin").cpu.arch.endian();
 82            const exponent_limb = switch (endian) {
 83                .little => exponent / 32,
 84                .big => result.len - 1 - exponent / 32,
 85            };
 86            const sign_bits: u32 = if (int < 0) math.maxInt(u32) else 0;
 87            @memset(result[0..exponent_limb], switch (endian) {
 88                .little => 0,
 89                .big => sign_bits,
 90            });
 91            result[exponent_limb] = sign_bits << @truncate(exponent);
 92            @memset(result[exponent_limb + 1 ..], switch (endian) {
 93                .little => sign_bits,
 94                .big => 0,
 95            });
 96        },
 97        .unsigned => @memset(result, 0),
 98    }
 99    std.mem.writePackedIntNative(I, std.mem.sliceAsBytes(result), exponent, int);
100}
101
102test {
103    _ = @import("int_from_float_test.zig");
104}