我读过关于双精度和单精度之间的区别。然而,在大多数情况下,float和double似乎是可互换的,即使用其中一个似乎不会影响结果。事实真的如此吗?什么时候浮点数和双精度数可以互换?它们之间有什么区别?


当前回答

给定二次方程:x2−4.0000000 x + 3.9999999 = 0, 10位有效数字的精确根为:r1 = 2.000316228, r2 = 1.999683772。

使用float和double,我们可以编写一个测试程序:

#include <stdio.h>
#include <math.h>

void dbl_solve(double a, double b, double c)
{
    double d = b*b - 4.0*a*c;
    double sd = sqrt(d);
    double r1 = (-b + sd) / (2.0*a);
    double r2 = (-b - sd) / (2.0*a);
    printf("%.5f\t%.5f\n", r1, r2);
}

void flt_solve(float a, float b, float c)
{
    float d = b*b - 4.0f*a*c;
    float sd = sqrtf(d);
    float r1 = (-b + sd) / (2.0f*a);
    float r2 = (-b - sd) / (2.0f*a);
    printf("%.5f\t%.5f\n", r1, r2);
}   

int main(void)
{
    float fa = 1.0f;
    float fb = -4.0000000f;
    float fc = 3.9999999f;
    double da = 1.0;
    double db = -4.0000000;
    double dc = 3.9999999;
    flt_solve(fa, fb, fc);
    dbl_solve(da, db, dc);
    return 0;
}  

运行程序得到:

2.00000 2.00000
2.00032 1.99968

注意,这些数字并不大,但是使用float仍然可以得到抵消效果。

(事实上,上面的方法并不是用单精度浮点数或双精度浮点数求解二次方程的最佳方法,但即使使用更稳定的方法,答案也不会改变。)

其他回答

内置比较操作的不同之处在于,当你用浮点数比较两个数字时,数据类型的差异(即浮点数或双精度数)可能会导致不同的结果。

双精度为64,单精度为64 (float)是32位。 double有一个更大的尾数(实数的整数位)。 任何不准确的地方都将在double中减小。

巨大的差异。

顾名思义,double的精度是浮点数[1]的2倍。一般来说,double有15个十进制数字的精度,而float有7个。

下面是如何计算位数的:

Double有52个尾数位+ 1个隐藏位:log(253)÷log(10) = 15.95位 浮点数有23个尾数位+ 1个隐藏位:log(224)÷log(10) = 7.22位数字

当重复计算时,这种精度损失可能导致更大的截断误差累积。

float a = 1.f / 81;
float b = 0;
for (int i = 0; i < 729; ++ i)
    b += a;
printf("%.7g\n", b); // prints 9.000023

double a = 1.0 / 81;
double b = 0;
for (int i = 0; i < 729; ++ i)
    b += a;
printf("%.15g\n", b); // prints 8.99999999999996

同样,float的最大值约为3e38,但double约为1.7e308,因此对于一些简单的事情,使用float可以比double更容易达到“无穷大”(即一个特殊的浮点数),例如计算60的阶乘。

在测试期间,可能有一些测试用例包含这些巨大的数字,如果使用浮点数,可能会导致程序失败。


当然,有时,即使是双精度也不够精确,因此我们有时会有长双精度[1](上面的例子在Mac上给出了9.000000000000000066),但所有浮点类型都有四舍五入错误,所以如果精度非常重要(例如货币处理),你应该使用int或分数类。


此外,不要使用+=对大量浮点数求和,因为错误很快就会累积起来。如果使用Python,请使用fsum。否则,尝试实现Kahan求和算法。


[1]: C和c++标准没有指定float、double和long double的表示方式。这三种方法都有可能实现为IEEE双精度。然而,对于大多数架构(gcc, MSVC;x86, x64, ARM) float确实是IEEE单精度浮点数(binary32), double是IEEE双精度浮点数(binary64)。

浮点数的精度比双精度数低。虽然你已经知道了,但为了更好地理解,请阅读《关于浮点算术我们应该知道什么》。

浮点计算中涉及的数字的大小并不是最相关的事情。相关的是正在进行的计算。

从本质上讲,如果您正在执行计算,而结果是一个无理数或循环小数,那么当将该数字压缩到您正在使用的有限大小的数据结构中时,将会出现舍入错误。因为double是float大小的两倍,所以舍入误差会小很多。

测试可能特别使用可能导致这种错误的数字,因此测试您是否在代码中使用了适当的类型。