How to convert double to string using only math.h?

I am trying to convert double to string in a native NT application, i.e. an application that depends only on ntdll.dll. Unfortunately, the ntdll version does vsnprintfnot support %f, etc., Forcing me to implement the conversion myself.

The above-mentioned ntdll.dllexports only some of the functions math.h( floor, ceil, log, pow, ...). However, I am sure that I can implement any of the inaccessible functions math.h, if necessary.

There is an implementation for floating point conversion in GNU libc, but the code is extremely dense and hard to understand (the GNU indentation style doesn't help here).

I already implemented the conversion by normalizing the number (i.e., multiplying / dividing the number by 10 until it is in the interval [1, 10)), and then generate each digit, reducing the integral part with modfand multiplying the fractional part by 10. This works , but there is a loss of accuracy (only the first 15 digits are correct). Loss of accuracy is, of course, inherent in the algorithm.

I would agree with 17 digits, but an algorithm that could generate an arbitrary number of digits would be preferable.

Could you suggest an algorithm or show me a good resource?

+3
source share
7 answers

. , . : , , , ACM.

2006 , , - . ( ) .

+3

15 () . " "; double bignums.

, 17 , long double; Windows, , 19 .

+5

. , 10 ( [1,10], [0,1), ). 2, , " " * 2 ^ e; bcd , .

, g=m*2^e : h=floor(g*10^k) i=modf(g*10^k) k, , , : "long double" (80 , , Visual ++ ?) 17 .

_gcvt ( - ntdll.dll, msvcrt *.dll?)

, IEEE binary64 52 . 52 * log10 (2) = 15.65... (: , 16 )

+4
#include <cstdint>

// --------------------------------------------------------------------------
// Return number of decimal-digits of a given unsigned-integer
// N is unit8_t/uint16_t/uint32_t/uint64_t
template <class N> inline uint8_t GetUnsignedDecDigits(const N n)
{
    static_assert(std::numeric_limits<N>::is_integer && !std::numeric_limits<N>::is_signed,
                  "GetUnsignedDecDigits: unsigned integer type expected"                   );

    const uint8_t anMaxDigits[]= {3, 5, 8, 10, 13, 15, 17, 20};
    const uint8_t nMaxDigits   = anMaxDigits[sizeof(N)-1];

    uint8_t nDigits=  1;
    N       nRoof  = 10;

    while ((n >= nRoof) && (nDigits<nMaxDigits))
    {
        nDigits++;
        nRoof*= 10;
    }

    return nDigits;
}

// --------------------------------------------------------------------------
// Convert floating-point value to NULL-terminated string represention
TCHAR* DoubleToStr(double f       ,  // [i  ]
                   TCHAR* pczStr  ,  // [i/o] caller should allocate enough space
                   int    nDigitsI,  // [i  ] digits of integer    part including sign / <1: auto
                   int    nDigitsF ) // [i  ] digits of fractional part                / <0: auto
{
    switch (_fpclass(f))
    {
        case _FPCLASS_SNAN:
        case _FPCLASS_QNAN: _tcscpy_s(pczStr, 5, _T("NaN" )); return pczStr;
        case _FPCLASS_NINF: _tcscpy_s(pczStr, 5, _T("-INF")); return pczStr;
        case _FPCLASS_PINF: _tcscpy_s(pczStr, 5, _T("+INF")); return pczStr;
    }

    if (nDigitsI> 18) nDigitsI= 18;  if (nDigitsI< 1) nDigitsI= -1;
    if (nDigitsF> 18) nDigitsF= 18;  if (nDigitsF< 0) nDigitsF= -1;

    bool bNeg= (f<0);
    if (f<0)
        f= -f;

    int nE= 0;                                  // exponent (displayed if != 0)

    if ( ((-1 == nDigitsI) && (f >= 1e18              )) ||   // large value: switch to scientific representation
         ((-1 != nDigitsI) && (f >= pow(10., nDigitsI)))    )
    {
       nE= (int)log10(f);
       f/= (double)pow(10., nE);

       if (-1 != nDigitsF)
           nDigitsF= __max(nDigitsF, nDigitsI+nDigitsF-(bNeg?2:1)-4);

       nDigitsI= (bNeg?2:1);
    }
    else if (f>0)
    if ((-1 == nDigitsF) && (f <= 1e-10))       // small value: switch to scientific representation
    {
        nE= (int)log10(f)-1;
        f/= (double)pow(10., nE);

       if (-1 != nDigitsF)
           nDigitsF= __max(nDigitsF, nDigitsI+nDigitsF-(bNeg?2:1)-4);

        nDigitsI= (bNeg?2:1);
    }

    double fI;
    double fF= modf(f, &fI);                    // fI: integer part, fF: fractional part

    if (-1 == nDigitsF)                         // figure out number of meaningfull digits in fF
    {
        double fG, fGI, fGF;
        do
        {
            nDigitsF++;
            fG = fF*pow(10., nDigitsF);
            fGF= modf(fG, &fGI);
        }
        while (fGF > 1e-10);
    }

    const double afPower10[20]= {1e0 , 1e1 , 1e2 , 1e3 , 1e4 , 1e5 , 1e6 , 1e7 , 1e8 , 1e9 , 
                                 1e10, 1e11, 1e12, 1e13, 1e14, 1e15, 1e16, 1e17, 1e18, 1e19 };

    uint64_t uI= (uint64_t)round(fI                    );
    uint64_t uF= (uint64_t)round(fF*afPower10[nDigitsF]);

    if (uF)
        if (GetUnsignedDecDigits(uF) > nDigitsF)    // X.99999 was rounded to X+1
        {
            uF= 0;
            uI++;

            if (nE)
            {
                uI/= 10;
                nE++;
            }
        }

    uint8_t nRealDigitsI= GetUnsignedDecDigits(uI);
    if (bNeg)
        nRealDigitsI++;

    int nPads= 0;

    if (-1 != nDigitsI)
    {
        nPads= nDigitsI-nRealDigitsI;

        for (int i= nPads-1; i>=0; i--)         // leading spaces
            pczStr[i]= _T(' ');
    }

    if (bNeg)                                   // minus sign
    {
        pczStr[nPads]= _T('-');
        nRealDigitsI--;
        nPads++;
    }

    for (int j= nRealDigitsI-1; j>=0; j--)      // digits of integer    part
    {
        pczStr[nPads+j]= (uint8_t)(uI%10) + _T('0');
        uI /= 10;
    }

    nPads+= nRealDigitsI;

    if (nDigitsF)
    {
        pczStr[nPads++]= _T('.');               // decimal point
        for (int k= nDigitsF-1; k>=0; k--)      // digits of fractional part
        {
            pczStr[nPads+k]= (uint8_t)(uF%10)+ _T('0');
            uF /= 10;
        }
    }

    nPads+= nDigitsF;

    if (nE)
    {
        pczStr[nPads++]= _T('e');               // exponent sign

        if (nE<0)
        {
            pczStr[nPads++]= _T('-');
            nE= -nE;
        }
        else
            pczStr[nPads++]= _T('+');

        for (int l= 2; l>=0; l--)               // digits of exponent
        {
            pczStr[nPads+l]= (uint8_t)(nE%10) + _T('0');
            nE /= 10;
        }

        pczStr[nPads+3]= 0;
    }
    else
        pczStr[nPads]= 0;

    return pczStr;
}
+2

vsnprintf I64?

double x = SOME_VAL; // allowed to be from -1.e18 to 1.e18
bool sign = (SOME_VAL < 0);
if ( sign ) x = -x;
__int64 i = static_cast<__int64>( x );
double xm = x - static_cast<double>( i );
__int64 w = static_cast<__int64>( xm*pow(10.0, DIGITS_VAL) ); // DIGITS_VAL indicates how many digits after the decimal point you want to get

char out[100];
vsnprintf( out, sizeof out, "%s%I64.%I64", (sign?"-":""), i, w );

- gcvt.

+1

uClibc- printf?

+1

Source: https://habr.com/ru/post/1717863/


All Articles