softfloat: fix float*_scalnb() corner cases

float*_scalnb() were not taking into account all cases. This patch fixes
some corner cases:
- NaN values in input were not properly propagated and the invalid flag
  not correctly raised. Use propagateFloat*NaN() for that.
- NaN or infinite values in input of floatx80_scalnb() were not correctly
  detected due to a typo.
- The sum of exponent and n could overflow, leading to strange results.
  Additionally having int16 defined to int make that happening for a very
  small range of values. Fix that by saturating n to the maximum exponent
  range, and using an explicit wider type if needed.

Reviewed-by: Peter Maydell <peter.maydell@linaro.org>
Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
This commit is contained in:
Aurelien Jarno 2011-04-20 13:04:22 +02:00
parent f6714d365d
commit 326b9e98a3

View File

@ -6333,7 +6333,7 @@ MINMAX(64, 0x7ff)
float32 float32_scalbn( float32 a, int n STATUS_PARAM )
{
flag aSign;
int16 aExp;
int16_t aExp;
uint32_t aSig;
a = float32_squash_input_denormal(a STATUS_VAR);
@ -6342,6 +6342,9 @@ float32 float32_scalbn( float32 a, int n STATUS_PARAM )
aSign = extractFloat32Sign( a );
if ( aExp == 0xFF ) {
if ( aSig ) {
return propagateFloat32NaN( a, a STATUS_VAR );
}
return a;
}
if ( aExp != 0 )
@ -6349,6 +6352,12 @@ float32 float32_scalbn( float32 a, int n STATUS_PARAM )
else if ( aSig == 0 )
return a;
if (n > 0x200) {
n = 0x200;
} else if (n < -0x200) {
n = -0x200;
}
aExp += n - 1;
aSig <<= 7;
return normalizeRoundAndPackFloat32( aSign, aExp, aSig STATUS_VAR );
@ -6357,7 +6366,7 @@ float32 float32_scalbn( float32 a, int n STATUS_PARAM )
float64 float64_scalbn( float64 a, int n STATUS_PARAM )
{
flag aSign;
int16 aExp;
int16_t aExp;
uint64_t aSig;
a = float64_squash_input_denormal(a STATUS_VAR);
@ -6366,6 +6375,9 @@ float64 float64_scalbn( float64 a, int n STATUS_PARAM )
aSign = extractFloat64Sign( a );
if ( aExp == 0x7FF ) {
if ( aSig ) {
return propagateFloat64NaN( a, a STATUS_VAR );
}
return a;
}
if ( aExp != 0 )
@ -6373,6 +6385,12 @@ float64 float64_scalbn( float64 a, int n STATUS_PARAM )
else if ( aSig == 0 )
return a;
if (n > 0x1000) {
n = 0x1000;
} else if (n < -0x1000) {
n = -0x1000;
}
aExp += n - 1;
aSig <<= 10;
return normalizeRoundAndPackFloat64( aSign, aExp, aSig STATUS_VAR );
@ -6382,19 +6400,29 @@ float64 float64_scalbn( float64 a, int n STATUS_PARAM )
floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM )
{
flag aSign;
int16 aExp;
int32_t aExp;
uint64_t aSig;
aSig = extractFloatx80Frac( a );
aExp = extractFloatx80Exp( a );
aSign = extractFloatx80Sign( a );
if ( aExp == 0x7FF ) {
if ( aExp == 0x7FFF ) {
if ( aSig<<1 ) {
return propagateFloatx80NaN( a, a STATUS_VAR );
}
return a;
}
if (aExp == 0 && aSig == 0)
return a;
if (n > 0x10000) {
n = 0x10000;
} else if (n < -0x10000) {
n = -0x10000;
}
aExp += n;
return normalizeRoundAndPackFloatx80( STATUS(floatx80_rounding_precision),
aSign, aExp, aSig, 0 STATUS_VAR );
@ -6405,7 +6433,7 @@ floatx80 floatx80_scalbn( floatx80 a, int n STATUS_PARAM )
float128 float128_scalbn( float128 a, int n STATUS_PARAM )
{
flag aSign;
int32 aExp;
int32_t aExp;
uint64_t aSig0, aSig1;
aSig1 = extractFloat128Frac1( a );
@ -6413,6 +6441,9 @@ float128 float128_scalbn( float128 a, int n STATUS_PARAM )
aExp = extractFloat128Exp( a );
aSign = extractFloat128Sign( a );
if ( aExp == 0x7FFF ) {
if ( aSig0 | aSig1 ) {
return propagateFloat128NaN( a, a STATUS_VAR );
}
return a;
}
if ( aExp != 0 )
@ -6420,6 +6451,12 @@ float128 float128_scalbn( float128 a, int n STATUS_PARAM )
else if ( aSig0 == 0 && aSig1 == 0 )
return a;
if (n > 0x10000) {
n = 0x10000;
} else if (n < -0x10000) {
n = -0x10000;
}
aExp += n - 1;
return normalizeRoundAndPackFloat128( aSign, aExp, aSig0, aSig1
STATUS_VAR );