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

#1 楼

差异很大。
顾名思义,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,因此请使用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)。

评论


求和的通常建议是在求和之前,按大小(最小的优先)对浮点数进行排序。

–R .. GitHub停止帮助ICE
2010年8月6日,9:49

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

–洗头
19年2月8日在5:27

@ R..GitHub STOPHELPINGICE:为什么?你能解释一下吗?

– InQuisitive
1月2日7:27

@InQusitive:例如,考虑一个数组,该数组由值2 ^ 24和后面的值1的2 ^ 24个重复组成。按顺序求和会产生2 ^ 24。反转产生2 ^ 25。当然,您可以举个例子(例如,使其成为2 ^ 25重复的1),其中单个累加器的任何顺序最终都将导致灾难性的错误,但是最小的优先级是最好的。为了做得更好,您需要某种树。

–R .. GitHub停止帮助ICE
1月2日15:18

@ R..GitHubSTOPHELPINGICE:如果数组同时包含正数和负数,则求和更加棘手。

– chqrlie
9月7日晚上8:59

#2 楼

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


共有三种浮点类型:floatdoublelong doubledouble类型提供至少与float一样高的精度,而long double类型提供至少与double一样高的精度。 float类型的值集是double类型的值集的子集; double类型的值集是long double类型的值集的子集。


C ++标准添加:



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


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

#3 楼

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

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

#4 楼


双精度数为64,单精度精度为
(浮点数)为32位。
双精度数具有较大的尾数(实数的整数位)。
任何不准确之处都是在双人间中较小。


#5 楼

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

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

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

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

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

评论


只是为了确保:您问题的解决方案应该是最好使用int?如果要迭代100次,则应使用int而不是使用double来计数

– BlueTrin
16-09-19在12:07

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

–理查德
17年9月24日在23:10

#7 楼

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

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

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

通常来说,当需要浮点值/变量时,只需使用type double。默认情况下,表达式中使用的文字浮点值将被视为双精度,大多数返回浮点值的数学函数将返回双精度。如果只使用double,您将避免很多头痛和打字。

评论


实际上,对于浮点数,准确的是在7到8之间,即7.225。

– Peter Mortensen
13年4月12日在20:25



#8 楼

浮点数的精度低于双精度数。尽管您已经知道,但请阅读有关浮点算法的知识,以便更好地理解。

评论


例如,所有AVR双打都是浮点型(四字节)。

– Peter Mortensen
13年4月12日在20:22



#9 楼

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

#10 楼

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

#11 楼

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

#12 楼

共有三种浮点类型:

float
double
long double

简单的维恩图将解释以下内容:
类型的值


#13 楼

int(整数)不同,float具有小数点,而double也具有小数点。
但是两者之间的区别是double的详细信息是float的两倍,这意味着它可以具有两倍的小数。小数点后的数字数量。

评论


这一点都不意味着。它实际上意味着两倍的整数十进制数字,并且是两倍多。小数位数与精度之间的关系不是线性的:它取决于值:例如0.5是精确的,但0.33333333333333333333不是。

–user207421
17年9月24日在23:34