diff --git a/lib/compiler_rt/truncf.zig b/lib/compiler_rt/truncf.zig index cd8e8cc2bd5088d71b9471d8112bf4266ec6d02a..d2b82e6bba5a743269f5c5bf219fd6aeaaf719eb 100644 --- a/lib/compiler_rt/truncf.zig +++ b/lib/compiler_rt/truncf.zig @@ -168,7 +168,7 @@ pub fn f80_floatCast_f128(a: f128) f80 { // Conjure the result by beginning with infinity, setting the qNaN // bit and inserting the (truncated) trailing NaN field. res.exp = 0x7fff; - res.fraction = 0x8000000000000000; + res.fraction = integer_bit | (integer_bit >> 1); res.fraction |= @as(u64, @truncate(a_abs >> (src_sig_bits - dst_sig_bits))); } else { // The exponent of a is within the range of normal numbers in the @@ -322,6 +322,7 @@ inline fn trunc_f80(comptime dst_t: type, a: f80) dst_t { var a_rep = std.math.F80.fromFloat(a); const sign = a_rep.exp & 0x8000; a_rep.exp &= 0x7FFF; + const significand = a_rep.fraction; a_rep.fraction &= 0x7FFFFFFFFFFFFFFF; var abs_result: dst_rep_t = undefined; @@ -355,14 +356,14 @@ inline fn trunc_f80(comptime dst_t: type, a: f80) dst_t { // a underflows on conversion to the destination type or is an exact // zero. The result may be a denormal or zero. Extract the exponent // to get the shift amount for the denormalization. - const shift = src_exp_bias - dst_exp_bias - a_rep.exp; + const shift = src_exp_bias - dst_exp_bias - a_rep.exp + 1; // Right shift by the denormalization amount with sticky. if (shift > src_sig_bits) { abs_result = 0; } else { - const sticky = @intFromBool(a_rep.fraction << @intCast(shift) != 0); - const denormalized_significand = (a_rep.fraction >> @intCast(shift)) | sticky; + const sticky = @intFromBool(significand << @intCast(src_sig_bits + 1 - shift) != 0); + const denormalized_significand = (significand >> @intCast(shift)) | sticky; abs_result = @intCast(denormalized_significand >> (src_sig_bits - dst_sig_bits)); const round_bits = denormalized_significand & round_mask; if (round_bits > halfway) { diff --git a/lib/compiler_rt/truncf_test.zig b/lib/compiler_rt/truncf_test.zig index 6595c47b52d8f868d019c1728ae3d57735e12323..78d1caa99c024c211d46b57518266119c2911fc4 100644 --- a/lib/compiler_rt/truncf_test.zig +++ b/lib/compiler_rt/truncf_test.zig @@ -135,6 +135,35 @@ test f16_floatCast_f64 { try test_f16_floatCast_f64(65536.0, 0x7c00); } +fn test_f16_floatCast_f80(a: f80, expected: u16) !void { + const actual: u16 = @bitCast(f16_floatCast_f80(a)); + try testing.expectEqual(expected, actual); +} + +fn test_f32_floatCast_f80(a: f80, expected: u32) !void { + const actual: u32 = @bitCast(f32_floatCast_f80(a)); + try testing.expectEqual(expected, actual); +} + +fn test_f64_floatCast_f80(a: f80, expected: u64) !void { + const actual: u64 = @bitCast(f64_floatCast_f80(a)); + try testing.expectEqual(expected, actual); +} + +test "f80 conversions preserve destination subnormals" { + try test_f16_floatCast_f80(0x1p-24, 0x0001); + try test_f16_floatCast_f80(0x1.8p-24, 0x0002); + try test_f16_floatCast_f80(0x1.8p-20, 0x0018); + try test_f16_floatCast_f80(0x1p-17, 0x0080); + try test_f16_floatCast_f80(0x1.fep-15, 0x03fc); + + try test_f32_floatCast_f80(0x1p-149, 0x00000001); + try test_f32_floatCast_f80(0x1p-147, 0x00000004); + + try test_f64_floatCast_f80(0x1p-1074, 0x0000000000000001); + try test_f64_floatCast_f80(0x1.8p-1073, 0x0000000000000003); +} + fn test_f32_floatCast_f128(a: f128, expected: u32) !void { const x = f32_floatCast_f128(a); @@ -284,9 +313,17 @@ fn test_f80_floatCast_f128(a: f128, expected: f80) !void { try testing.expect(x == expected); } +fn test_f80_floatCast_f128_bits(a: u128, expected_exp: u16, expected_fraction: u64) !void { + const actual = std.math.F80.fromFloat(f80_floatCast_f128(@bitCast(a))); + try testing.expectEqual(expected_exp, actual.exp); + try testing.expectEqual(expected_fraction, actual.fraction); +} + test f80_floatCast_f128 { try test_f80_floatCast_f128(1.5, 1.5); try test_f80_floatCast_f128(2.5, 2.5); try test_f80_floatCast_f128(-2.5, -2.5); try test_f80_floatCast_f128(0.0, 0.0); + + try test_f80_floatCast_f128_bits(0x7fff0000000000000000000000000001, 0x7fff, 0xc000000000000000); }