C++ 将字符串转换为float,C++;实施

C++ 将字符串转换为float,C++;实施,c++,visual-studio,floating-point,C++,Visual Studio,Floating Point,VS最小双值=2.2250738585072014e-308。atof函数将字符串转换为双精度值,例如,当您在调试器中查看此值时,将获得原始字符串表示形式 double d = atof("2.2250738585072014e-308"); // debugger will show 2.2250738585072014e-308 正如我们所见,double值没有非规范化(没有DEN) 我尝试在将字符串转换为double时达到相同的精度。代码如下: double my_atof(cha

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

double d = atof("2.2250738585072014e-308");    // debugger will show 2.2250738585072014e-308
正如我们所见,double值没有非规范化(没有DEN)

我尝试在将字符串转换为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;
}
调试器显示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*=0.1;

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

但它会导致非规范化值。如何达到与VS相同的精度,以便调试器显示相同的数字?

问题在于
0.1
常数的双重表示,或者除以
10.0
,这会产生完全相同的结果:十的负幂在浮点数中没有精确的表示,因为它们不能精确地表示为2的负幂和

当你通过重复乘法来计算十的负幂时,你会累积误差。最初的几个负幂是正确的,但大约在
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 %.30lf\n", 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的负幂的表,并引用该表而不是手动计算幂来修复您的实现。或者,您可以通过连续乘法构造10的正幂,然后进行单除法
1/pow10
来构造相应的负幂()。

调试器只是显示一个表示。根据调试器的不同,您可以更改此设置。如果你这么关心的话,你可以看看位模式。为什么*=0.1和/=10.0会给出不同的结果,而它们应该是相同的?@igntec,因为它们在有限精度二进制算术中不相同。阅读等人也读得很好:难道你不能通过增加10的幂来得到准确的结果吗?@MarkRansom对,这也行,因为计算10的正幂的乘法是精确的,然后一次除法就可以得到相应的负幂,而不会累积误差。我编辑了答案并链接了一个附加的演示。非常感谢你!
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 %.30lf\n", 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