为什么是 100000000000000000!= 10000000000000000000

Why is 10000000000000000 != 10000000000000000?

本文关键字:10000000000000000000 100000000000000000 为什么      更新时间:2023-10-16

首先,使用C++在Visual Studio中查看以下代码:

float a = 10000000000000000.0;
float b = a - 10000000000000000.0;

打印出来时,结果是:

a = 10000000272564224.000000
b = 272564224.000000

在"调试"下的"监视"中查看它们时,结果是:

-Name-   -Value-          -Type-
a        1.0000000e+016   float
b        2.7256422e+008   float

前问题:我确定 100000000000000000.0 在 float 的范围内。为什么我们不能使用 float 获得正确的 a/b


后续问题:对于预提问,基于所有伟大的以下答案。我知道原因是 32 位浮点数的准确度约为 7 位,因此超过前 6-7 位,所有投注都关闭了。这就是为什么数学不成功,打印这些大数字看起来是错误的。我必须使用双倍才能获得更高的准确性。那么,为什么浮点声称能够处理大量数据,同时我们不能信任它呢?

您使用的巨大数字确实在浮点数的"范围内",但并非所有数字都在浮点数的"精度"范围内。 32 位浮点数的准确度约为 7 位,因此超过前 6-7 位,所有投注都关闭。 这就是为什么数学不成功的原因,当你使用这些大数字时,打印看起来"错误"。 如果您想要更高的准确性,请使用双精度。 有关详细信息,请参阅 http://en.wikipedia.org/wiki/Floating_point#IEEE_754:_floating_point_in_modern_computers

浮点数大约需要 6-7 位小数(分数为 23 位),因此任何具有更多小数位的数字都只是一个近似值。这就引出了那个 rondom 数字。

有关浮点格式精度的详细信息:http://en.wikipedia.org/wiki/Single-precision_floating-point_format

对于更新的问题:当需要精度时,切勿使用浮点格式。我们不能只指定更大的内存空间。处理具有非常大量小数的数字需要非常大量的内存。因此,使用更复杂的方法代替(例如使用字符串格式然后依次处理字符)。

为了避免这个问题,请使用double,它给出大约 16-17 位小数(分数为 52 位)或 long double 以获得更高的精度。

#include <stdio.h>
int main()
{
double a = 10000000000000000.0;
double b = a - 10000000000000000.0;
printf("%fn%f", a, b);
}

示例 http://ideone.com/rJN1QI

您的困惑是由隐式转换和float缺乏准确性引起的。

让我为您填写隐式转换:

float a = (float)10000000000000000.0;
float b = (float)((double)a - 10000000000000000.0);

这会将文字double转换为float,它最接近的是10000000272564224。然后减法是用double而不是float执行的,所以第二个 100000000000000000.0 不会失去精度。

我们可以使用 nextafter 函数来更好地了解浮点类型的精度。 nextafter有两个论点;它将相邻的可表示数字返回到其第一个参数,沿其第二个参数的方向。

10000000000000000.0(或1.0e16)完全在类型float的可表示值范围内,但该值本身不能精确表示。

下面是一个说明该问题的小程序:

#include <math.h>
#include <stdio.h>
int main()
{
    float a    =       10000000000000000.0;
    double d_a =       10000000000000000.0;
    printf("      %20.2fn", nextafterf(a, 0.0f));
    printf("a   = %20.2fn", a);
    printf("      %20.2fn", nextafterf(a, 1.0e30f));
    putchar('n');
    printf("      %20.2fn", nextafter(d_a, 0.0));
    printf("d_a = %20.2fn", d_a);
    printf("      %20.2fn", nextafter(d_a, 1.0e30));
    putchar('n');
}

这是我系统上的输出:

       9999999198822400.00
a   = 10000000272564224.00
      10000001346306048.00
       9999999999999998.00
d_a = 10000000000000000.00
      10000000000000002.00

如果使用类型 float ,则最接近10000000000000000.00的是 10000000272564224.00

但是在你的第二个声明中:

float b = a - 10000000000000000.0

减法在类型double中完成;常数10000000000000000.0已经是双精度类型,并且a被提升到double以匹配。因此,这需要存储在a中的1.0e16的差近似,并从中减去可以用类型 double 表示的更好的近似(实际上是精确的)。

相关文章:
  • 没有找到相关文章