浮动和双重之间的区别
我知道,我已经读过双精度和单精度之间的区别,但是他们应该给大多数情况下相同的结果吗?
我在编程竞赛中解决了一个问题,并且计算的浮点数并不是很大,所以我决定使用float而不是double,并且检查了它 – 我得到了正确的结果。 但是,当我发送解决方案时,它说只有十个测试中的一个是正确的。 我一遍又一遍地检查,直到我发现使用double的float不一样。 我把计算结果加倍,输出结果加倍,程序给出了相同的结果,但这次它正确地通过了所有10个测试。
我再说一遍,输出结果是相同的,结果是相同的,但是放置浮动不起作用 – 只有两倍。 这个数值并没有那么大,在同样的测试中,程序在浮点数和双精度数上都得到了相同的结果,但在线判断只接受了双精度解。
为什么? 有什么不同?
巨大的差异。
正如名字所暗示的, double
精度float
2 float
[1] 。 一般情况下, double
有15个十进制数字的精度,而float
有7个。
以下是如何计算数字的数字:
double
有52个尾数位+ 1个隐藏位:log(2 53 )÷log(10)= 15.95位
float
有23个尾数位+ 1个隐藏位:log(2 24 )÷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
可以比简单的计算60 factorial更容易打到Infinity。
也许他们的测试用例包含这些巨大的数字,导致你的程序失败。
当然,有时甚至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)。
以下是标准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之间是精度最高的浮点数。
给定一个二次方程: x 2 – 4.0000000 x + 3.9999999 = 0,到10个有效数字的确切根是r 1 = 2.000316228和r 2 = 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
取消效果。
(实际上,使用单精度浮点数或双精度浮点数求解二次方程不是最好的方法,但即使使用更稳定的方法 ,答案也不会改变。)
- double是64,单精度(float)是32位。
- 双数有一个更大的尾数(实数的整数位)。
- 任何不准确的地方都会小一些。
“有计算浮点数不是很大”
数字的大小是无关紧要的,正在执行的计算是相关的。
实际上,如果你正在执行一个计算,结果是一个无理数或重复小数,那么当这个数字被压缩到你正在使用的有限大小的数据结构中时,会出现舍入误差。 由于double是float的两倍,所以舍入误差会小很多。
在线测试可能特别使用了会导致此类错误的数字,因此测试了您在代码中使用了适当的类型。
漂浮比双打精度要低。 虽然你已经知道了,但是为了更好的理解,阅读我们应该了解的浮点算术 。
浮点型,32位长,精度为7位。 虽然它可以存储非常大或非常小的范围(+/- 3.4 * 10 ^ 38或* 10 ^ -38),它只有7位有效数字。
类型double,64位长,具有更大的范围(* 10 ^ + / – 308)和15位精度。
long double通常是80位,但是为了对齐的目的,给定的编译器/ OS配对可以将其存储为12-16字节。 长双数有一个可笑的巨大的指数,应该有19位数的精度。 M $以其无穷的智慧,将长双倍限制在8个字节,与普通双倍相同。
一般来说,当你需要一个浮点值/变量时,只需要使用double类型。 在表达式中使用的文字浮点值默认会被视为双精度值,大部分返回浮点值的数学函数都会返回双精度值。 如果你使用双倍的话,你将会节省很多头痛和模型。
当使用浮点数时,您不能相信您的本地测试将与服务器端完成的测试完全相同。 环境和编译器可能与您本地系统和最终测试的运行位置不同。 在TopCoder比赛中,我曾多次看到过这个问题,特别是当你试图比较两个浮点数时。
我只是遇到了一个让我永远不知道的错误,并可能给你一个浮点精度的好例子。
#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以后所看到的,精度显着下降。
不过,如果我设定为两倍,这样的问题就不会发生。
我花了五个小时才意识到这个小错误,这个错误毁了我的程序。
希望这是有帮助的。
内置比较操作与将2个数字与浮点数进行比较时不同,数据类型(即float或double)的差异可能会导致不同的结果。
与int
(整数)不同, float
具有小数点, double
也可以。 但是两者之间的区别在于, double
是float
两倍,意味着它可以在小数点后数字的两倍。