int_from_float.zig (4079B) - Raw
1 const std = @import("std"); 2 const Int = std.meta.Int; 3 const math = std.math; 4 const Log2Int = math.Log2Int; 5 6 pub 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 54 pub 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 @Type(.{ .int = .{ .signedness = signedness, .bits = 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 = @Type(comptime .{ .int = .{ 70 .signedness = signedness, 71 .bits = @as(u16, @intFromBool(signedness == .signed)) + significand_bits, 72 } }); 73 74 const parts = math.frexp(a); 75 const significand_bits_adjusted_to_handle_smin = @as(i32, significand_bits) + 76 @intFromBool(signedness == .signed and parts.exponent == 32 * result.len); 77 const exponent = @max(parts.exponent - significand_bits_adjusted_to_handle_smin, 0); 78 const int: I = @intFromFloat(switch (exponent) { 79 0 => a, 80 else => math.ldexp(parts.significand, significand_bits_adjusted_to_handle_smin), 81 }); 82 switch (signedness) { 83 .signed => { 84 const endian = @import("builtin").cpu.arch.endian(); 85 const exponent_limb = switch (endian) { 86 .little => exponent / 32, 87 .big => result.len - 1 - exponent / 32, 88 }; 89 const sign_bits: u32 = if (int < 0) math.maxInt(u32) else 0; 90 @memset(result[0..exponent_limb], switch (endian) { 91 .little => 0, 92 .big => sign_bits, 93 }); 94 result[exponent_limb] = sign_bits << @truncate(exponent); 95 @memset(result[exponent_limb + 1 ..], switch (endian) { 96 .little => sign_bits, 97 .big => 0, 98 }); 99 }, 100 .unsigned => @memset(result, 0), 101 } 102 std.mem.writePackedIntNative(I, std.mem.sliceAsBytes(result), exponent, int); 103 } 104 105 test { 106 _ = @import("int_from_float_test.zig"); 107 }