2022-06-16 07:09:56 +01:00
|
|
|
const math = @import("std").math;
|
|
|
|
const common = @import("./common.zig");
|
|
|
|
const trunc_f80 = @import("./truncf.zig").trunc_f80;
|
|
|
|
|
|
|
|
pub const panic = common.panic;
|
|
|
|
|
|
|
|
comptime {
|
2022-12-28 13:57:17 +00:00
|
|
|
@export(__trunctfxf2, .{ .name = "__trunctfxf2", .linkage = common.linkage, .visibility = common.visibility });
|
2022-06-16 07:09:56 +01:00
|
|
|
}
|
|
|
|
|
2022-06-16 23:14:12 +01:00
|
|
|
pub fn __trunctfxf2(a: f128) callconv(.C) f80 {
|
2022-06-16 07:09:56 +01:00
|
|
|
const src_sig_bits = math.floatMantissaBits(f128);
|
|
|
|
const dst_sig_bits = math.floatMantissaBits(f80) - 1; // -1 for the integer bit
|
|
|
|
|
|
|
|
// Various constants whose values follow from the type parameters.
|
|
|
|
// Any reasonable optimizer will fold and propagate all of these.
|
|
|
|
const src_bits = @typeInfo(f128).Float.bits;
|
|
|
|
const src_exp_bits = src_bits - src_sig_bits - 1;
|
|
|
|
const src_inf_exp = 0x7FFF;
|
|
|
|
|
|
|
|
const src_inf = src_inf_exp << src_sig_bits;
|
|
|
|
const src_sign_mask = 1 << (src_sig_bits + src_exp_bits);
|
|
|
|
const src_abs_mask = src_sign_mask - 1;
|
|
|
|
const round_mask = (1 << (src_sig_bits - dst_sig_bits)) - 1;
|
|
|
|
const halfway = 1 << (src_sig_bits - dst_sig_bits - 1);
|
|
|
|
|
|
|
|
// Break a into a sign and representation of the absolute value
|
2023-06-22 18:46:56 +01:00
|
|
|
const a_rep = @as(u128, @bitCast(a));
|
2022-06-16 07:09:56 +01:00
|
|
|
const a_abs = a_rep & src_abs_mask;
|
|
|
|
const sign: u16 = if (a_rep & src_sign_mask != 0) 0x8000 else 0;
|
|
|
|
const integer_bit = 1 << 63;
|
|
|
|
|
|
|
|
var res: math.F80 = undefined;
|
|
|
|
|
|
|
|
if (a_abs > src_inf) {
|
|
|
|
// a is NaN.
|
|
|
|
// Conjure the result by beginning with infinity, setting the qNaN
|
|
|
|
// bit and inserting the (truncated) trailing NaN field.
|
|
|
|
res.exp = 0x7fff;
|
|
|
|
res.fraction = 0x8000000000000000;
|
2023-06-22 18:46:56 +01:00
|
|
|
res.fraction |= @as(u64, @truncate(a_abs >> (src_sig_bits - dst_sig_bits)));
|
2022-06-16 07:09:56 +01:00
|
|
|
} else {
|
|
|
|
// The exponent of a is within the range of normal numbers in the
|
|
|
|
// destination format. We can convert by simply right-shifting with
|
|
|
|
// rounding, adding the explicit integer bit, and adjusting the exponent
|
2023-06-22 18:46:56 +01:00
|
|
|
res.fraction = @as(u64, @truncate(a_abs >> (src_sig_bits - dst_sig_bits))) | integer_bit;
|
2023-07-24 11:34:16 +01:00
|
|
|
res.exp = @truncate(a_abs >> src_sig_bits);
|
2022-06-16 07:09:56 +01:00
|
|
|
|
|
|
|
const round_bits = a_abs & round_mask;
|
|
|
|
if (round_bits > halfway) {
|
|
|
|
// Round to nearest
|
2022-12-21 14:40:30 +00:00
|
|
|
const ov = @addWithOverflow(res.fraction, 1);
|
|
|
|
res.fraction = ov[0];
|
|
|
|
res.exp += ov[1];
|
|
|
|
res.fraction |= @as(u64, ov[1]) << 63; // Restore integer bit after carry
|
2022-06-16 07:09:56 +01:00
|
|
|
} else if (round_bits == halfway) {
|
|
|
|
// Ties to even
|
2022-12-21 14:40:30 +00:00
|
|
|
const ov = @addWithOverflow(res.fraction, res.fraction & 1);
|
|
|
|
res.fraction = ov[0];
|
|
|
|
res.exp += ov[1];
|
|
|
|
res.fraction |= @as(u64, ov[1]) << 63; // Restore integer bit after carry
|
2022-06-16 07:09:56 +01:00
|
|
|
}
|
|
|
|
if (res.exp == 0) res.fraction &= ~@as(u64, integer_bit); // Remove integer bit for de-normals
|
|
|
|
}
|
|
|
|
|
|
|
|
res.exp |= sign;
|
|
|
|
return math.make_f80(res);
|
|
|
|
}
|