c - 需要帮助理解 "Varieties of Static Analyzers: A Comparison with ASTREE"中呈现的浮点舍入

标签 c floating-point

% cat double-float1.c

 int main () {
 double x; float a, y, z, r1, r2;
 a = 1.0; x = 1125899973951488.0; y = (x + a); z = (x - a);
 r1 = y - z; r2 = 2 * a;
 printf("(x + a) - (x - a) = %f\n", r1);
 printf("2a = %f\n", r2);
 }



% gcc double-float1.c >& /dev/null; ./a.out
 (x + a) - (x - a) = 134217728.000000
 2a = 2.000000

更改最低有效数字后

% cat double-float2.c



int main () {
 double x; float a, y, z, r1, r2;
 a = 1.0; x = 1125899973951487.0; y = (x + a); z = (x - a);
 r1 = y - z; r2 = 2 * a;
 printf("(x + a) - (x - a) = %f\n", r1);
 printf("2a = %f\n", r2);
 }



% gcc double-float2.c >& /dev/null; ./a.out
 (x + a) - (x - a) = 0.000000
 2a = 2.000000

任何人都可以帮助我理解第一个示例中的内部表示如何舍入为不同的值以及第二个示例中的内部表示如何舍入为相同的值。我在其中找到了上述示例的论文链接如下。

Varieties of Static Analyzers: A Comparison with ASTREE

最佳答案

原因是 float 内部必须使用精度有限的尾数来表示。首先,如果您存储的实际十进制数无法使用实际二进制数表示,则根据特定的IEEE-754 rules,它会四舍五入到下一个最接近的二进制值。 。接下来,如果实际十进制数的位数超出了浮点类型允许的精度,则即使您使用的是十进制浮点类型,也必须对其进行舍入。

请注意,在这种情况下,根本原因是精度,而不是二进制格式,尽管二进制格式会增加额外的困惑,因为它四舍五入到可以用 24 个二进制数字表示的最接近的数字。如果您的语言具有 10 位精度的 native 十进制浮点类型,它仍然需要对内存中的数字进行舍入:

Decimal number     | Nearest decimal with 10 digits precision 
------------------ | ---------------------------------------
123450000149997.0  | 123450000100000
123450000149998.0  | 123450000100000
123450000149999.0  | 123450000100000
123450000150000.0  | 123450000200000
123450000150001.0  | 123450000200000
123450000150002.0  | 123450000200000

就您而言,舍入点恰好位于 11258999739514881125899973951489 之间。自32-bit float has 24 bits of precision (为尾数保留的 23 位 + 隐式前导位设置为 1),它的十进制精度约为 7.22 位十进制数字。这就是您的示例中数字的存储方式:

Decimal number     | Actual IEEE-754 value | IEEE-754 bits (sign, exponent, mantissa)
------------------ | ----------------------|-----------------------------------------
1125899973951486.0 | 1125899906842624      | 0 10110001 00000000000000000000000
1125899973951487.0 | 1125899906842624      | 0 10110001 00000000000000000000000
1125899973951488.0 | 1125899906842624      | 0 10110001 00000000000000000000000
1125899973951489.0 | 1125900041060352      | 0 10110001 00000000000000000000001
1125899973951490.0 | 1125900041060352      | 0 10110001 00000000000000000000001
1125899973951491.0 | 1125900041060352      | 0 10110001 00000000000000000000001

因此,(x - 1)(x + 1) 将舍入为相同的值,直到达到 1125899973951488.0。要探索如何使用 IEEE-754 在内部存储 float ,有一个有用的 online calculator你可以使用。

关于c - 需要帮助理解 "Varieties of Static Analyzers: A Comparison with ASTREE"中呈现的浮点舍入,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45647191/

相关文章:

c - C语言如何将字符串插入到链表中?

c - fclose() 导致崩溃

C++ 浮点加法(从头开始): Negative results cannot be computed

math - float 学有问题吗?

c++ - 检查浮点参数是否正确

c++ - 为什么 electric fence/Valgrind 无法捕获此缓冲区溢出问题?

c - Snow Leopard 上损坏的汇编程序

c++ - ldexp 应该正确舍入

c - 将decimal/int转换为float时如何决定何时舍入

c++ - float 的字节顺序