diff mbox

[14/22] softfloat: Add support for ties-away rounding

Message ID 1388496958-3542-15-git-send-email-peter.maydell@linaro.org
State New
Headers show

Commit Message

Peter Maydell Dec. 31, 2013, 1:35 p.m. UTC
IEEE754-2008 specifies a new rounding mode:

"roundTiesToAway: the floating-point number nearest to the infinitely
precise result shall be delivered; if the two nearest floating-point
numbers bracketing an unrepresentable infinitely precise result are
equally near, the one with larger magnitude shall be delivered."

Implement this new mode (it is needed for ARM). The general principle
is that the required code is exactly like the ties-to-even code,
except that we do not need to do the "in case of exact tie clear LSB
to round-to-even", because the rounding operation naturally causes
the exact tie to round up in magnitude.

Signed-off-by: Peter Maydell <peter.maydell@linaro.org>
---
 fpu/softfloat.c         | 68 ++++++++++++++++++++++++++++++++++++-------------
 include/fpu/softfloat.h |  3 ++-
 2 files changed, 53 insertions(+), 18 deletions(-)

Comments

Richard Henderson Dec. 31, 2013, 2:51 p.m. UTC | #1
[Tom, this is exactly what you need to fix FRIN rounding.]

On 12/31/2013 05:35 AM, Peter Maydell wrote:
> -    float_round_to_zero      = 3
> +    float_round_to_zero      = 3,
> +    float_round_ties_away    = 4,

I'm not keen on the name.  Does anyone else think float_round_nearest_inf is a
better name?

