@@ -120,6 +120,27 @@ float16 float16_maybe_silence_nan(float16 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.
*----------------------------------------------------------------------------*/
#if defined(TARGET_SPARC)
@@ -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);
}
}
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> --- fpu/softfloat-specialize.h | 21 +++++++++++++++++++++ fpu/softfloat.c | 28 +++++++++++++++++----------- 2 files changed, 38 insertions(+), 11 deletions(-)