【问题标题】:How to convert double to char[] by myself in c++?如何在c ++中自己将double转换为char []?
【发布时间】:2015-04-10 08:35:24
【问题描述】:

由于我必须非常快地将大量的 double 转换为 char[],在进入 sprintf 之后,我发现 _cfltcvt_l 完成了真正的转换工作,而在调用 _cfltcvt_l 之前,它有很多 @987654329 @,case,validate...函数我真的不需要,所以我想自己将double转换为char[]。

我想得到和sprintf(c,"%16.9E",d);does一样的结果,例如:

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

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

这是我的代码:

#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;
}

但是当 d=2.5323867855e+298 时,c={"2.532386785e+298"} 而不是 c={"2.532386786e+298"}

那是因为在这个循环之后:

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

d 失去精度,最后一位 5 变为 4999969

那么我该如何修复我的代码,或者有更好的方法来实现另一个sprintf(c,"%16.9E",d);

谢谢。

【问题讨论】:

标签: c++ format printf type-conversion double


【解决方案1】:

这些转换的参考文档是 &ldqou;How to 准确地打印浮点数”,Steele 和 白色的;它发表于 PLDI '90,可在线获取 来自 ACM 数字图书馆。

鉴于您对速度和固定格式感兴趣,使用 10 位数,一种更简单的方法可能是缩放 编号到[1E9...1E10) 范围内,然后使用modf 提取 整数部分,如果小数部分是则递增 大于0.5。然后将整数部分转换为int, 并从中生成您的数字。你缩放的数量 by 应该给出指数。

找到扩展它的价值可能是一个关键问题; 使用log10 是一个明显的解决方案,只要它在 你的图书馆。否则,您可以使用十的幂表 (离线计算一次)和第二个表,由 指数(见frexp)到这个表中。使用这种技术, 您最多需要进行两次比较才能找到正确的缩放比例 因素。 (modffrexp 之类的函数通常非常 快,因为他们操纵数字的位模式 直接地;如果他们不这样做,很容易做你需要的 自己操作double 的位模式。)

当然,这只是一种方法的一般建议。 魔鬼在细节中,需要非常小心 正确处理所有极端情况。但有一个很好的机会 它会比sprintf 更快,正是因为它 只有这一种特定的格式。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-28
    • 1970-01-01
    • 2011-11-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多