> +++ b/fpu/softfloat.c
> @@ -107,7 +107,7 @@ static int32 roundAndPackInt32( flag zSign, uint64_t absZ STATUS_PARAM)
>      roundingMode = STATUS(float_rounding_mode);
>      roundNearestEven = ( roundingMode == float_round_nearest_even );
>      roundIncrement = 0x40;
> -    if ( ! roundNearestEven ) {
> +    if (!roundNearestEven && roundingMode != float_round_ties_away) {
>          if ( roundingMode == float_round_to_zero ) {
>              roundIncrement = 0;
>          }

This whole section of code is now a mess.  I know you're looking for minimal
changes here, but perhaps I can convince you that

    switch (roundingMode) {
    case float_round_nearest_even:
    case float_round_ties_away:
        roundIncrement = 0x40;
        break;
    case float_round_to_zero:
        roundIncrement = 0;
        break;
    case float_round_up:
        roundIncrement = zSign ? 0 : 0x7f;
        break;
    case float_round_down:
        roundIncrement = zSign ? 0x7f : 0;
        break;
    default:
        abort();
    }

is easier to follow?

Otherwise, I don't see anything actually wrong in the patch.


r~
Peter Maydell Dec. 31, 2013, 2:56 p.m. UTC | #2
On 31 December 2013 14:51, Richard Henderson <rth@twiddle.net> wrote:
> [Tom, this is exactly what you need to fix FRIN rounding.]
>
> On 12/31/2013 05:35 AM, Peter Maydell wrote:
>> -    float_round_to_zero      = 3
>> +    float_round_to_zero      = 3,
>> +    float_round_ties_away    = 4,
>
> I'm not keen on the name.  Does anyone else think float_round_nearest_inf is a
> better name?

The IEEE spec specifically calls this roundTiesToAway. I'd rather
not deviate from the official name unless there's a good reason.
(Though admittedly we don't really match up on the other rounding
mode names.)

>
>> +++ b/fpu/softfloat.c
>> @@ -107,7 +107,7 @@ static int32 roundAndPackInt32( flag zSign, uint64_t absZ STATUS_PARAM)
>>      roundingMode = STATUS(float_rounding_mode);
>>      roundNearestEven = ( roundingMode == float_round_nearest_even );
>>      roundIncrement = 0x40;
>> -    if ( ! roundNearestEven ) {
>> +    if (!roundNearestEven && roundingMode != float_round_ties_away) {
>>          if ( roundingMode == float_round_to_zero ) {
>>              roundIncrement = 0;
>>          }
>
> This whole section of code is now a mess.  I know you're looking for minimal
> changes here, but perhaps I can convince you that
>
>     switch (roundingMode) {
>     case float_round_nearest_even:
>     case float_round_ties_away:
>         roundIncrement = 0x40;
>         break;
>     case float_round_to_zero:
>         roundIncrement = 0;
>         break;
>     case float_round_up:
>         roundIncrement = zSign ? 0 : 0x7f;
>         break;
>     case float_round_down:
>         roundIncrement = zSign ? 0x7f : 0;
>         break;
>     default:
>         abort();
>     }
>
> is easier to follow?

I agree it looks much better. I'd prefer to keep adding features
and refactoring code in separate patches, though.

thanks
-- PMM
Richard Henderson Dec. 31, 2013, 2:59 p.m. UTC | #3
On 12/31/2013 06:56 AM, Peter Maydell wrote:
> The IEEE spec specifically calls this roundTiesToAway. I'd rather
> not deviate from the official name unless there's a good reason.

Fair enough.


r~
diff mbox

Patch

diff --git a/fpu/softfloat.c b/fpu/softfloat.c
index 623a4b9..ce7970f 100644
--- a/fpu/softfloat.c
+++ b/fpu/softfloat.c
@@ -107,7 +107,7 @@  static int32 roundAndPackInt32( flag zSign, uint64_t absZ STATUS_PARAM)
     roundingMode = STATUS(float_rounding_mode);
     roundNearestEven = ( roundingMode == float_round_nearest_even );
     roundIncrement = 0x40;
-    if ( ! roundNearestEven ) {
+    if (!roundNearestEven && roundingMode != float_round_ties_away) {
         if ( roundingMode == float_round_to_zero ) {
             roundIncrement = 0;
         }
@@ -155,7 +155,7 @@  static int64 roundAndPackInt64( flag zSign, uint64_t absZ0, uint64_t absZ1 STATU
     roundingMode = STATUS(float_rounding_mode);
     roundNearestEven = ( roundingMode == float_round_nearest_even );
     increment = ( (int64_t) absZ1 < 0 );
-    if ( ! roundNearestEven ) {
+    if (!roundNearestEven && roundingMode != float_round_ties_away) {
         if ( roundingMode == float_round_to_zero ) {
             increment = 0;
         }
@@ -206,7 +206,7 @@  static int64 roundAndPackUint64(flag zSign, uint64_t absZ0,
     roundingMode = STATUS(float_rounding_mode);
     roundNearestEven = (roundingMode == float_round_nearest_even);
     increment = ((int64_t)absZ1 < 0);
-    if (!roundNearestEven) {
+    if (!roundNearestEven && roundingMode != float_round_ties_away) {
         if (roundingMode == float_round_to_zero) {
             increment = 0;
         } else if (absZ1) {
@@ -354,7 +354,7 @@  static float32 roundAndPackFloat32(flag zSign, int_fast16_t zExp, uint32_t zSig
     roundingMode = STATUS(float_rounding_mode);
     roundNearestEven = ( roundingMode == float_round_nearest_even );
     roundIncrement = 0x40;
-    if ( ! roundNearestEven ) {
+    if (!roundNearestEven && roundingMode != float_round_ties_away) {
         if ( roundingMode == float_round_to_zero ) {
             roundIncrement = 0;
         }
@@ -536,7 +536,7 @@  static float64 roundAndPackFloat64(flag zSign, int_fast16_t zExp, uint64_t zSig
     roundingMode = STATUS(float_rounding_mode);
     roundNearestEven = ( roundingMode == float_round_nearest_even );
     roundIncrement = 0x200;
-    if ( ! roundNearestEven ) {
+    if (!roundNearestEven && roundingMode != float_round_ties_away) {
         if ( roundingMode == float_round_to_zero ) {
             roundIncrement = 0;
         }
@@ -718,7 +718,7 @@  static floatx80
         goto precision80;
     }
     zSig0 |= ( zSig1 != 0 );
-    if ( ! roundNearestEven ) {
+    if (!roundNearestEven && roundingMode != float_round_ties_away) {
         if ( roundingMode == float_round_to_zero ) {
             roundIncrement = 0;
         }
@@ -1029,7 +1029,7 @@  static float128
     roundingMode = STATUS(float_rounding_mode);
     roundNearestEven = ( roundingMode == float_round_nearest_even );
     increment = ( (int64_t) zSig2 < 0 );
-    if ( ! roundNearestEven ) {
+    if (!roundNearestEven && roundingMode != float_round_ties_away) {
         if ( roundingMode == float_round_to_zero ) {
             increment = 0;
         }
@@ -1756,6 +1756,11 @@  float32 float32_round_to_int( float32 a STATUS_PARAM)
                 return packFloat32( aSign, 0x7F, 0 );
             }
             break;
+        case float_round_ties_away:
+            if (aExp == 0x7E) {
+                return packFloat32(aSign, 0x7F, 0);
+            }
+            break;
          case float_round_down:
             return make_float32(aSign ? 0xBF800000 : 0);
          case float_round_up:
@@ -1771,8 +1776,9 @@  float32 float32_round_to_int( float32 a STATUS_PARAM)
     if ( roundingMode == float_round_nearest_even ) {
         z += lastBitMask>>1;
         if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
-    }
-    else if ( roundingMode != float_round_to_zero ) {
+    } else if (roundingMode == float_round_ties_away) {
+        z += lastBitMask >> 1;
+    } else if (roundingMode != float_round_to_zero) {
         if ( extractFloat32Sign( make_float32(z) ) ^ ( roundingMode == float_round_up ) ) {
             z += roundBitsMask;
         }
@@ -3144,6 +3150,9 @@  static float32 roundAndPackFloat16(flag zSign, int_fast16_t zExp,
             increment = zSig & (increment << 1);
         }
         break;
+    case float_round_ties_away:
+        increment = (mask + 1) >> 1;
+        break;
     case float_round_up:
         increment = zSign ? 0 : mask;
         break;
@@ -3449,6 +3458,11 @@  float64 float64_round_to_int( float64 a STATUS_PARAM )
                 return packFloat64( aSign, 0x3FF, 0 );
             }
             break;
+        case float_round_ties_away:
+            if (aExp == 0x3FE) {
+                return packFloat64(aSign, 0x3ff, 0);
+            }
+            break;
          case float_round_down:
             return make_float64(aSign ? LIT64( 0xBFF0000000000000 ) : 0);
          case float_round_up:
@@ -3465,8 +3479,9 @@  float64 float64_round_to_int( float64 a STATUS_PARAM )
     if ( roundingMode == float_round_nearest_even ) {
         z += lastBitMask>>1;
         if ( ( z & roundBitsMask ) == 0 ) z &= ~ lastBitMask;
-    }
-    else if ( roundingMode != float_round_to_zero ) {
+    } else if (roundingMode == float_round_ties_away) {
+        z += lastBitMask >> 1;
+    } else if (roundingMode != float_round_to_zero) {
         if ( extractFloat64Sign( make_float64(z) ) ^ ( roundingMode == float_round_up ) ) {
             z += roundBitsMask;
         }
@@ -4722,6 +4737,11 @@  floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM )
                     packFloatx80( aSign, 0x3FFF, LIT64( 0x8000000000000000 ) );
             }
             break;
+        case float_round_ties_away:
+            if (aExp == 0x3FFE) {
+                return packFloatx80(aSign, 0x3FFF, LIT64(0x8000000000000000));
+            }
+            break;
          case float_round_down:
             return
                   aSign ?
@@ -4742,8 +4762,9 @@  floatx80 floatx80_round_to_int( floatx80 a STATUS_PARAM )
     if ( roundingMode == float_round_nearest_even ) {
         z.low += lastBitMask>>1;
         if ( ( z.low & roundBitsMask ) == 0 ) z.low &= ~ lastBitMask;
-    }
-    else if ( roundingMode != float_round_to_zero ) {
+    } else if (roundingMode == float_round_ties_away) {
+        z.low += lastBitMask >> 1;
+    } else if (roundingMode != float_round_to_zero) {
         if ( extractFloatx80Sign( z ) ^ ( roundingMode == float_round_up ) ) {
             z.low += roundBitsMask;
         }
@@ -5801,8 +5822,15 @@  float128 float128_round_to_int( float128 a STATUS_PARAM )
                     if ( (uint64_t) ( z.low<<1 ) == 0 ) z.high &= ~1;
                 }
             }
-        }
-        else if ( roundingMode != float_round_to_zero ) {
+        } else if (roundingMode == float_round_ties_away) {
+            if (lastBitMask) {
+                add128(z.high, z.low, 0, lastBitMask >> 1, &z.high, &z.low);
+            } else {
+                if ((int64_t) z.low < 0) {
+                    ++z.high;
+                }
+            }
+        } else if (roundingMode != float_round_to_zero) {
             if (   extractFloat128Sign( z )
                  ^ ( roundingMode == float_round_up ) ) {
                 add128( z.high, z.low, 0, roundBitsMask, &z.high, &z.low );
@@ -5824,6 +5852,11 @@  float128 float128_round_to_int( float128 a STATUS_PARAM )
                     return packFloat128( aSign, 0x3FFF, 0, 0 );
                 }
                 break;
+            case float_round_ties_away:
+                if (aExp == 0x3FFE) {
+                    return packFloat128(aSign, 0x3FFF, 0, 0);
+                }
+                break;
              case float_round_down:
                 return
                       aSign ? packFloat128( 1, 0x3FFF, 0, 0 )
@@ -5846,8 +5879,9 @@  float128 float128_round_to_int( float128 a STATUS_PARAM )
             if ( ( ( z.high & roundBitsMask ) | a.low ) == 0 ) {
                 z.high &= ~ lastBitMask;
             }
-        }
-        else if ( roundingMode != float_round_to_zero ) {
+        } else if (roundingMode == float_round_ties_away) {
+            z.high += lastBitMask>>1;
+        } else if (roundingMode != float_round_to_zero) {
             if (   extractFloat128Sign( z )
                  ^ ( roundingMode == float_round_up ) ) {
                 z.high |= ( a.low != 0 );
diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h
index 08c7559..7f50d4f 100644
--- a/include/fpu/softfloat.h
+++ b/include/fpu/softfloat.h
@@ -152,7 +152,8 @@  enum {
     float_round_nearest_even = 0,
     float_round_down         = 1,
     float_round_up           = 2,
-    float_round_to_zero      = 3
+    float_round_to_zero      = 3,
+    float_round_ties_away    = 4,
 };
 
 /*----------------------------------------------------------------------------