diff mbox

[07/20] softfloat: fix float*_scalnb() corner cases

Message ID 1303160412-8107-8-git-send-email-aurelien@aurel32.net
State New
Headers show

Commit Message

Aurelien Jarno April 18, 2011, 8:59 p.m. UTC
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.

Signed-off-by: Aurelien Jarno <aurelien@aurel32.net>
---
 fpu/softfloat.c |   47 ++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 42 insertions(+), 5 deletions(-)

Comments

Peter Maydell April 19, 2011, 11:57 a.m. UTC | #1
On 18 April 2011 21:59, Aurelien Jarno <aurelien@aurel32.net> wrote:

> @@ -6349,6 +6352,12 @@ float32 float32_scalbn( float32 a, int n STATUS_PARAM )
>     else if ( aSig == 0 )
>         return a;
>
> +    if (n > 0x80) {
> +        n = 0x80;
> +    } else if (n < -0x80) {
> +        n = -0x80;
> +    }
> +
>     aExp += n - 1;
>     aSig <<= 7;
>     return normalizeRoundAndPackFloat32( aSign, aExp, aSig STATUS_VAR );

I don't think your if() condition is right here. Consider the
float32 00800000 (1.0 * 2 ^ -126 ; the smallest possible normalised
number); you can multiply this by, say, 2^253, without overflowing
to infinity. However your if() here means we'll incorrectly
compute the result of multiplying by 2^128 instead. s/0x80/0x200/
should work.

The others look OK to me. I would personally prefer to retain the int16
etc rather than moving to int16_t, but I don't quite feel strongly enough
to argue about it.

-- PMM
Aurelien Jarno April 20, 2011, 9:21 a.m. UTC | #2
On Tue, Apr 19, 2011 at 12:57:23PM +0100, Peter Maydell wrote:
> On 18 April 2011 21:59, Aurelien Jarno <aurelien@aurel32.net> wrote:
> 
> > @@ -6349,6 +6352,12 @@ float32 float32_scalbn( float32 a, int n STATUS_PARAM )
> >     else if ( aSig == 0 )
> >         return a;
> >
> > +    if (n > 0x80) {
> > +        n = 0x80;
> > +    } else if (n < -0x80) {
> > +        n = -0x80;
> > +    }
> > +
> >     aExp += n - 1;
> >     aSig <<= 7;
> >     return normalizeRoundAndPackFloat32( aSign, aExp, aSig STATUS_VAR );
> 
> I don't think your if() condition is right here. Consider the
> float32 00800000 (1.0 * 2 ^ -126 ; the smallest possible normalised
> number); you can multiply this by, say, 2^253, without overflowing
> to infinity. However your if() here means we'll incorrectly
> compute the result of multiplying by 2^128 instead. s/0x80/0x200/
> should work.
> 

Correct, will be fixed in the next version.
diff mbox

Patch

diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 4368069..8f755c3 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -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 > 0x80) {
+        n = 0x80;
+    } else if (n < -0x80) {
+        n = -0x80;
+    }
+
     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 );