zig

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

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 }