| /* |
| * Copyright (c) 2019, The Linux Foundation. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions are |
| * met: |
| * * Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * * Redistributions in binary form must reproduce the above |
| * copyright notice, this list of conditions and the following |
| * disclaimer in the documentation and/or other materials provided |
| * with the distribution. |
| * * Neither the name of The Linux Foundation nor the names of its |
| * contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED |
| * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF |
| * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT |
| * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS |
| * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR |
| * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF |
| * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR |
| * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE |
| * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN |
| * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| #include "AEEStdDef.h" |
| #include "AEEstd.h" |
| #include "AEEStdErr.h" |
| #include "std_dtoa.h" |
| #include "math.h" |
| |
| // |
| // Useful Macros |
| // |
| #define FAILED(b) ( (b) != AEE_SUCCESS ? TRUE : FALSE ) |
| #define CLEANUP_ON_ERROR(b,l) if( FAILED( b ) ) { goto l; } |
| #define FP_POW_10(n) fp_pow_10(n) |
| |
| static __inline |
| uint32 std_dtoa_clz32( uint32 ulVal ) |
| // |
| // This function returns the number of leading zeroes in a uint32. |
| // This is a naive implementation that uses binary search. This could be |
| // replaced by an optimized inline assembly code. |
| // |
| { |
| if( (int)ulVal <= 0 ) |
| { |
| return ( ulVal == 0 ) ? 32 : 0; |
| } |
| else |
| { |
| uint32 uRet = 28; |
| uint32 uTmp = 0; |
| uTmp = ( ulVal > 0xFFFF ) * 16; ulVal >>= uTmp, uRet -= uTmp; |
| uTmp = ( ulVal > 0xFF ) * 8; ulVal >>= uTmp, uRet -= uTmp; |
| uTmp = ( ulVal > 0xF ) * 4; ulVal >>= uTmp, uRet -= uTmp; |
| return uRet + ( ( 0x55AF >> ( ulVal * 2 ) ) & 3 ); |
| } |
| } |
| |
| static __inline |
| uint32 std_dtoa_clz64( uint64 ulVal ) |
| // |
| // This function returns the number of leading zeroes in a uint64. |
| // |
| { |
| uint32 ulCount = 0; |
| |
| if( !( ulVal >> 32 ) ) |
| { |
| ulCount += 32; |
| } |
| else |
| { |
| ulVal >>= 32; |
| } |
| |
| return ulCount + std_dtoa_clz32( (uint32)ulVal ); |
| } |
| |
| double fp_pow_10( int nPow ) |
| { |
| double dRet = 1.0; |
| int nI = 0; |
| boolean bNegative = FALSE; |
| double aTablePos[] = { 0, 1e1, 1e2, 1e4, 1e8, 1e16, 1e32, 1e64, 1e128, |
| 1e256 }; |
| double aTableNeg[] = { 0, 1e-1, 1e-2, 1e-4, 1e-8, 1e-16, 1e-32, 1e-64, 1e-128, |
| 1e-256 }; |
| double* pTable = aTablePos; |
| int nTableSize = STD_ARRAY_SIZE( aTablePos ); |
| |
| if( 0 == nPow ) |
| { |
| return 1.0; |
| } |
| |
| if( nPow < 0 ) |
| { |
| bNegative = TRUE; |
| nPow = -nPow; |
| pTable = aTableNeg; |
| nTableSize = STD_ARRAY_SIZE( aTableNeg ); |
| } |
| |
| for( nI = 1; nPow && (nI < nTableSize); nI++ ) |
| { |
| if( nPow & 1 ) |
| { |
| dRet *= pTable[nI]; |
| } |
| |
| nPow >>= 1; |
| } |
| |
| if( nPow ) |
| { |
| // Overflow. Trying to compute a large power value. |
| uint64 ulInf = STD_DTOA_FP_POSITIVE_INF; |
| dRet = bNegative ? 0 : UINT64_TO_DOUBLE( ulInf ); |
| } |
| |
| return dRet; |
| } |
| |
| double fp_round( double dNumber, int nPrecision ) |
| // |
| // This functions rounds dNumber to the specified precision nPrecision. |
| // For example: |
| // fp_round(2.34553, 3) = 2.346 |
| // fp_round(2.34553, 4) = 2.3455 |
| // |
| { |
| double dResult = dNumber; |
| double dRoundingFactor = FP_POW_10( -nPrecision ) * 0.5; |
| |
| if( dNumber < 0 ) |
| { |
| dResult = dNumber - dRoundingFactor; |
| } |
| else |
| { |
| dResult = dNumber + dRoundingFactor; |
| } |
| |
| return dResult; |
| } |
| |
| int fp_log_10( double dNumber ) |
| // |
| // This function finds the integer part of the log_10( dNumber ). |
| // The function assumes that dNumber != 0. |
| // |
| { |
| // Absorb the negative sign |
| if( dNumber < 0 ) |
| { |
| dNumber = -dNumber; |
| } |
| |
| return (int)( floor( log10( dNumber ) ) ); |
| } |
| |
| int fp_check_special_cases( double dNumber, FloatingPointType* pNumberType ) |
| // |
| // This function evaluates the input floating-point number dNumber to check for |
| // following special cases: NaN, +/-Infinity. |
| // The evaluation is based on the IEEE Standard 754 for Floating Point Numbers |
| // |
| { |
| int nError = AEE_SUCCESS; |
| FloatingPointType NumberType = FP_TYPE_UNKOWN; |
| uint64 ullValue = 0; |
| uint64 ullSign = 0; |
| int64 n64Exponent = 0; |
| uint64 ullMantissa = 0; |
| |
| ullValue = DOUBLE_TO_UINT64( dNumber ); |
| |
| // Extract the sign, exponent and mantissa |
| ullSign = FP_SIGN( ullValue ); |
| n64Exponent = FP_EXPONENT_BIASED( ullValue ); |
| ullMantissa = FP_MANTISSA_DENORM( ullValue ); |
| |
| // |
| // Rules for special cases are listed below: |
| // For Infinity, the following needs to be true: |
| // 1. Exponent should have all bits set to 1. |
| // 2. Mantissa should have all bits set to 0. |
| // |
| // For NaN, the following needs to be true: |
| // 1. Exponent should have all bits set to 1. |
| // 2. Mantissa should be non-zero. |
| // Note that we do not differentiate between QNaNs and SNaNs. |
| // |
| if( STD_DTOA_DP_INFINITY_EXPONENT_ID == n64Exponent ) |
| { |
| if( 0 == ullMantissa ) |
| { |
| // Inifinity. |
| if( ullSign ) |
| { |
| NumberType = FP_TYPE_NEGATIVE_INF; |
| } |
| else |
| { |
| NumberType = FP_TYPE_POSITIVE_INF; |
| } |
| } |
| else |
| { |
| // NaN |
| NumberType = FP_TYPE_NAN; |
| } |
| } |
| else |
| { |
| // A normal number |
| NumberType = FP_TYPE_GENERAL; |
| } |
| |
| // Set the output value |
| *pNumberType = NumberType; |
| |
| return nError; |
| } |
| |
| int std_dtoa_decimal( double dNumber, int nPrecision, |
| char acIntegerPart[ STD_DTOA_FORMAT_INTEGER_SIZE ], |
| char acFractionPart[ STD_DTOA_FORMAT_FRACTION_SIZE ] ) |
| { |
| int nError = AEE_SUCCESS; |
| boolean bNegativeNumber = FALSE; |
| double dIntegerPart = 0.0; |
| double dFractionPart = 0.0; |
| double dTempIp = 0.0; |
| double dTempFp = 0.0; |
| int nMaxIntDigs = STD_DTOA_FORMAT_INTEGER_SIZE; |
| uint32 ulI = 0; |
| int nIntStartPos = 0; |
| |
| // Optimization: Special case an input of 0 |
| if( 0.0 == dNumber ) |
| { |
| acIntegerPart[0] = '0'; |
| acIntegerPart[1] = '\0'; |
| |
| for( ulI = 0; (ulI < STD_DTOA_FORMAT_FRACTION_SIZE - 1) && (nPrecision > 0); |
| ulI++, nPrecision-- ) |
| { |
| acFractionPart[ulI] = '0'; |
| } |
| acFractionPart[ ulI ] = '\0'; |
| |
| goto bail; |
| } |
| |
| // Absorb the negative sign |
| if( dNumber < 0 ) |
| { |
| acIntegerPart[0] = '-'; |
| nIntStartPos = 1; |
| dNumber = -dNumber; |
| bNegativeNumber = TRUE; |
| } |
| |
| // Split the input number into it's integer and fraction parts |
| dFractionPart = modf( dNumber, &dIntegerPart ); |
| |
| // First up, convert the integer part |
| if( 0.0 == dIntegerPart ) |
| { |
| acIntegerPart[ nIntStartPos ] = '0'; |
| } |
| else |
| { |
| double dRoundingConst = FP_POW_10( -STD_DTOA_PRECISION_ROUNDING_VALUE ); |
| int nIntDigs = 0; |
| int nI = 0; |
| |
| // Compute the number of digits in the integer part of the number |
| nIntDigs = fp_log_10( dIntegerPart ) + 1; |
| |
| // For negative numbers, a '-' sign has already been written. |
| if( TRUE == bNegativeNumber ) |
| { |
| nIntDigs++; |
| } |
| |
| // Check for overflow |
| if( nIntDigs >= nMaxIntDigs ) |
| { |
| // Overflow! |
| // Note that currently, we return a simple AEE_EFAILED for all |
| // errors. |
| nError = AEE_EFAILED; |
| goto bail; |
| } |
| |
| // Null Terminate the string |
| acIntegerPart[ nIntDigs ] = '\0'; |
| |
| for( nI = nIntDigs - 1; nI >= nIntStartPos; nI-- ) |
| { |
| dIntegerPart = dIntegerPart / 10.0; |
| dTempFp = modf( dIntegerPart, &dTempIp ); |
| |
| // Round it to the a specific precision |
| dTempFp = dTempFp + dRoundingConst; |
| |
| // Convert the digit to a character |
| acIntegerPart[ nI ] = (int)( dTempFp * 10 ) + '0'; |
| if( !MY_ISDIGIT( acIntegerPart[ nI ] ) ) |
| { |
| // Overflow! |
| // Note that currently, we return a simple AEE_EFAILED for all |
| // errors. |
| nError = AEE_EFAILED; |
| goto bail; |
| } |
| dIntegerPart = dTempIp; |
| } |
| } |
| |
| // Just a double check for integrity sake. This should ideally never happen. |
| // Out of bounds scenario. That is, the integer part of the input number is |
| // too large. |
| if( dIntegerPart != 0.0 ) |
| { |
| // Note that currently, we return a simple AEE_EFAILED for all |
| // errors. |
| nError = AEE_EFAILED; |
| goto bail; |
| } |
| |
| // Now, convert the fraction part |
| for( ulI = 0; ( nPrecision > 0 ) && ( ulI < STD_DTOA_FORMAT_FRACTION_SIZE - 1 ); |
| nPrecision--, ulI++ ) |
| { |
| if( 0.0 == dFractionPart ) |
| { |
| acFractionPart[ ulI ] = '0'; |
| } |
| else |
| { |
| double dRoundingValue = FP_POW_10( -( nPrecision + |
| STD_DTOA_PRECISION_ROUNDING_VALUE ) ); |
| acFractionPart[ ulI ] = (int)( ( dFractionPart + dRoundingValue ) * 10.0 ) + '0'; |
| if( !MY_ISDIGIT( acFractionPart[ ulI ] ) ) |
| { |
| // Overflow! |
| // Note that currently, we return a simple AEE_EFAILED for all |
| // errors. |
| nError = AEE_EFAILED; |
| goto bail; |
| } |
| |
| dFractionPart = ( dFractionPart * 10.0 ) - |
| (int)( ( dFractionPart + FP_POW_10( -nPrecision - 6 ) ) * 10.0 ); |
| } |
| } |
| |
| |
| bail: |
| |
| return nError; |
| } |
| |
| int std_dtoa_hex( double dNumber, int nPrecision, char cFormat, |
| char acIntegerPart[ STD_DTOA_FORMAT_INTEGER_SIZE ], |
| char acFractionPart[ STD_DTOA_FORMAT_FRACTION_SIZE ], |
| int* pnExponent ) |
| { |
| int nError = AEE_SUCCESS; |
| uint64 ullMantissa = 0; |
| uint64 ullSign = 0; |
| int64 n64Exponent = 0; |
| static const char HexDigitsU[] = "0123456789ABCDEF"; |
| static const char HexDigitsL[] = "0123456789abcde"; |
| boolean bFirstDigit = TRUE; |
| int nI = 0; |
| int nF = 0; |
| uint64 ullValue = DOUBLE_TO_UINT64( dNumber ); |
| int nManShift = 0; |
| const char *pcDigitArray = ( cFormat == 'A' ) ? HexDigitsU : HexDigitsL; |
| boolean bPrecisionSpecified = TRUE; |
| |
| // If no precision is specified, then set the precision to be fairly |
| // large. |
| if( nPrecision < 0 ) |
| { |
| nPrecision = STD_DTOA_FORMAT_FRACTION_SIZE; |
| bPrecisionSpecified = FALSE; |
| } |
| else |
| { |
| bPrecisionSpecified = TRUE; |
| } |
| |
| // Extract the sign, exponent and mantissa |
| ullSign = FP_SIGN( ullValue ); |
| n64Exponent = FP_EXPONENT( ullValue ); |
| ullMantissa = FP_MANTISSA( ullValue ); |
| |
| // Write out the sign |
| if( ullSign ) |
| { |
| acIntegerPart[ nI++ ] = '-'; |
| } |
| |
| // Optimization: Special case an input of 0 |
| if( 0.0 == dNumber ) |
| { |
| acIntegerPart[0] = '0'; |
| acIntegerPart[1] = '\0'; |
| |
| for( nF = 0; (nF < STD_DTOA_FORMAT_FRACTION_SIZE - 1) && (nPrecision > 0); |
| nF++, nPrecision-- ) |
| { |
| acFractionPart[nF] = '0'; |
| } |
| acFractionPart[nF] = '\0'; |
| |
| goto bail; |
| } |
| |
| // The mantissa is in lower 53 bits (52 bits + an implicit 1). |
| // If we are dealing with a denormalized number, then the implicit 1 |
| // is absent. The above macros would have then set that bit to 0. |
| // Shift the mantisaa on to the highest bits. |
| |
| if( 0 == ( n64Exponent + STD_DTOA_DP_EXPONENT_BIAS ) ) |
| { |
| // DENORMALIZED NUMBER. |
| // A denormalized number is of the form: |
| // 0.bbb...bbb x 2^Exponent |
| // Shift the mantissa to the higher bits while discarding the leading 0 |
| ullMantissa <<= 12; |
| |
| // Lets update the exponent so as to make sure that the first hex value |
| // in the mantissa is non-zero, i.e., at least one of the first 4 bits is |
| // non-zero. |
| nManShift = std_dtoa_clz64( ullMantissa ) - 3; |
| if( nManShift > 0 ) |
| { |
| ullMantissa <<= nManShift; |
| n64Exponent -= nManShift; |
| } |
| } |
| else |
| { |
| // NORMALIZED NUMBER. |
| // A normalized number has the following form: |
| // 1.bbb...bbb x 2^Exponent |
| // Shift the mantissa to the higher bits while retaining the leading 1 |
| ullMantissa <<= 11; |
| } |
| |
| // Now, lets get the decimal point out of the picture by shifting the |
| // exponent by 1. |
| n64Exponent++; |
| |
| // Read the mantissa four bits at a time to form the hex output |
| for( nI = 0, nF = 0, bFirstDigit = TRUE; ullMantissa != 0; |
| ullMantissa <<= 4 ) |
| { |
| uint64 ulHexVal = ullMantissa & 0xF000000000000000uLL; |
| ulHexVal >>= 60; |
| if( bFirstDigit ) |
| { |
| // Write to the integral part of the number |
| acIntegerPart[ nI++ ] = pcDigitArray[ulHexVal]; |
| bFirstDigit = FALSE; |
| } |
| else if( nF < nPrecision ) |
| { |
| // Write to the fractional part of the number |
| acFractionPart[ nF++ ] = pcDigitArray[ulHexVal]; |
| } |
| } |
| |
| // Pad the fraction with trailing zeroes upto the specified precision |
| for( ; bPrecisionSpecified && (nF < nPrecision); nF++ ) |
| { |
| acFractionPart[ nF ] = '0'; |
| } |
| |
| // Now the output is of the form; |
| // h.hhh x 2^Exponent |
| // where h is a non-zero hexadecimal number. |
| // But we were dealing with a binary fraction 0.bbb...bbb x 2^Exponent. |
| // Therefore, we need to subtract 4 from the exponent (since the shift |
| // was to the base 16 and the exponent is to the base 2). |
| n64Exponent -= 4; |
| *pnExponent = (int)n64Exponent; |
| |
| bail: |
| return nError; |
| } |