float和double的区别是什么?

我读过关于双精度和单精度之间的区别。然而,在大多数情况下,floatdouble似乎是可互换的,即使用其中一个似乎不会影响结果。事实真的如此吗?什么时候浮点数和双精度数可以互换?它们之间有什么区别?

1136410 次浏览
    双精度是64,单精度 . (float)为32位
  • double有一个更大的尾数(实数的整数位)。
  • 任何不准确的地方都将在double中减小。

浮点数的精度比双精度数低。虽然你已经知道了,但为了更好地理解,请阅读关于浮点运算我们应该知道什么

以下是标准C99 (ISO-IEC 9899 6.2.5§10)或c++ 2003 (ISO-IEC 14882-2003 3.1.9§8)标准所说的:

浮点数有三种类型:floatdoublelong double。类型double提供的精度至少与float相同,类型long double提供的精度至少与double相同。类型为float的值集是类型为double的值集的子集;类型为double的值集是类型为long double的值集的子集。

c++标准增加了:

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

我建议看一看优秀的每个计算机科学家都应该知道浮点运算,它深入覆盖了IEEE浮点标准。您将了解表示细节,并将意识到在量级和精度之间存在权衡。浮点表示的精度随着幅度的减小而增加,因此-1到1之间的浮点数具有最高的精度。

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

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

从本质上讲,如果您正在执行计算,而结果是一个无理数或循环小数,那么当将该数字压缩到您正在使用的有限大小的数据结构中时,将会出现舍入错误。因为double是float大小的两倍,所以舍入误差会小很多。

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

巨大的差异。

顾名思义,double的精度是float[1]的2倍。通常,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求和算法


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

给定一个二次方程: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获得抵消效果。

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

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

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

类型long double名义上是80位,尽管给定的编译器/操作系统配对可能会将其存储为12-16字节以进行对齐。长双精度数的指数大得离谱,应该有19位精度。微软以其无限的智慧,将long double限制为8字节,与普通double相同。

一般来说,当需要浮点值/变量时,只需使用double类型。默认情况下,表达式中使用的字面浮点值将被视为双精度值,并且大多数返回浮点值的数学函数都会返回双精度值。如果只使用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设置为double,这样的问题就不会发生。

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

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

如果使用嵌入式处理,最终底层硬件(例如FPGA或某些特定的处理器/微控制器模型)将在硬件中优化实现float,而double将使用软件例程。因此,如果浮点数的精度足以满足需求,则使用浮点数执行程序的速度将比使用浮点数执行程序的速度快几倍。正如在其他答案中提到的,要小心累积错误。

有三种浮点类型:

  • 浮动
  • 长两倍
一个简单的维恩图可以解释: 类型

的值集合

enter image description here

在数量上,正如其他答案所指出的那样,区别在于double类型的精度大约是float类型的两倍,范围是float类型的三倍(取决于你如何计算)。

但也许更重要的是质的差异。类型float具有良好的精度,通常对于您正在做的任何事情都足够好。另一方面,类型double具有优秀的精度,无论你在做什么,它几乎总是足够好。

结果是,它不像它应该的那样广为人知,你应该总是使用double类型。除非你有一些特别的特殊需要,否则你几乎不应该使用float类型。

众所周知,“舍入错误”;在做浮点运算时经常会出现问题。舍入错误可能很微妙,很难追踪,也很难修复。大多数程序员没有时间或专业知识来跟踪和修复浮点算法中的数值错误——因为不幸的是,每个不同的算法的细节最终都是不同的。但是double类型有足够的精度,所以在大多数情况下,你不必担心。 无论如何你都会得到好的结果。另一方面,对于float类型,舍入会突然出现一直以来.

类型在floatdouble之间必然的不同之处在于执行速度。在当今大多数通用处理器上,类型floatdouble上的算术运算所花费的时间大致相同。所有事情都是并行完成的,所以你不会因为double类型的更大范围和精度而付出速度上的代价。这就是为什么建议你几乎不应该使用float类型是安全的:使用double不应该在速度上付出任何代价,也不应该在空间上花费太多,而且它几乎肯定会在避免精度和舍入错误方面带来丰厚的回报。

(话虽如此,但其中一个“特殊需要”;你可能需要float类型的地方是当你在微控制器上做嵌入式工作,或编写针对GPU优化的代码时。在这些处理器上,double类型可能要慢得多,或者几乎不存在,所以在这些情况下,程序员通常会选择float类型来提高速度,并可能为精度付出代价。)