495

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

4

14 回答 14

604

巨大的差异。

顾名思义, adouble的精度是[1]的 2 倍。一般来说,a有 15 位精度,而有 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,因此使用float可以比简单的计算更容易达到“无穷大”(即特殊的浮点数)double,例如计算 60 的阶乘。

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


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


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


[1]:C 和 C++ 标准没有指定 和的float表示。有可能所有三个都实现为 IEEE 双精度。尽管如此,对于大多数架构(gcc、MSVC;x86、x64、ARM)来说确实是一个 IEEE 单精度浮点数(binary32),并且一个 IEEE 双精度浮点数(binary64)。doublelong doublefloat double

于 2010-03-05T13:06:43.593 回答
60

以下是标准 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 浮点标准的优秀的What Every Computer Scientist Should Know About Floating-Point Arithmetic 。您将了解表示的详细信息,并且您会意识到在幅度和精度之间存在权衡。浮点表示的精度随着幅度的减小而增加,因此介于 -1 和 1 之间的浮点数是精度最高的。

于 2010-03-05T12:54:51.520 回答
30

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

使用floatand 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.

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

于 2010-03-05T17:57:25.627 回答
19
  • 双精度是 64,单精度(浮点)是 32 位。
  • 双精度数的尾数更大(实数的整数位)。
  • 双精度中的任何不准确性都会更小。
于 2010-03-05T12:53:20.800 回答
13

我刚刚遇到了一个错误,我花了很长时间才弄清楚,并且可能会给你一个浮点精度的好例子。

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

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

于 2015-10-20T06:51:04.713 回答
12

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

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

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

于 2010-03-05T13:05:56.283 回答
10

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

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

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

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

于 2011-03-08T05:13:27.817 回答
10

共有三种浮点类型:

  • 漂浮
  • 双倍的
  • 长双

一个简单的维恩图将解释: 类型的值集

在此处输入图像描述

于 2020-09-07T08:48:54.333 回答
9

浮点数的精度低于双精度数。尽管您已经知道,但请阅读我们应该了解的关于浮点运算的知识以更好地理解。

于 2010-03-05T12:54:45.763 回答
3

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

于 2010-03-05T13:00:57.617 回答
3

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

于 2011-12-07T07:40:27.383 回答
1

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

于 2020-05-07T13:36:32.583 回答
0

从数量上讲,正如其他答案所指出的那样,不同之处在于类型double的精度大约是类型的两倍,范围是类型的三倍float(取决于您的计数方式)。

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

结果并不像它应该的那样广为人知,那就是你应该几乎总是使用 typedouble。除非你有一些特别的需要,你几乎不应该使用 type float

众所周知,在进行浮点运算时,“舍入误差”通常是一个问题。舍入误差可能很微妙,难以追踪,也难以修复。大多数程序员没有时间或专业知识来追踪和修复浮点算法中的数值错误——因为不幸的是,每种不同算法的细节最终都会有所不同。但是 typedouble有足够的精度,所以在很多时候,你不必担心。无论如何你都会得到好的结果。float另一方面,对于 type ,舍入问题总是会出现令人担忧问题。

类型之间并不总是不同的事情是执行速度。在当今的大多数通用处理器上,对类型和类型的算术运算所花费的时间或多或少完全相同。一切都是并行完成的,因此您不会因为 type 的更大范围和精度而付出速度损失。这就是为什么提出建议是安全的,除非你有一些特别的需要,否则你几乎不应该使用 type 。(话虽如此,其中一个特殊需求是当您在微控制器上进行嵌入式工作或编写针对 GPU 优化的代码时。在这些处理器上,键入floatdoublefloatdoubledoublefloatdouble可能会慢很多,或者实际上不存在,因此程序员通常会选择类型float来提高速度,并以精度为代价。)

于 2022-02-26T12:34:26.473 回答
-2

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

于 2017-09-05T12:10:09.353 回答