float和double有什么区别?

问题描述 投票:361回答:12

我已经读过双精度和单精度之间的区别。然而,在大多数情况下,floatdouble似乎是可以互换的,即使用一个或另一个似乎不会影响结果。这是真的吗?浮动和双打什么时候可以互换?它们之间有什么区别?

c++ c floating-point precision
12个回答
460
投票

巨大的差异。

顾名思义,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,所以使用float可以比double更容易点击“无穷大”(即一个特殊的浮点数),例如:计算60的阶乘。

在测试期间,可能有一些测试用例包含这些巨大的数字,如果您使用浮点数,可能会导致程序失败。


当然,有时甚至double都不够精确,因此我们有时会有long double [1](上面的例子在Mac上给出9.000000000000000066),但是所有浮点类型都有四舍五入的错误,所以如果精度非常重要(例如,货币处理)你应该使用int或分数类。


此外,不要使用+=来汇总大量浮点数,因为错误会很快累积。如果您使用的是Python,请使用fsum。否则,尝试实施Kahan summation algorithm


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


2
投票

内置比较操作的不同之处在于,当您将2个数字与浮点数进行比较时,数据类型的差异(即浮点数或双精度数)可能会导致不同的结果。


-1
投票

int(整数)不同,float有小数点,double也是如此。但两者之间的区别在于double的详细程度是float的两倍,这意味着它可以使小数点后的数字加倍。


54
投票

这是标准C99(ISO-IEC 98996.2.5§10)或C ++ 2003(ISO-IEC 14882-20033.1.9§8)标准所说的:

有三种浮点类型:floatdoublelong 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之间的浮点数是具有最高精度的浮点数。


26
投票

给定二次方程:x2 - 4.0000000 x + 3.9999999 = 0,精确根到10位有效数字是r1 = 2.000316228和r2 = 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获得取消效果。

(事实上​​,上述不是使用单精度或双精度浮点数求解二次方程的最佳方法,但即使使用more stable method,答案也保持不变。)


19
投票
  • 双精度为64,单精度(浮点数)为32位。
  • double有一个更大的尾数(实数的整数位)。
  • 双倍的任何不准确性都会更小。

11
投票

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

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

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


8
投票

浮点数的精度低于双精度数。虽然您已经知道,但请阅读 What WE Should Know About Floating-Point Arithmetic以便更好地理解。


8
投票

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

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

类型long double名义上是80位,但是给定的编译器/ OS配对可以将其存储为12-16个字节以用于对齐目的。 long double有一个非常庞大的指数,应该有19位数的精度。微软凭借其无限的智慧,将长双倍限制为8字节,与普通双重相同。

一般来说,只需在需要浮点值/变量时使用double类型。默认情况下,表达式中使用的文字浮点值将被视为双精度值,并且返回浮点值的大多数数学函数都会返回双精度值。如果你只使用双倍,你将节省许多头痛和类型。


6
投票

我刚遇到一个错误,让我永远弄明白,并且可能会给你一个漂浮精度的好例子。

#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,则不会发生这样的问题。

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


3
投票

使用浮点数时,您不能相信您的本地测试将与服务器端的测试完全相同。环境和编译器可能与您本地系统和运行最终测试的位置不同。我之前在一些TopCoder比赛中已经多次看到这个问题,特别是如果你试图比较两个浮点数。

© www.soinside.com 2019 - 2024. All rights reserved.