softfloat: Fix float64_to_uint64
The comment preceding the float64_to_uint64 routine suggests that the implementation is broken. And this is, indeed, the case. This patch properly implements the conversion of a 64-bit floating point number to an unsigned, 64 bit integer. This contribution can be licensed under either the softfloat-2a or -2b license. Signed-off-by: Tom Musta <tommusta@gmail.com> Reviewed-by: Peter Maydell <peter.maydell@linaro.org> Signed-off-by: Peter Maydell <peter.maydell@linaro.org> Reviewed-by: Richard Henderson <rth@twiddle.net>
This commit is contained in:
		
							parent
							
								
									564658dc8c
								
							
						
					
					
						commit
						f883b3896e
					
				| @ -203,6 +203,56 @@ static int64 roundAndPackInt64( flag zSign, uint64_t absZ0, uint64_t absZ1 STATU | |||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | /*----------------------------------------------------------------------------
 | ||||||
|  | | Takes the 128-bit fixed-point value formed by concatenating `absZ0' and | ||||||
|  | | `absZ1', with binary point between bits 63 and 64 (between the input words), | ||||||
|  | | and returns the properly rounded 64-bit unsigned integer corresponding to the | ||||||
|  | | input.  Ordinarily, the fixed-point input is simply rounded to an integer, | ||||||
|  | | with the inexact exception raised if the input cannot be represented exactly | ||||||
|  | | as an integer.  However, if the fixed-point input is too large, the invalid | ||||||
|  | | exception is raised and the largest unsigned integer is returned. | ||||||
|  | *----------------------------------------------------------------------------*/ | ||||||
|  | 
 | ||||||
|  | static int64 roundAndPackUint64(flag zSign, uint64_t absZ0, | ||||||
|  |                                 uint64_t absZ1 STATUS_PARAM) | ||||||
|  | { | ||||||
|  |     int8 roundingMode; | ||||||
|  |     flag roundNearestEven, increment; | ||||||
|  | 
 | ||||||
|  |     roundingMode = STATUS(float_rounding_mode); | ||||||
|  |     roundNearestEven = (roundingMode == float_round_nearest_even); | ||||||
|  |     increment = ((int64_t)absZ1 < 0); | ||||||
|  |     if (!roundNearestEven) { | ||||||
|  |         if (roundingMode == float_round_to_zero) { | ||||||
|  |             increment = 0; | ||||||
|  |         } else if (absZ1) { | ||||||
|  |             if (zSign) { | ||||||
|  |                 increment = (roundingMode == float_round_down) && absZ1; | ||||||
|  |             } else { | ||||||
|  |                 increment = (roundingMode == float_round_up) && absZ1; | ||||||
|  |             } | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (increment) { | ||||||
|  |         ++absZ0; | ||||||
|  |         if (absZ0 == 0) { | ||||||
|  |             float_raise(float_flag_invalid STATUS_VAR); | ||||||
|  |             return LIT64(0xFFFFFFFFFFFFFFFF); | ||||||
|  |         } | ||||||
|  |         absZ0 &= ~(((uint64_t)(absZ1<<1) == 0) & roundNearestEven); | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (zSign && absZ0) { | ||||||
|  |         float_raise(float_flag_invalid STATUS_VAR); | ||||||
|  |         return 0; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
|  |     if (absZ1) { | ||||||
|  |         STATUS(float_exception_flags) |= float_flag_inexact; | ||||||
|  |     } | ||||||
|  |     return absZ0; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| /*----------------------------------------------------------------------------
 | /*----------------------------------------------------------------------------
 | ||||||
| | Returns the fraction bits of the single-precision floating-point value `a'. | | Returns the fraction bits of the single-precision floating-point value `a'. | ||||||
| *----------------------------------------------------------------------------*/ | *----------------------------------------------------------------------------*/ | ||||||
| @ -6643,16 +6693,51 @@ uint_fast16_t float64_to_uint16_round_to_zero(float64 a STATUS_PARAM) | |||||||
|     return res; |     return res; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| /* FIXME: This looks broken.  */ | /*----------------------------------------------------------------------------
 | ||||||
|  | | Returns the result of converting the double-precision floating-point value | ||||||
|  | | `a' to the 64-bit unsigned integer format.  The conversion is | ||||||
|  | | performed according to the IEC/IEEE Standard for Binary Floating-Point | ||||||
|  | | Arithmetic---which means in particular that the conversion is rounded | ||||||
|  | | according to the current rounding mode.  If `a' is a NaN, the largest | ||||||
|  | | positive integer is returned.  If the conversion overflows, the | ||||||
|  | | largest unsigned integer is returned.  If 'a' is negative, the value is | ||||||
|  | | rounded and zero is returned; negative values that do not round to zero | ||||||
|  | | will raise the inexact exception. | ||||||
|  | *----------------------------------------------------------------------------*/ | ||||||
|  | 
 | ||||||
| uint64_t float64_to_uint64(float64 a STATUS_PARAM) | uint64_t float64_to_uint64(float64 a STATUS_PARAM) | ||||||
| { | { | ||||||
|     int64_t v; |     flag aSign; | ||||||
|  |     int_fast16_t aExp, shiftCount; | ||||||
|  |     uint64_t aSig, aSigExtra; | ||||||
|  |     a = float64_squash_input_denormal(a STATUS_VAR); | ||||||
| 
 | 
 | ||||||
|     v = float64_val(int64_to_float64(INT64_MIN STATUS_VAR)); |     aSig = extractFloat64Frac(a); | ||||||
|     v += float64_val(a); |     aExp = extractFloat64Exp(a); | ||||||
|     v = float64_to_int64(make_float64(v) STATUS_VAR); |     aSign = extractFloat64Sign(a); | ||||||
| 
 |     if (aSign && (aExp > 1022)) { | ||||||
|     return v - INT64_MIN; |         float_raise(float_flag_invalid STATUS_VAR); | ||||||
|  |         if (float64_is_any_nan(a)) { | ||||||
|  |             return LIT64(0xFFFFFFFFFFFFFFFF); | ||||||
|  |         } else { | ||||||
|  |             return 0; | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  |     if (aExp) { | ||||||
|  |         aSig |= LIT64(0x0010000000000000); | ||||||
|  |     } | ||||||
|  |     shiftCount = 0x433 - aExp; | ||||||
|  |     if (shiftCount <= 0) { | ||||||
|  |         if (0x43E < aExp) { | ||||||
|  |             float_raise(float_flag_invalid STATUS_VAR); | ||||||
|  |             return LIT64(0xFFFFFFFFFFFFFFFF); | ||||||
|  |         } | ||||||
|  |         aSigExtra = 0; | ||||||
|  |         aSig <<= -shiftCount; | ||||||
|  |     } else { | ||||||
|  |         shift64ExtraRightJamming(aSig, 0, shiftCount, &aSig, &aSigExtra); | ||||||
|  |     } | ||||||
|  |     return roundAndPackUint64(aSign, aSig, aSigExtra STATUS_VAR); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM) | uint64_t float64_to_uint64_round_to_zero (float64 a STATUS_PARAM) | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Tom Musta
						Tom Musta