简体   繁体   中英

How to convert double to char[] by myself in c++?

Since i have to convert lots of double to char[] very fast, after step into sprintf , i found that _cfltcvt_l did the real conversion work, and before _cfltcvt_l is called, it has many switch , case , validate ...function that i don't really need, so i want to convert double to char[] by myself.

I want to get the same result as sprintf(c,"%16.9E",d); does, for example:

d=49.9999999995; --> c={"5.000000000E+001"}

d=2.5323867855e+298; --> c={"2.532386786e+298"}

Here is my code:

#include<iostream>
using namespace std;
void fast_sprintf(char* c, double d)
{
    int i = 0, e = 0, n = 0, flag = 0;//flag=0E+;1E-

    if (d < 0)
    {
        c[i++] = '-';
        d = -d;
    }
    while (d >= 10)
    {
        d /= 10;//here is the problem
        e++;
    }
    while (d < 1)
    {
        d *= 10;
        e++;
        flag = 1;
    }
    int v = d, dot;
    c[i++] = '0' + v;//the integer part
    dot = i;
    n++;
    c[i++] = '.';
    d -= v;
    while (d != 0 && n < 10)
    {
        d *= 10;
        v = d;
        c[i++] = '0' + v;
        n++;
        d -= v;
    }
    if (d != 0)
    {

        if (d * 10 >= 5)//rounding
        {
            int j = i - 1;
            c[j]++;
            while (c[j]>'9')
            {
                c[j] = '0';
                if (j - 1 == dot)
                    j--;
                c[--j]++;
            }
        }
    }
    else
    {
        while (n < 10)
        {
            c[i++] = '0';
            n++;
        }
    }

    c[i++] = 'E';
    c[i++] = (flag == 0) ? '+' : '-';
    if (e >= 100)
    {
        int tmp = e / 100;
        c[i++] = '0' + tmp;
        e -= (tmp*100);
        c[i++] = '0' + e / 10;
        c[i++] = '0' + e % 10;
    }
    else if (e <= 9)
    {
        c[i++] = '0';
        c[i++] = '0';
        c[i++] = '0' + e;
    }
    else
    {
        c[i++] = '0';
        c[i++] = '0' + e / 10;
        c[i++] = '0' + e % 10;
    }
    c[i] = '\0';
}

int main()
{
    char c[20];
    //double d=49.9999999995;
    double d=2.5323867855e+298;

    sprintf(c,"%16.9E",d);
    cout<<c<<endl;

    fast_sprintf(c,d);
    cout<<c<<endl;
    return 0;
}

But when d=2.5323867855e+298, c={"2.53238678 5 e+298"} instead of c={"2.53238678 6 e+298"}

That's because after this loop:

//before loop, d=2.5323867855000001e+298
while (d >= 10)
    {
        d /= 10;//here is the problem
        e++;
    }
//after loop, d=2.5323867854999969

d loses its precision, the last digit 5 becomes 4999969 .

So how can i fix my code or is there a better way to implement another sprintf(c,"%16.9E",d); ?

Thanks.

The reference document for these conversions is &ldqou;How to Print Floating-Point Numbers Accurately”, by Steele and White; it was published in PLDI '90, and is available on line from the ACM digital library.

Given that you're interested in speed and a fixed format, with 10 digits, a somewhat simpler approach might be to scale the number to the range [1E9...1E10) , then use modf to extract the integer part, incrementing it if the fractional part is greater than 0.5 . Then convert the integral part to an int , and generate your digits from that. The amount you've scaled it by should give the exponent.

Finding the value to scale it by could be a critical issue; using log10 is an obvious solution, provided it's accurate in your library. Otherwise, you could use a table of powers of ten (calculated once, off line), and a second table, indexed by the exponent (see frexp ) into this table. Using this technique, you'd have at most two comparisons to find the correct scaling factor. (Functions like modf and frexp are generally very fast, because they manipulate the bit pattern of the number directly; if they don't, it's easy to do what you need by manipulating the bit pattern of your double yourself.)

This is, of course, just a general suggestion of an approach. The devil is in the details, and it will take a lot of care to get all of the corner cases right. But there is a good chance that it will be faster than sprintf , precisely because it only does this one particular format.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM