我读过关于双精度和单精度之间的区别。然而,在大多数情况下,float和double似乎是可互换的,即使用其中一个似乎不会影响结果。事实真的如此吗?什么时候浮点数和双精度数可以互换?它们之间有什么区别?
当前回答
我刚刚遇到了一个错误,我花了很长时间才弄清楚,这可能会给你一个浮点精度的好例子。
#include <iostream>
#include <iomanip>
int main(){
for(float t=0;t<1;t+=0.01){
std::cout << std::fixed << std::setprecision(6) << t << std::endl;
}
}
输出为
0.000000
0.010000
0.020000
0.030000
0.040000
0.050000
0.060000
0.070000
0.080000
0.090000
0.100000
0.110000
0.120000
0.130000
0.140000
0.150000
0.160000
0.170000
0.180000
0.190000
0.200000
0.210000
0.220000
0.230000
0.240000
0.250000
0.260000
0.270000
0.280000
0.290000
0.300000
0.310000
0.320000
0.330000
0.340000
0.350000
0.360000
0.370000
0.380000
0.390000
0.400000
0.410000
0.420000
0.430000
0.440000
0.450000
0.460000
0.470000
0.480000
0.490000
0.500000
0.510000
0.520000
0.530000
0.540000
0.550000
0.560000
0.570000
0.580000
0.590000
0.600000
0.610000
0.620000
0.630000
0.640000
0.650000
0.660000
0.670000
0.680000
0.690000
0.700000
0.710000
0.720000
0.730000
0.740000
0.750000
0.760000
0.770000
0.780000
0.790000
0.800000
0.810000
0.820000
0.830000
0.839999
0.849999
0.859999
0.869999
0.879999
0.889999
0.899999
0.909999
0.919999
0.929999
0.939999
0.949999
0.959999
0.969999
0.979999
0.989999
0.999999
正如你所看到的,在0.83之后,精度显著下降。
然而,如果我将t设为双倍,这样的问题就不会发生。
我花了五个小时才意识到这个小错误,它毁了我的程序。
其他回答
以下是标准C99 (ISO-IEC 9899 6.2.5§10)或c++ 2003 (ISO-IEC 14882-2003 3.1.9§8)标准所说的:
浮点数有三种类型:浮点数、双精度浮点数和长双精度浮点数。double类型提供的精度至少与float类型相同,long double类型提供的精度至少与double类型相同。float类型的值集是double类型值集的子集;double类型的值集是long double类型值集的子集。
c++标准增加了:
浮点类型的值表示是由实现定义的。
我建议你看一看优秀的《每个计算机科学家都应该知道浮点算术》,它深入介绍了IEEE浮点标准。您将了解表示细节,并将意识到在量级和精度之间存在权衡。浮点表示的精度随着幅度的减小而增加,因此-1到1之间的浮点数具有最高的精度。
浮点数的精度比双精度数低。虽然你已经知道了,但为了更好地理解,请阅读《关于浮点算术我们应该知道什么》。
在数量上,正如其他答案所指出的,不同之处在于double类型的精度是float类型的两倍,范围是float类型的三倍(取决于你如何计算)。
但也许更重要的是质的差异。float类型具有良好的精度,无论您正在做什么,这通常都足够好。另一方面,Type double具有出色的精度,无论你在做什么,它几乎总是足够好。
结果是,几乎总是应该使用类型double,这一点并不广为人知。除非你有一些特别的需要,否则你几乎不应该使用float类型。
As everyone knows, "roundoff error" is often a problem when you're doing floating-point work. Roundoff error can be subtle, and difficult to track down, and difficult to fix. Most programmers don't have the time or expertise to track down and fix numerical errors in floating-point algorithms — because unfortunately, the details end up being different for every different algorithm. But type double has enough precision such that, much of the time, you don't have to worry. You'll get good results anyway. With type float, on the other hand, alarming-looking issues with roundoff crop up all the time.
And the thing that's not necessarily different between type float and double is execution speed. On most of today's general-purpose processors, arithmetic operations on type float and double take more or less exactly the same amount of time. Everything's done in parallel, so you don't pay a speed penalty for the greater range and precision of type double. That's why it's safe to make the recommendation that you should almost never use type float: Using double shouldn't cost you anything in speed, and it shouldn't cost you much in space, and it will almost definitely pay off handsomely in freedom from precision and roundoff error woes.
(尽管如此,当你在微控制器上进行嵌入式工作或编写针对GPU优化的代码时,你可能需要float类型的“特殊需求”之一。在这些处理器上,double类型可能会非常慢,或者几乎不存在,所以在这种情况下,程序员通常会选择float类型来提高速度,并可能为精度付出代价。)
如果使用嵌入式处理,最终底层硬件(例如FPGA或某些特定的处理器/微控制器模型)将在硬件中优化实现float,而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)。