转换字符串到浮点数,c++实现

visual studio - Convert string to float, C++ implementation

本文关键字:c++ 实现 浮点数 字符串 转换      更新时间:2023-10-16

VS最小双值= 2.2250738585072014e-308。Atof函数将字符串转换为双精度值,例如当您在调试器中查看此值时,您将获得原始字符串表示。

double d = atof("2.2250738585072014e-308");    // debugger will show 2.2250738585072014e-308

正如我们所看到的,double值不是反规范化的(没有DEN)

我尝试在将string转换为double时实现相同的精度。下面是代码:

double my_atof(char* digits, int digits_length, int ep)
{
int idot = digits_length;
for (int i = 0; i < digits_length; i++)
{
    if (digits[i] == '.')
    {
        idot = i;
        break;
    }
}
double accum = 0.0;
int power = ep + idot - 1;
for (int i = 0; i < digits_length; i++)
{
    if (digits[i] != '.')
    {
        if (digits[i] != '0')
        {
            double base_in_power = 1.0;
            if (power >= 0)
            {
                for (int k = 0; k < power; k++) base_in_power *= 10.0;
            }
            else if (power < 0)
            {
                for (int k = 0; k < -power; k++) base_in_power *= 0.1;
            }
            accum += (digits[i] - '0') * base_in_power;
        }
        power--;
    }
    else power = ep - 1;
}
return accum;
}

现在,让我们试试:

char* float_str = "2.2250738585072014";
int float_length = strlen(float_str);
double d = my_atof(float_str, float_length, -308);

调试显示d = 2.2250738585072379e-308。我试着替换

for (int k = 0; k < -power; k++) base_in_power *= 0.1;

for (int k = 0; k < -power; k++) base_in_power /= 10.0;

,但它会导致非规范化的值。如何实现与VS相同的精度,使调试器将显示相同的数字?

问题是0.1常数的双表示,或者除以10.0,产生完全相同的结果:10的负幂在浮点数中没有精确的表示,因为它们没有精确的表示为2的负幂的和。

当你通过重复乘法计算10的负幂时,你会累积误差。开始的几个负幂是正确的,但在0.000001之后,差异变得明显。运行这个程序看看发生了什么:

double p10[] = {
    0.1, 0.01, 0.001, 0.0001, 0.00001, 0.000001, 0.0000001, 0.00000001, 0.000000001, 0.0000000001
};
int main(void) {
    double a = 1;
    for (int i = 0 ; i != 10 ; i++) {
        double aa = a * 0.1;
        double d = aa - p10[i];
        printf("%d %.30lfn", aa == p10[i], d);
        a = aa;
    }
    return 0;
}

输出如下所示:

1 0.000000000000000000000000000000
1 0.000000000000000000000000000000
1 0.000000000000000000000000000000
1 0.000000000000000000000000000000
1 0.000000000000000000000000000000
0 0.000000000000000000000211758237
0 0.000000000000000000000026469780
0 0.000000000000000000000001654361
0 0.000000000000000000000000206795
0 0.000000000000000000000000025849

演示。

前几个幂完全匹配,但随后出现了一些差异。在字符串到浮点数的转换过程中,如果使用计算的幂来组合数字,则累积的错误会进入最终结果。如果库函数使用了一个查找表(参见这个示例实现),那么您得到的结果将与它们得到的结果不同。

你可以通过硬编码一个10的负幂表来修复你的实现,并引用这个表而不是手动计算幂。或者,您可以通过连续乘法构造一个正的十幂,然后做一个单除法1 / pow10来构造相应的负幂(演示)。