| /* | |
| * 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; | |
| } |