我已经读过双精度和单精度之间的区别。但是,在大多数情况下,float
和double
似乎是可以互换的,即使用一个或另一个似乎不会影响结果。这是真的吗?浮动和双打什么时候可以互换?它们之间有什么区别?
答案 0 :(得分:451)
巨大差异。
顾名思义,double
的精度是float
[1] 的2倍。通常,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
可以命中“无穷大”(即一个特殊的浮点数)比简单的double
更容易,例如计算60的阶乘。
在测试期间,可能有一些测试用例包含这些巨大的数字,如果您使用浮点数,可能会导致您的程序失败。
当然,有时甚至double
都不够准确,因此我们有时会long double
[1] (上面的例子在Mac上给出了9.000000000000000066),但是所有浮点类型都会受到舍入错误的影响,因此如果精度非常重要(例如货币处理),则应使用int
或分数类。
此外,不要使用+=
来汇总大量浮点数,因为错误会很快累积。如果您使用的是Python,请使用fsum
。否则,请尝试实施Kahan summation algorithm。
[1]:C和C ++标准没有指定float
,double
和long double
的表示。所有这三个都可以实现为IEEE双精度。然而,对于大多数体系结构(gcc,MSVC; x86,x64,ARM)float
确实是IEEE单精度浮点数(binary32)和double
是 IEEE双精度浮点数(binary64)。
答案 1 :(得分:53)
以下是标准C99(ISO-IEC 98996.2.5§10)或C ++ 2003(ISO-IEC 14882-20033.1.9§8)标准所说的内容:
有三种浮点类型:
float
,double
和long double
。类型double
提供的精度至少与float
一样,类型long double
提供的精度至少与double
一样。类型float
的值集是类型double
的值集的子集;类型double
的值集是long double
类型值集的子集。
C ++标准补充说:
浮点类型的值表示是实现定义的。
我建议您深入了解一下涵盖IEEE浮点标准的优秀What Every Computer Scientist Should Know About Floating-Point Arithmetic。您将了解表示细节,并且您将意识到在幅度和精度之间存在权衡。浮点表示的精度随着幅度的减小而增加,因此-1和1之间的浮点数是精度最高的。
答案 2 :(得分:26)
给定二次方程: 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
获得取消效果。
(事实上,上述不是使用单精度或双精度浮点数求解二次方程的最佳方法,但即使使用more stable method,答案也保持不变。)
答案 3 :(得分:19)
答案 4 :(得分:11)
浮点计算中涉及的数字大小并不是最相关的。这是正在执行的相关计算。
本质上,如果您正在执行计算并且结果是无理数或重复的小数,那么当该数被压缩到您正在使用的有限大小数据结构时,将会出现舍入错误。由于double是float的两倍,因此舍入误差会小很多。
测试可能会特别使用可能导致此类错误的数字,因此测试您在代码中使用了相应的类型。
答案 5 :(得分:8)
浮点数的精度低于双精度数。虽然您已经知道,但请阅读 What WE Should Know About Floating-Point Arithmetic以便更好地理解。
答案 6 :(得分: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类型。默认情况下,表达式中使用的文字浮点值将被视为双精度值,并且返回浮点值的大多数数学函数都会返回双精度值。如果你只使用双倍,你会省去许多头痛和类型。
答案 7 :(得分:7)
我遇到了一个错误,让我永远弄明白,并且可能会给你一个漂浮精度的好例子。
#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,则不会发生此类问题。
我花了五个小时才意识到这个小错误,这毁了我的程序。
答案 8 :(得分:3)
使用浮点数时,您不能相信您的本地测试与服务器端的测试完全相同。环境和编译器可能与您本地系统和运行最终测试的位置不同。我之前在一些TopCoder比赛中已经多次看到这个问题,特别是如果你试图比较两个浮点数。
答案 9 :(得分:2)
内置比较操作的不同之处在于,当您将2个数字与浮点数进行比较时,数据类型的差异(即浮点数或双数字)可能会导致不同的结果。
答案 10 :(得分:1)
如果使用嵌入式处理,最终底层硬件(例如FPGA或某些特定的处理器/微控制器模型)将在硬件中以最佳方式实现浮动,而double会使用软件例程。因此,如果浮点数的精度足以应付需求,则程序会以浮点数执行的速度快一些,然后再执行两次。如其他答案所述,请当心累积错误。
答案 11 :(得分:0)
float和double的区别在于double具有比float变量更大的精度值。
当您将变量声明为float时,它仅允许在小数点后输入6个小数位。
也就是说,对于float变量
float f= 2.3333333;
//。后的7个小数位。
是您可以存储的最大值
即使您存储的值在小数点后超过六位,它也不会存储整数,而只会存储到小数点后的前六位。
另外,如果您尝试在此变量的小数点后存储7位以上的数字,则会收到错误消息。在这种情况下,您需要按以下方式对其进行初始化-
float f= 2.3333334443f;
//然后编译器会将其解释为2.3333334
如果是Double,它将存储小数点后最多15位数字。
例如:double d=1.222222345675423;
//小数点后15位
答案 12 :(得分:0)
答案 13 :(得分:-1)
与int
(整数)不同,float
有一个小数点,double
也是如此。
但两者之间的区别在于double
的详细程度是float
的两倍,这意味着它可以使小数点后的数字加倍。