120 lines
4.1 KiB
Zig
120 lines
4.1 KiB
Zig
const builtin = @import("builtin");
|
|
const std = @import("std");
|
|
const maxInt = std.math.maxInt;
|
|
|
|
fn floatsiXf(comptime T: type, a: i32) T {
|
|
@setRuntimeSafety(builtin.is_test);
|
|
|
|
const Z = std.meta.IntType(false, T.bit_count);
|
|
const S = std.meta.IntType(false, T.bit_count - @clz(Z, @as(Z, T.bit_count) - 1));
|
|
|
|
if (a == 0) {
|
|
return @as(T, 0.0);
|
|
}
|
|
|
|
const significandBits = std.math.floatMantissaBits(T);
|
|
const exponentBits = std.math.floatExponentBits(T);
|
|
const exponentBias = ((1 << exponentBits - 1) - 1);
|
|
|
|
const implicitBit = @as(Z, 1) << significandBits;
|
|
const signBit = @as(Z, 1 << Z.bit_count - 1);
|
|
|
|
const sign = a >> 31;
|
|
// Take absolute value of a via abs(x) = (x^(x >> 31)) - (x >> 31).
|
|
const abs_a = (a ^ sign) -% sign;
|
|
// The exponent is the width of abs(a)
|
|
const exp = @as(Z, 31 - @clz(i32, abs_a));
|
|
|
|
const sign_bit = if (sign < 0) signBit else 0;
|
|
|
|
var mantissa: Z = undefined;
|
|
// Shift a into the significand field and clear the implicit bit.
|
|
if (exp <= significandBits) {
|
|
// No rounding needed
|
|
const shift = @intCast(S, significandBits - exp);
|
|
mantissa = @intCast(Z, @bitCast(u32, abs_a)) << shift ^ implicitBit;
|
|
} else {
|
|
const shift = @intCast(S, exp - significandBits);
|
|
// Round to the nearest number after truncation
|
|
mantissa = @intCast(Z, @bitCast(u32, abs_a)) >> shift ^ implicitBit;
|
|
// Align to the left and check if the truncated part is halfway over
|
|
const round = @bitCast(u32, abs_a) << @intCast(u5, 31 - shift);
|
|
mantissa += @boolToInt(round > 0x80000000);
|
|
// Tie to even
|
|
mantissa += mantissa & 1;
|
|
}
|
|
|
|
// Use the addition instead of a or since we may have a carry from the
|
|
// mantissa to the exponent
|
|
var result = mantissa;
|
|
result += (exp + exponentBias) << significandBits;
|
|
result += sign_bit;
|
|
|
|
return @bitCast(T, result);
|
|
}
|
|
|
|
pub fn __floatsisf(arg: i32) callconv(.C) f32 {
|
|
@setRuntimeSafety(builtin.is_test);
|
|
return @call(.{ .modifier = .always_inline }, floatsiXf, .{ f32, arg });
|
|
}
|
|
|
|
pub fn __floatsidf(arg: i32) callconv(.C) f64 {
|
|
@setRuntimeSafety(builtin.is_test);
|
|
return @call(.{ .modifier = .always_inline }, floatsiXf, .{ f64, arg });
|
|
}
|
|
|
|
pub fn __floatsitf(arg: i32) callconv(.C) f128 {
|
|
@setRuntimeSafety(builtin.is_test);
|
|
return @call(.{ .modifier = .always_inline }, floatsiXf, .{ f128, arg });
|
|
}
|
|
|
|
pub fn __aeabi_i2d(arg: i32) callconv(.AAPCS) f64 {
|
|
@setRuntimeSafety(false);
|
|
return @call(.{ .modifier = .always_inline }, __floatsidf, .{arg});
|
|
}
|
|
|
|
pub fn __aeabi_i2f(arg: i32) callconv(.AAPCS) f32 {
|
|
@setRuntimeSafety(false);
|
|
return @call(.{ .modifier = .always_inline }, __floatsisf, .{arg});
|
|
}
|
|
|
|
fn test_one_floatsitf(a: i32, expected: u128) void {
|
|
const r = __floatsitf(a);
|
|
std.testing.expect(@bitCast(u128, r) == expected);
|
|
}
|
|
|
|
fn test_one_floatsidf(a: i32, expected: u64) void {
|
|
const r = __floatsidf(a);
|
|
std.testing.expect(@bitCast(u64, r) == expected);
|
|
}
|
|
|
|
fn test_one_floatsisf(a: i32, expected: u32) void {
|
|
const r = __floatsisf(a);
|
|
std.testing.expect(@bitCast(u32, r) == expected);
|
|
}
|
|
|
|
test "floatsidf" {
|
|
test_one_floatsidf(0, 0x0000000000000000);
|
|
test_one_floatsidf(1, 0x3ff0000000000000);
|
|
test_one_floatsidf(-1, 0xbff0000000000000);
|
|
test_one_floatsidf(0x7FFFFFFF, 0x41dfffffffc00000);
|
|
test_one_floatsidf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xc1e0000000000000);
|
|
}
|
|
|
|
test "floatsisf" {
|
|
test_one_floatsisf(0, 0x00000000);
|
|
test_one_floatsisf(1, 0x3f800000);
|
|
test_one_floatsisf(-1, 0xbf800000);
|
|
test_one_floatsisf(0x7FFFFFFF, 0x4f000000);
|
|
test_one_floatsisf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xcf000000);
|
|
}
|
|
|
|
test "floatsitf" {
|
|
test_one_floatsitf(0, 0);
|
|
test_one_floatsitf(0x7FFFFFFF, 0x401dfffffffc00000000000000000000);
|
|
test_one_floatsitf(0x12345678, 0x401b2345678000000000000000000000);
|
|
test_one_floatsitf(-0x12345678, 0xc01b2345678000000000000000000000);
|
|
test_one_floatsitf(@bitCast(i32, @intCast(u32, 0xffffffff)), 0xbfff0000000000000000000000000000);
|
|
test_one_floatsitf(@bitCast(i32, @intCast(u32, 0x80000000)), 0xc01e0000000000000000000000000000);
|
|
}
|