summaryrefslogtreecommitdiff
path: root/src/std_dtoa.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/std_dtoa.c')
-rw-r--r--src/std_dtoa.c504
1 files changed, 504 insertions, 0 deletions
diff --git a/src/std_dtoa.c b/src/std_dtoa.c
new file mode 100644
index 0000000..943be3b
--- /dev/null
+++ b/src/std_dtoa.c
@@ -0,0 +1,504 @@
+/*
+ * 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;
+}