0

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

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

打印出来时,结果是:

a = 10000000272564224.000000
b = 272564224.000000

当在 Debug 下的 Watch 中查看它们时,结果是:

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

前置问题:我确定10000000000000000.0在 .0 范围内float。为什么我们不能得到正确的 a / b使用float


后续问题:对于前置问题,基于所有出色的以下答案。我知道原因是32 位浮点数的准确度约为 7 位,所以除了前 6-7 位之外,所有赌注都关闭了。这就是为什么数学不成立的原因,并且对于这些大数字来说打印看起来是错误的。我必须使用 double 以获得更高的准确性。那么为什么 float 声称能够处理大量数字,同时我们又不能信任它呢?

4

4 回答 4

2

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

于 2013-12-22T07:58:28.523 回答
2

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

有关浮点格式精度的更多信息: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("%f\n%f", a, b);
}

示例http://ideone.com/rJN1QI

于 2013-12-22T07:59:04.830 回答
1

您的困惑是由隐式转换和float.

让我为您填写隐式转换:

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

这会将文字转换doublefloat,并且它可以获得的最接近的是 10000000272564224。然后使用double,而不是执行减法float,因此第二个 10000000000000000.0 不会丢失精度。

于 2013-12-22T08:07:58.890 回答
0

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

该值10000000000000000.0(或1.0e16)在type 的可表示值范围内float,但该值本身无法准确表示。

这是一个说明问题的小程序:

#include <math.h>
#include <stdio.h>

int main()
{
    float a    =       10000000000000000.0;
    double d_a =       10000000000000000.0;

    printf("      %20.2f\n", nextafterf(a, 0.0f));
    printf("a   = %20.2f\n", a);
    printf("      %20.2f\n", nextafterf(a, 1.0e30f));
    putchar('\n');

    printf("      %20.2f\n", nextafter(d_a, 0.0));
    printf("d_a = %20.2f\n", d_a);
    printf("      %20.2f\n", nextafter(d_a, 1.0e30));
    putchar('\n');
}

这是我系统上的输出:

       9999999198822400.00
a   = 10000000272564224.00
      10000001346306048.00

       9999999999999998.00
d_a = 10000000000000000.00
      10000000000000002.00

如果你使用 type float,你能得到的最接近的10000000000000000.0010000000272564224.00.

但在你的第二个声明中:

float b = a - 10000000000000000.0

减法是在 type 中完成的double;该常量10000000000000000.0已经是 double 类型,并a被提升为double匹配。1.0e16因此,这采用了存储在 中的较差近似值a,并从中减去了可以用 type 表示的更好的近似值(实际上它是精确的)double

于 2013-12-22T08:19:09.140 回答