float和double有什么区别?


420

我已经读过双精度和单精度之间的区别。但是,在大多数情况下,float并且double似乎是可以互换的,即,使用其中一种似乎并不影响结果。真的是这样吗?花车和双打何时可以互换?它们之间有什么区别?

Answers:


521

差异很大。

顾名思义,a double的精度是[1]的 2倍。通常,a 的精度为15位小数,而a 的精度为7位。floatdoublefloat

计算位数的方法如下:

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,因此floatdouble简单的计算(例如计算60的阶乘),使用可以更容易地达到“无穷大”(即特殊的浮点数)。

在测试过程中,可能有几个测试用例包含这些庞大的数字,如果您使用浮点数,则可能导致程序失败。


当然,有时甚至double不够精确,因此我们有时会得到long double[1](上面的示例在Mac上为9.000000000000000066),但是所有浮点类型都存在舍入误差,因此精度非常重要(例如,金钱)处理),您应该使用int或分数类。


此外,+=由于误差会迅速累积,因此请勿用于对大量浮点数求和。如果您使用的是Python,请使用fsum。否则,尝试实现Kahan求和算法


[1]:C和C ++标准没有规定的表示floatdoublelong double。所有这三个都可能实现为IEEE双精度。但是,对于大多数体系结构(gcc,MSVC,x86,x64,ARM)float 确实是IEEE单精度浮点数(binary32),并且double IEEE双精度浮点数(binary64)。


9
求和的通常建议是在求和之前按幅度(最小的优先)对浮点数进行排序。
R .. GitHub停止帮助ICE,2010年

请注意,虽然C / C ++的float和double几乎总是IEEE单精度和双精度,但C / C ++的long double的可变性要大得多,具体取决于您的CPU,编译器和OS。有时它与double相同,有时是某些系统特定的扩展格式,有时是IEEE四精度。
plugwash

@ R..GitHubSTOPHELPINGICE:为什么?你能解释一下吗?
不确定

@InQusitive:例如,考虑一个数组,该数组由值2 ^ 24和后面的值1的2 ^ 24个重复组成。按顺序求和会产生2 ^ 24。反转产生2 ^ 25。当然,您可以举一些例子(例如,使其成为1的2 ^ 25重复),其中单个累加器的任何顺序最终都是灾难性的错误,但是在这种情况下,最小幅度优先是最好的。为了做得更好,您需要某种树。
R .. GitHub停止帮助ICE

56

标准C99(ISO-IEC 9899 6.2.5§10)或C ++ 2003(ISO-IEC 14882-2003 3.1.9§8)标准如下:

有三种浮点类型:floatdouble,和long double。该类型double至少提供与相同的精度float,而该类型long double至少提供与一样的精度double。该类型的值float集是该类型的值集的子集double;该类型的值double集是该类型的值集的子集long double

C ++标准增加了:

浮点类型的值表示形式是实现定义的。

我建议看一看优秀的每位计算机科学家应该了解的有关浮点算法的知识,该算法深入地涵盖了IEEE浮点标准。您将了解制图表达的详细信息,并且您将意识到在幅度和精度之间要进行权衡。浮点表示的精度随着幅度的减小而增加,因此-1和1之间的浮点数是精度最高的数字。


27

给定一个二次方程:x 2  − 4.0000000  x  + 3.9999999 = 0,精确到10个有效数字的根是r 1  = 2.000316228和r 2  = 1.999683772。

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

#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

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


19
  • 双精度为64,单精度(浮点数)为32位。
  • 双精度数的尾数较大(实数的整数位)。
  • 任何不准确之处都会在两倍中减少。

12

浮点计算中涉及的数字大小不是最相关的东西。相关的是正在执行的计算。

本质上,如果您正在执行计算并且结果是一个无理数或重复的小数,那么当该数字被压缩为您使用的有限大小的数据结构时,将出现舍入错误。由于double是float大小的两倍,因此舍入误差会小很多。

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


9

类型为float的32位长,精度为7位。虽然它可以存储非常大或非常小的范围(+/- 3.4 * 10 ^ 38或* 10 ^ -38)的值,但它只有7位有效数字。

类型为double的64位长,具有较大的范围(* 10 ^ + /-308)和15位精度。

long double类型的标称值为80位,尽管给定的编译器/ OS配对可以将其存储为12-16字节以用于对齐。长双数的指数非常大,应该具有19位数字的精度。微软以其无限的智慧将长整数限制为8个字节,与普通双倍相同。

一般来说,当您需要浮点值/变量时,只需使用double类型。默认情况下,表达式中使用的文字浮点值将被视为双精度,大多数返回浮点值的数学函数将返回双精度。如果只使用double,则可以避免很多麻烦和类型转换。



9

我刚遇到一个错误,使我花了很长时间才找出来,并可能为您提供浮点精度的一个很好的例子。

#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为double,则不会发生此类问题。

我花了五个小时才意识到这个小错误,这毁了我的程序。


4
只是为了确保:您的问题的解决方案应该首选使用int?如果要迭代100次,则应使用int而不是使用double进行计数
BlueTrin

8
double在这里使用不是一个好的解决方案。您可以int进行计数并进行内部乘法以获得浮点值。
理查德


3

使用浮点数时,您不能相信本地测试将与在服务器端进行的测试完全相同。您的本地系统以及最终测试的运行环境和编译器可能有所不同。在某些TopCoder竞赛中,我曾多次看到此问题,尤其是当您尝试比较两个浮点数时。


3

内置的比较操作有所不同,因为您将两个数字与浮点进行比较时,数据类型的差异(即,浮点数或双精度数)可能会导致不同的结果。


1

如果采用嵌入式处理,则最终的底层硬件(例如FPGA或某些特定的处理器/微控制器模型)将以最佳方式在硬件中实现浮动,而double会使用软件例程。因此,如果浮点数的精度足以应付需求,则程序将以浮点数执行的速度快一些,然后再执行两次。如其他答案所述,请注意累积错误。


-1

int(整数)不同,a float具有小数点,a 也具有小数点double。但是两者的区别是a的double详细程度是a的两倍float,这意味着它的小数点后的位数可以是原来的两倍。


4
这一点都不意味着。实际上,它表示整数十进制数字的两倍,并且是两倍以上。小数位数和精度之间的关系不是线性的:它取决于值:例如,0.5精确,而0.33333333333333333333则不是。
洛恩侯爵
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.