diff options
author | Richard Henderson | 2020-11-22 01:35:54 +0100 |
---|---|---|
committer | Richard Henderson | 2021-06-03 23:09:02 +0200 |
commit | 98b3cff7537ad2a9ce0faa6ad0af4191bd066916 (patch) | |
tree | 1268113bf435285c52033e27e75224f3ba155ce4 | |
parent | softfloat: Introduce Floatx80RoundPrec (diff) | |
download | qemu-98b3cff7537ad2a9ce0faa6ad0af4191bd066916.tar.gz qemu-98b3cff7537ad2a9ce0faa6ad0af4191bd066916.tar.xz qemu-98b3cff7537ad2a9ce0faa6ad0af4191bd066916.zip |
softfloat: Adjust parts_uncanon_normal for floatx80
With floatx80_precision_x, the rounding happens across
the break between words. Notice this case with
frac_lsb = round_mask + 1 -> 0
and check the bits in frac_hi as needed.
In addition, since frac_shift == 0, we won't implicitly clear
round_mask via the right-shift, so explicitly clear those bits.
This fixes rounding for floatx80_precision_[sd].
Reviewed-by: Alex Bennée <alex.bennee@linaro.org>
Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
-rw-r--r-- | fpu/softfloat-parts.c.inc | 36 |
1 files changed, 30 insertions, 6 deletions
diff --git a/fpu/softfloat-parts.c.inc b/fpu/softfloat-parts.c.inc index a026581c33..efb81bbebe 100644 --- a/fpu/softfloat-parts.c.inc +++ b/fpu/softfloat-parts.c.inc @@ -155,7 +155,13 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, switch (s->float_rounding_mode) { case float_round_nearest_even: - inc = ((p->frac_lo & roundeven_mask) != frac_lsbm1 ? frac_lsbm1 : 0); + if (N > 64 && frac_lsb == 0) { + inc = ((p->frac_hi & 1) || (p->frac_lo & round_mask) != frac_lsbm1 + ? frac_lsbm1 : 0); + } else { + inc = ((p->frac_lo & roundeven_mask) != frac_lsbm1 + ? frac_lsbm1 : 0); + } break; case float_round_ties_away: inc = frac_lsbm1; @@ -176,7 +182,11 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, overflow_norm = true; /* fall through */ case float_round_to_odd_inf: - inc = p->frac_lo & frac_lsb ? 0 : round_mask; + if (N > 64 && frac_lsb == 0) { + inc = p->frac_hi & 1 ? 0 : round_mask; + } else { + inc = p->frac_lo & frac_lsb ? 0 : round_mask; + } break; default: g_assert_not_reached(); @@ -191,8 +201,8 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, p->frac_hi |= DECOMPOSED_IMPLICIT_BIT; exp++; } + p->frac_lo &= ~round_mask; } - frac_shr(p, frac_shift); if (fmt->arm_althp) { /* ARM Alt HP eschews Inf and NaN for a wider exponent. */ @@ -201,18 +211,21 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, flags = float_flag_invalid; exp = exp_max; frac_allones(p); + p->frac_lo &= ~round_mask; } } else if (unlikely(exp >= exp_max)) { flags |= float_flag_overflow | float_flag_inexact; if (overflow_norm) { exp = exp_max - 1; frac_allones(p); + p->frac_lo &= ~round_mask; } else { p->cls = float_class_inf; exp = exp_max; frac_clear(p); } } + frac_shr(p, frac_shift); } else if (s->flush_to_zero) { flags |= float_flag_output_denormal; p->cls = float_class_zero; @@ -232,18 +245,29 @@ static void partsN(uncanon_normal)(FloatPartsN *p, float_status *s, /* Need to recompute round-to-even/round-to-odd. */ switch (s->float_rounding_mode) { case float_round_nearest_even: - inc = ((p->frac_lo & roundeven_mask) != frac_lsbm1 - ? frac_lsbm1 : 0); + if (N > 64 && frac_lsb == 0) { + inc = ((p->frac_hi & 1) || + (p->frac_lo & round_mask) != frac_lsbm1 + ? frac_lsbm1 : 0); + } else { + inc = ((p->frac_lo & roundeven_mask) != frac_lsbm1 + ? frac_lsbm1 : 0); + } break; case float_round_to_odd: case float_round_to_odd_inf: - inc = p->frac_lo & frac_lsb ? 0 : round_mask; + if (N > 64 && frac_lsb == 0) { + inc = p->frac_hi & 1 ? 0 : round_mask; + } else { + inc = p->frac_lo & frac_lsb ? 0 : round_mask; + } break; default: break; } flags |= float_flag_inexact; frac_addi(p, p, inc); + p->frac_lo &= ~round_mask; } exp = (p->frac_hi & DECOMPOSED_IMPLICIT_BIT) != 0; |