C++ - float 和 double 的区别?
C++ - float 和 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++标准补充说:
浮点类型的值表示是实现定义的。
我建议您仔细阅读What Every Computer Scientist Should Know About Floating-Point Arithmetic,它深入介绍了IEEE浮点数标准。您将了解表示细节,并意识到有一个在数量级和精度之间取得的权衡。浮点表示的精度随数量级的降低而增加,因此-1和1之间的浮点数具有最高的精度。
巨大的差异。
正如其名称所示,double
的精度是float
的两倍[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
会更容易遇到“无穷大”(即一种特殊的浮点数)。
在测试过程中,有时会出现这些巨大的数字,如果使用浮点数,可能会导致程序失败。
当然,有时候,即使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)。