softfloat: Fix single-to-half precision float conversions
Fix various bugs in the single-to-half-precision conversion code: * input NaNs not correctly converted in IEEE mode (fixed by defining and using a commonNaNToFloat16()) * wrong values returned when converting NaN/Inf into non-IEEE half precision value * wrong values returned for conversion of values which are on the boundary between denormal and zero for the half precision format * zeroes not correctly identified * excessively large results in non-IEEE mode should generate InvalidOp, not Overflow Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
This commit is contained in:
parent
bcd4d9afd4
commit
600e30d2b2
@ -119,6 +119,27 @@ float16 float16_maybe_silence_nan(float16 a_)
|
||||
return a_;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| Returns the result of converting the canonical NaN `a' to the half-
|
||||
| precision floating-point format.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
||||
static float16 commonNaNToFloat16(commonNaNT a STATUS_PARAM)
|
||||
{
|
||||
uint16_t mantissa = a.high>>54;
|
||||
|
||||
if (STATUS(default_nan_mode)) {
|
||||
return float16_default_nan;
|
||||
}
|
||||
|
||||
if (mantissa) {
|
||||
return make_float16(((((uint16_t) a.sign) << 15)
|
||||
| (0x1F << 10) | mantissa));
|
||||
} else {
|
||||
return float16_default_nan;
|
||||
}
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------------
|
||||
| The pattern for a default generated single-precision NaN.
|
||||
*----------------------------------------------------------------------------*/
|
||||
|
@ -2796,24 +2796,30 @@ float16 float32_to_float16(float32 a, flag ieee STATUS_PARAM)
|
||||
aSign = extractFloat32Sign( a );
|
||||
if ( aExp == 0xFF ) {
|
||||
if (aSig) {
|
||||
/* Make sure correct exceptions are raised. */
|
||||
float32ToCommonNaN(a STATUS_VAR);
|
||||
aSig |= 0x00400000;
|
||||
/* Input is a NaN */
|
||||
float16 r = commonNaNToFloat16( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR );
|
||||
if (!ieee) {
|
||||
return packFloat16(aSign, 0, 0);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
return packFloat16(aSign, 0x1f, aSig >> 13);
|
||||
/* Infinity */
|
||||
if (!ieee) {
|
||||
float_raise(float_flag_invalid STATUS_VAR);
|
||||
return packFloat16(aSign, 0x1f, 0x3ff);
|
||||
}
|
||||
return packFloat16(aSign, 0x1f, 0);
|
||||
}
|
||||
if (aExp == 0 && aSign == 0) {
|
||||
if (aExp == 0 && aSig == 0) {
|
||||
return packFloat16(aSign, 0, 0);
|
||||
}
|
||||
/* Decimal point between bits 22 and 23. */
|
||||
aSig |= 0x00800000;
|
||||
aExp -= 0x7f;
|
||||
if (aExp < -14) {
|
||||
mask = 0x007fffff;
|
||||
if (aExp < -24) {
|
||||
aExp = -25;
|
||||
} else {
|
||||
mask >>= 24 + aExp;
|
||||
mask = 0x00ffffff;
|
||||
if (aExp >= -24) {
|
||||
mask >>= 25 + aExp;
|
||||
}
|
||||
} else {
|
||||
mask = 0x00001fff;
|
||||
@ -2855,7 +2861,7 @@ float16 float32_to_float16(float32 a, flag ieee STATUS_PARAM)
|
||||
}
|
||||
} else {
|
||||
if (aExp > 16) {
|
||||
float_raise( float_flag_overflow | float_flag_inexact STATUS_VAR);
|
||||
float_raise(float_flag_invalid | float_flag_inexact STATUS_VAR);
|
||||
return packFloat16(aSign, 0x1f, 0x3ff);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user