diff options
Diffstat (limited to 'fpu')
| -rw-r--r-- | fpu/softfloat-specialize.h | 130 | ||||
| -rw-r--r-- | fpu/softfloat.c | 130 | ||||
| -rw-r--r-- | fpu/softfloat.h | 25 |
3 files changed, 230 insertions, 55 deletions
diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index eb644b2273..2d025bfa36 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -57,6 +57,107 @@ typedef struct { } commonNaNT; /*---------------------------------------------------------------------------- +| The pattern for a default generated half-precision NaN. +*----------------------------------------------------------------------------*/ +#if defined(TARGET_ARM) +#define float16_default_nan make_float16(0x7E00) +#elif SNAN_BIT_IS_ONE +#define float16_default_nan make_float16(0x7DFF) +#else +#define float16_default_nan make_float16(0xFE00) +#endif + +/*---------------------------------------------------------------------------- +| Returns 1 if the half-precision floating-point value `a' is a quiet +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +int float16_is_quiet_nan(float16 a_) +{ + uint16_t a = float16_val(a_); +#if SNAN_BIT_IS_ONE + return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); +#else + return ((a & ~0x8000) >= 0x7c80); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns 1 if the half-precision floating-point value `a' is a signaling +| NaN; otherwise returns 0. +*----------------------------------------------------------------------------*/ + +int float16_is_signaling_nan(float16 a_) +{ + uint16_t a = float16_val(a_); +#if SNAN_BIT_IS_ONE + return ((a & ~0x8000) >= 0x7c80); +#else + return (((a >> 9) & 0x3F) == 0x3E) && (a & 0x1FF); +#endif +} + +/*---------------------------------------------------------------------------- +| Returns a quiet NaN if the half-precision floating point value `a' is a +| signaling NaN; otherwise returns `a'. +*----------------------------------------------------------------------------*/ +float16 float16_maybe_silence_nan(float16 a_) +{ + if (float16_is_signaling_nan(a_)) { +#if SNAN_BIT_IS_ONE +# if defined(TARGET_MIPS) || defined(TARGET_SH4) + return float16_default_nan; +# else +# error Rules for silencing a signaling NaN are target-specific +# endif +#else + uint16_t a = float16_val(a_); + a |= (1 << 9); + return make_float16(a); +#endif + } + return a_; +} + +/*---------------------------------------------------------------------------- +| Returns the result of converting the half-precision floating-point NaN +| `a' to the canonical NaN format. If `a' is a signaling NaN, the invalid +| exception is raised. +*----------------------------------------------------------------------------*/ + +static commonNaNT float16ToCommonNaN( float16 a STATUS_PARAM ) +{ + commonNaNT z; + + if ( float16_is_signaling_nan( a ) ) float_raise( float_flag_invalid STATUS_VAR ); + z.sign = float16_val(a) >> 15; + z.low = 0; + z.high = ((bits64) float16_val(a))<<54; + return z; +} + +/*---------------------------------------------------------------------------- +| 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) @@ -144,9 +245,14 @@ static commonNaNT float32ToCommonNaN( float32 a STATUS_PARAM ) | precision floating-point format. *----------------------------------------------------------------------------*/ -static float32 commonNaNToFloat32( commonNaNT a ) +static float32 commonNaNToFloat32( commonNaNT a STATUS_PARAM) { bits32 mantissa = a.high>>41; + + if ( STATUS(default_nan_mode) ) { + return float32_default_nan; + } + if ( mantissa ) return make_float32( ( ( (bits32) a.sign )<<31 ) | 0x7F800000 | ( a.high>>41 ) ); @@ -398,10 +504,14 @@ static commonNaNT float64ToCommonNaN( float64 a STATUS_PARAM) | precision floating-point format. *----------------------------------------------------------------------------*/ -static float64 commonNaNToFloat64( commonNaNT a ) +static float64 commonNaNToFloat64( commonNaNT a STATUS_PARAM) { bits64 mantissa = a.high>>12; + if ( STATUS(default_nan_mode) ) { + return float64_default_nan; + } + if ( mantissa ) return make_float64( ( ( (bits64) a.sign )<<63 ) @@ -555,10 +665,16 @@ static commonNaNT floatx80ToCommonNaN( floatx80 a STATUS_PARAM) | double-precision floating-point format. *----------------------------------------------------------------------------*/ -static floatx80 commonNaNToFloatx80( commonNaNT a ) +static floatx80 commonNaNToFloatx80( commonNaNT a STATUS_PARAM) { floatx80 z; + if ( STATUS(default_nan_mode) ) { + z.low = floatx80_default_nan_low; + z.high = floatx80_default_nan_high; + return z; + } + if (a.high) z.low = a.high; else @@ -703,10 +819,16 @@ static commonNaNT float128ToCommonNaN( float128 a STATUS_PARAM) | precision floating-point format. *----------------------------------------------------------------------------*/ -static float128 commonNaNToFloat128( commonNaNT a ) +static float128 commonNaNToFloat128( commonNaNT a STATUS_PARAM) { float128 z; + if ( STATUS(default_nan_mode) ) { + z.low = float128_default_nan_low; + z.high = float128_default_nan_high; + return z; + } + shift128Right( a.high, a.low, 16, &z.high, &z.low ); z.high |= ( ( (bits64) a.sign )<<63 ) | LIT64( 0x7FFF000000000000 ); return z; diff --git a/fpu/softfloat.c b/fpu/softfloat.c index 17842f43da..30b07e9b44 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -67,6 +67,33 @@ void set_floatx80_rounding_precision(int val STATUS_PARAM) #endif /*---------------------------------------------------------------------------- +| Returns the fraction bits of the half-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE uint32_t extractFloat16Frac(float16 a) +{ + return float16_val(a) & 0x3ff; +} + +/*---------------------------------------------------------------------------- +| Returns the exponent bits of the half-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE int16 extractFloat16Exp(float16 a) +{ + return (float16_val(a) >> 10) & 0x1f; +} + +/*---------------------------------------------------------------------------- +| Returns the sign bit of the single-precision floating-point value `a'. +*----------------------------------------------------------------------------*/ + +INLINE flag extractFloat16Sign(float16 a) +{ + return float16_val(a)>>15; +} + +/*---------------------------------------------------------------------------- | Takes a 64-bit fixed-point value `absZ' with binary point between bits 6 | and 7, and returns the properly rounded 32-bit integer corresponding to the | input. If `zSign' is 1, the input is negated before being converted to an @@ -1534,7 +1561,7 @@ float64 float32_to_float64( float32 a STATUS_PARAM ) aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { - if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a STATUS_VAR )); + if ( aSig ) return commonNaNToFloat64( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloat64( aSign, 0x7FF, 0 ); } if ( aExp == 0 ) { @@ -1566,7 +1593,7 @@ floatx80 float32_to_floatx80( float32 a STATUS_PARAM ) aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { - if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloatx80( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) { @@ -1600,7 +1627,7 @@ float128 float32_to_float128( float32 a STATUS_PARAM ) aExp = extractFloat32Exp( a ); aSign = extractFloat32Sign( a ); if ( aExp == 0xFF ) { - if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloat128( float32ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloat128( aSign, 0x7FFF, 0, 0 ); } if ( aExp == 0 ) { @@ -2172,21 +2199,21 @@ float32 float32_sqrt( float32 a STATUS_PARAM ) static const float64 float32_exp2_coefficients[15] = { - make_float64( 0x3ff0000000000000ll ), /* 1 */ - make_float64( 0x3fe0000000000000ll ), /* 2 */ - make_float64( 0x3fc5555555555555ll ), /* 3 */ - make_float64( 0x3fa5555555555555ll ), /* 4 */ - make_float64( 0x3f81111111111111ll ), /* 5 */ - make_float64( 0x3f56c16c16c16c17ll ), /* 6 */ - make_float64( 0x3f2a01a01a01a01all ), /* 7 */ - make_float64( 0x3efa01a01a01a01all ), /* 8 */ - make_float64( 0x3ec71de3a556c734ll ), /* 9 */ - make_float64( 0x3e927e4fb7789f5cll ), /* 10 */ - make_float64( 0x3e5ae64567f544e4ll ), /* 11 */ - make_float64( 0x3e21eed8eff8d898ll ), /* 12 */ - make_float64( 0x3de6124613a86d09ll ), /* 13 */ - make_float64( 0x3da93974a8c07c9dll ), /* 14 */ - make_float64( 0x3d6ae7f3e733b81fll ), /* 15 */ + const_float64( 0x3ff0000000000000ll ), /* 1 */ + const_float64( 0x3fe0000000000000ll ), /* 2 */ + const_float64( 0x3fc5555555555555ll ), /* 3 */ + const_float64( 0x3fa5555555555555ll ), /* 4 */ + const_float64( 0x3f81111111111111ll ), /* 5 */ + const_float64( 0x3f56c16c16c16c17ll ), /* 6 */ + const_float64( 0x3f2a01a01a01a01all ), /* 7 */ + const_float64( 0x3efa01a01a01a01all ), /* 8 */ + const_float64( 0x3ec71de3a556c734ll ), /* 9 */ + const_float64( 0x3e927e4fb7789f5cll ), /* 10 */ + const_float64( 0x3e5ae64567f544e4ll ), /* 11 */ + const_float64( 0x3e21eed8eff8d898ll ), /* 12 */ + const_float64( 0x3de6124613a86d09ll ), /* 13 */ + const_float64( 0x3da93974a8c07c9dll ), /* 14 */ + const_float64( 0x3d6ae7f3e733b81fll ), /* 15 */ }; float32 float32_exp2( float32 a STATUS_PARAM ) @@ -2689,7 +2716,7 @@ float32 float64_to_float32( float64 a STATUS_PARAM ) aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { - if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloat32( float64ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloat32( aSign, 0xFF, 0 ); } shift64RightJamming( aSig, 22, &aSig ); @@ -2713,29 +2740,28 @@ float32 float64_to_float32( float64 a STATUS_PARAM ) | than the desired result exponent whenever `zSig' is a complete, normalized | significand. *----------------------------------------------------------------------------*/ -static bits16 packFloat16(flag zSign, int16 zExp, bits16 zSig) +static float16 packFloat16(flag zSign, int16 zExp, bits16 zSig) { - return (((bits32)zSign) << 15) + (((bits32)zExp) << 10) + zSig; + return make_float16( + (((bits32)zSign) << 15) + (((bits32)zExp) << 10) + zSig); } /* Half precision floats come in two formats: standard IEEE and "ARM" format. The latter gains extra exponent range by omitting the NaN/Inf encodings. */ - -float32 float16_to_float32( bits16 a, flag ieee STATUS_PARAM ) + +float32 float16_to_float32(float16 a, flag ieee STATUS_PARAM) { flag aSign; int16 aExp; bits32 aSig; - aSign = a >> 15; - aExp = (a >> 10) & 0x1f; - aSig = a & 0x3ff; + aSign = extractFloat16Sign(a); + aExp = extractFloat16Exp(a); + aSig = extractFloat16Frac(a); if (aExp == 0x1f && ieee) { if (aSig) { - /* Make sure correct exceptions are raised. */ - float32ToCommonNaN(a STATUS_VAR); - aSig |= 0x200; + return commonNaNToFloat32(float16ToCommonNaN(a STATUS_VAR) STATUS_VAR); } return packFloat32(aSign, 0xff, aSig << 13); } @@ -2753,7 +2779,7 @@ float32 float16_to_float32( bits16 a, flag ieee STATUS_PARAM ) return packFloat32( aSign, aExp + 0x70, aSig << 13); } -bits16 float32_to_float16( float32 a, flag ieee STATUS_PARAM) +float16 float32_to_float16(float32 a, flag ieee STATUS_PARAM) { flag aSign; int16 aExp; @@ -2768,24 +2794,30 @@ bits16 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; + } + /* Infinity */ + if (!ieee) { + float_raise(float_flag_invalid STATUS_VAR); + return packFloat16(aSign, 0x1f, 0x3ff); } - return packFloat16(aSign, 0x1f, aSig >> 13); + 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; @@ -2827,7 +2859,7 @@ bits16 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); } } @@ -2861,7 +2893,7 @@ floatx80 float64_to_floatx80( float64 a STATUS_PARAM ) aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { - if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloatx80( float64ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } if ( aExp == 0 ) { @@ -2896,7 +2928,7 @@ float128 float64_to_float128( float64 a STATUS_PARAM ) aExp = extractFloat64Exp( a ); aSign = extractFloat64Sign( a ); if ( aExp == 0x7FF ) { - if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a STATUS_VAR ) ); + if ( aSig ) return commonNaNToFloat128( float64ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); return packFloat128( aSign, 0x7FFF, 0, 0 ); } if ( aExp == 0 ) { @@ -3843,7 +3875,7 @@ float32 floatx80_to_float32( floatx80 a STATUS_PARAM ) aSign = extractFloatx80Sign( a ); if ( aExp == 0x7FFF ) { if ( (bits64) ( aSig<<1 ) ) { - return commonNaNToFloat32( floatx80ToCommonNaN( a STATUS_VAR ) ); + return commonNaNToFloat32( floatx80ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloat32( aSign, 0xFF, 0 ); } @@ -3871,7 +3903,7 @@ float64 floatx80_to_float64( floatx80 a STATUS_PARAM ) aSign = extractFloatx80Sign( a ); if ( aExp == 0x7FFF ) { if ( (bits64) ( aSig<<1 ) ) { - return commonNaNToFloat64( floatx80ToCommonNaN( a STATUS_VAR ) ); + return commonNaNToFloat64( floatx80ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloat64( aSign, 0x7FF, 0 ); } @@ -3900,7 +3932,7 @@ float128 floatx80_to_float128( floatx80 a STATUS_PARAM ) aExp = extractFloatx80Exp( a ); aSign = extractFloatx80Sign( a ); if ( ( aExp == 0x7FFF ) && (bits64) ( aSig<<1 ) ) { - return commonNaNToFloat128( floatx80ToCommonNaN( a STATUS_VAR ) ); + return commonNaNToFloat128( floatx80ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } shift128Right( aSig<<1, 0, 16, &zSig0, &zSig1 ); return packFloat128( aSign, aExp, zSig0, zSig1 ); @@ -4863,7 +4895,7 @@ float32 float128_to_float32( float128 a STATUS_PARAM ) aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 ) { - return commonNaNToFloat32( float128ToCommonNaN( a STATUS_VAR ) ); + return commonNaNToFloat32( float128ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloat32( aSign, 0xFF, 0 ); } @@ -4897,7 +4929,7 @@ float64 float128_to_float64( float128 a STATUS_PARAM ) aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 ) { - return commonNaNToFloat64( float128ToCommonNaN( a STATUS_VAR ) ); + return commonNaNToFloat64( float128ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloat64( aSign, 0x7FF, 0 ); } @@ -4932,7 +4964,7 @@ floatx80 float128_to_floatx80( float128 a STATUS_PARAM ) aSign = extractFloat128Sign( a ); if ( aExp == 0x7FFF ) { if ( aSig0 | aSig1 ) { - return commonNaNToFloatx80( float128ToCommonNaN( a STATUS_VAR ) ); + return commonNaNToFloatx80( float128ToCommonNaN( a STATUS_VAR ) STATUS_VAR ); } return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); } diff --git a/fpu/softfloat.h b/fpu/softfloat.h index 4a5345ceca..e57ee1efb0 100644 --- a/fpu/softfloat.h +++ b/fpu/softfloat.h @@ -120,23 +120,37 @@ enum { //#define USE_SOFTFLOAT_STRUCT_TYPES #ifdef USE_SOFTFLOAT_STRUCT_TYPES typedef struct { + uint16_t v; +} float16; +#define float16_val(x) (((float16)(x)).v) +#define make_float16(x) __extension__ ({ float16 f16_val = {x}; f16_val; }) +#define const_float16(x) { x } +typedef struct { uint32_t v; } float32; /* The cast ensures an error if the wrong type is passed. */ #define float32_val(x) (((float32)(x)).v) #define make_float32(x) __extension__ ({ float32 f32_val = {x}; f32_val; }) +#define const_float32(x) { x } typedef struct { uint64_t v; } float64; #define float64_val(x) (((float64)(x)).v) #define make_float64(x) __extension__ ({ float64 f64_val = {x}; f64_val; }) +#define const_float64(x) { x } #else +typedef uint16_t float16; typedef uint32_t float32; typedef uint64_t float64; +#define float16_val(x) (x) #define float32_val(x) (x) #define float64_val(x) (x) +#define make_float16(x) (x) #define make_float32(x) (x) #define make_float64(x) (x) +#define const_float16(x) (x) +#define const_float32(x) (x) +#define const_float64(x) (x) #endif #ifdef FLOATX80 typedef struct { @@ -253,8 +267,15 @@ float128 int64_to_float128( int64_t STATUS_PARAM ); /*---------------------------------------------------------------------------- | Software half-precision conversion routines. *----------------------------------------------------------------------------*/ -bits16 float32_to_float16( float32, flag STATUS_PARAM ); -float32 float16_to_float32( bits16, flag STATUS_PARAM ); +float16 float32_to_float16( float32, flag STATUS_PARAM ); +float32 float16_to_float32( float16, flag STATUS_PARAM ); + +/*---------------------------------------------------------------------------- +| Software half-precision operations. +*----------------------------------------------------------------------------*/ +int float16_is_quiet_nan( float16 ); +int float16_is_signaling_nan( float16 ); +float16 float16_maybe_silence_nan( float16 ); /*---------------------------------------------------------------------------- | Software IEC/IEEE single-precision conversion routines. |