blob: 943be3b99cc553d8663655ec0780789aad7e6606 [file] [log] [blame]
/*
* 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;
}