double和long double精度差异的原因是什么?
double和long double精度差异的原因是什么?
我读过双精度和单精度之间的差异。然而,在大多数情况下,float
和 double
看起来是可以互换的,也就是使用其中一个并不会影响结果。这是真的吗?何时可以互换 float 和 double?它们之间有什么区别?
C99标准(ISO-IEC 9899 6.2.5 §10)或C++2003标准(ISO-IEC 14882-2003 3.1.9 §8)中有如下规定:
有三种浮点类型:
float
、double
和long double
。类型double
提供的精度至少与float
相同,类型long double
提供的精度至少与double
相同。类型float
的值集是类型double
值集的子集;类型double
的值集是类型long double
值集的子集。
C++标准补充道:
浮点型的值表达式是实现定义的。
我建议您查看博学的计算机科学家应该知道的浮点算术,它深入讲解了IEEE浮点标准。您将了解表示细节,并意识到精度与数量之间存在权衡。浮点表示的精度随数量减少而增加,因此,-1到1之间的浮点数具有最高的精度。
巨大的差异。
顾名思义,double
比float
的精度高2倍[1]。一般情况下,double
的精度为15个小数位,而float
只有7个。
以下是数字位数的计算方法:
double
有52个尾数位+1个隐藏位:log(253) ÷ log(10) = 15.95个数字
float
有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
,所以在一些简单的计算(例如计算60的阶乘)中,使用float
更容易“溢出”(即成为一个特殊的浮点数)。
在测试过程中,可能会有一些测试用例包含这些巨大的数字,如果使用float
,程序可能会因此失败。
当然,有时候,即使使用double
也不够精确,因此我们有时会使用long double
[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)。