softfloat: Fix float64_to_uint64
The comment preceding the float64_to_uint64 routine suggests that the implementation is broken. And this is, indeed, the case. This patch properly implements the conversion of a 64-bit floating point number to an unsigned, 64 bit integer. This contribution can be licensed under either the softfloat-2a or -2b license. Signed-off-by: Tom Musta <tommusta@gmail.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <rth@twiddle.net>
This commit is contained in:
parent
c4850f9e1b
commit
fb3ea83aa5
101
fpu/softfloat.c
101
fpu/softfloat.c
@ -203,6 +203,56 @@ static int64 roundAndPackInt64( flag zSign, uint64_t absZ0, uint64_t absZ1 STATU
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*----------------------------------------------------------------------------
|
||||||
|
| Takes the 128-bit fixed-point value formed by concatenating `absZ0' and
|
||||||
|
| `absZ1', with binary point between bits 63 and 64 (between the input words),
|
||||||
|
| and returns the properly rounded 64-bit unsigned integer corresponding to the
|
||||||
|
| input. Ordinarily, the fixed-point input is simply rounded to an integer,
|
||||||
|
| with the inexact exception raised if the input cannot be represented exactly
|
||||||
|
| as an integer. However, if the fixed-point input is too large, the invalid
|
||||||
|
| exception is raised and the largest unsigned integer is returned.
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
static int64 roundAndPackUint64(flag zSign, uint64_t absZ0,
|
||||||
|
uint64_t absZ1 STATUS_PARAM)
|
||||||
|
{
|
||||||
|
int8 roundingMode;
|
||||||
|
flag roundNearestEven, increment;
|
||||||
|
|
||||||
|
roundingMode = STATUS(float_rounding_mode);
|
||||||
|
roundNearestEven = (roundingMode == float_round_nearest_even);
|
||||||
|
increment = ((int64_t)absZ1 < 0);
|
||||||
|
if (!roundNearestEven) {
|
||||||
|
if (roundingMode == float_round_to_zero) {
|
||||||
|
increment = 0;
|
||||||
|
} else if (absZ1) {
|
||||||
|
if (zSign) {
|
||||||
|
increment = (roundingMode == float_round_down) && absZ1;
|
||||||
|
} else {
|
||||||
|
increment = (roundingMode == float_round_up) && absZ1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (increment) {
|
||||||
|
++absZ0;
|
||||||
|
if (absZ0 == 0) {
|
||||||
|
float_raise(float_flag_invalid STATUS_VAR);
|
||||||
|
return LIT64(0xFFFFFFFFFFFFFFFF);
|
||||||
|
}
|
||||||
|
absZ0 &= ~(((uint64_t)(absZ1<<1) == 0) & roundNearestEven);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (zSign && absZ0) {
|
||||||
|
float_raise(float_flag_invalid STATUS_VAR);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (absZ1) {
|
||||||
|
STATUS(float_exception_flags) |= float_flag_inexact;
|
||||||
|
}
|
||||||
|
return absZ0;
|
||||||
|
}
|
||||||
|
|
||||||
/*----------------------------------------------------------------------------
|
/*----------------------------------------------------------------------------
|
||||||
| Returns the fraction bits of the single-precision floating-point value `a'.
|
| Returns the fraction bits of the single-precision floating-point value `a'.
|
||||||
*----------------------------------------------------------------------------*/
|
*----------------------------------------------------------------------------*/
|
||||||
@ -6643,16 +6693,51 @@ uint_fast16_t float64_to_uint16_round_to_zero(float64 a STATUS_PARAM)
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* FIXME: This looks broken. */
|
/*----------------------------------------------------------------------------
|
||||||
uint64_t float64_to_uint64 (float64 a STATUS_PARAM)
|
| Returns the result of converting the double-precision floating-point value
|
||||||
|
| `a' to the 64-bit unsigned integer format. The conversion is
|
||||||
|
| performed according to the IEC/IEEE Standard for Binary Floating-Point
|
||||||
|
| Arithmetic---which means in particular that the conversion is rounded
|
||||||
|
| according to the current rounding mode. If `a' is a NaN, the largest
|
||||||
|
| positive integer is returned. If the conversion overflows, the
|
||||||
|
| largest unsigned integer is returned. If 'a' is negative, the value is
|
||||||
|
| rounded and zero is returned; negative values that do not round to zero
|
||||||
|
| will raise the inexact exception.
|
||||||
|
*----------------------------------------------------------------------------*/
|
||||||
|
|
||||||
|
uint64_t float64_to_uint64(float64 a STATUS_PARAM)
|
||||||
{
|
{
|
||||||
int64_t v;
|
flag aSign;
|
||||||
|
int_fast16_t aExp, shiftCount;
|
||||||
|
uint64_t aSig, aSigExtra;
|
||||||
|
a = float64_squash_input_denormal(a STATUS_VAR);
|
||||||
|
|
||||||
v = float64_val(int64_to_float64(INT64_MIN STATUS_VAR));
|
aSig = extractFloat64Frac(a);
|
||||||
v += float64_val(a);
|
aExp = extractFloat64Exp(a);
|
||||||
v = float64_to_int64(make_float64(v) STATUS_VAR);
|
aSign = extractFloat64Sign(a);
|
||||||
|
if (aSign && (aExp > 1022)) {
|
||||||
return v - INT64_MIN;
|
float_raise(float_flag_invalid STATUS_VAR);
|
||||||
|
if (float64_is_any_nan(a)) {
|
||||||
|
return LIT64(0xFFFFFFFFFFFFFFFF);
|
||||||
|
} else {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (aExp) {
|
||||||
|
aSig |= LIT64(0x0010000000000000);
|
||||||
|
}
|
||||||
|
shiftCount = 0x433 - aExp;
|
||||||
|
if (shiftCount <= 0) {
|
||||||
|
if (0x43E < aExp) {
|
||||||
|
float_raise(float_flag_invalid STATUS_VAR);
|
||||||
|
return LIT64(0xFFFFFFFFFFFFFFFF);
|
||||||
|
}
|
||||||
|
aSigExtra = 0;
|
||||||
|
aSig <<= -shiftCount;
|
||||||
|
} else {
|
||||||
|
shift64ExtraRightJamming(aSig, 0, shiftCount, &aSig, &aSigExtra);
|
||||||
|
}
|
||||||
|
return roundAndPackUint64(aSign, aSig, aSigExtra STATUS_VAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM)
|
uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM)
|
||||||
|
Loading…
Reference in New Issue
Block a user