c - 为什么这段代码返回两个不同的值做同样的事情?

标签 c binary precision arbitrary-precision numerical-computing

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

int main()
{
    double a;
    double b;
    double q0 = 0.5 * M_PI + 0.5 * -2.1500000405000002;
    double q1 = 0.5 * M_PI + 0.5 * 0.0000000000000000;
    double w0 = 0.5 * M_PI + 0.5 * -43000.0008100000050000;
    double w1 = 0.5 * M_PI + 0.5 * -0.0000000000000000;
    double m = 1;
    double g = 43000000.81;
    double l1 = 0.1;
    double l2 = 0.1;
    double h = 0.0001;

    a = ((-g / l1) * sin(q0) + (sin(q1 - q0) * (cos(q1 - q0) * (w0 * w0 + (g / l1) * cos(q0)) + l2 * (w1 * w1 / l1))) / (m + pow(sin(q1 - q0), 2)));
    a = h * a;

    b = h * ((-g / l1) * sin(q0) + (sin(q1 - q0) * (cos(q1 - q0) * (w0 * w0 + (g / l1) * cos(q0)) + l2 * (w1 * w1 / l1))) / (m + pow(sin(q1 - q0), 2)));

    printf("%.20lf ", a);
    printf("%.20lf", b);
    return 0;
}

我对 a 和 b 进行相同的计算,不同之处在于我分两步获得 a 的值,而一步获得 b。

我的代码返回: -629.47620126173774000000 -629.47620126173763000000

最后两位小数不同的原因是什么?

最佳答案

C 标准(99 和 11)说:

The values of operations with floating operands and values subject to the usual arithmetic conversions and of floating constants are evaluated to a format whose range and precision may be greater than required by the type.

所以在诸如 h*(X+Y) 的表达式中,正如您在 b 的赋值中所拥有的,允许实现对中间值使用更高的精度X+Y 的结果可能存储在 double 中,即使子表达式的类型仍然被认为是 double。但是在 a=X+Y; a=h*a;,第一个赋值强制该值是实际可以存储在 double 中的值,导致结果略有不同。

另一种可能是编译器做了“浮点收缩”。再次引用 C 标准,

A floating expression may be contracted, that is, evaluated as though it were an atomic operation, thereby omitting rounding errors implied by the source code and the expression evaluation method.

如果处理器有一条指令可以在一个步骤中执行浮点加法然后乘法,并且编译器决定使用它,则很可能会发生这种情况。

假设其中一个或两个是原因,您的值 b 可能更准确地表示您指定的计算(假设所有输入都限制为可以在 a 中表示的值).

关于宏的cppreference页面FLT_EVAL_METHOD更详细地讨论了这两个问题。找出 FLT_EVAL_METHOD 的值并使用 #pragma STDC FP_CONTRACT OFF 可能会很有趣。

关于c - 为什么这段代码返回两个不同的值做同样的事情?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46677966/

相关文章:

floating-point - double 型的符合 IEEE 754 标准的 sqrt() 实现

c - c中的按位溢出检查

c - C++ 中嵌套函数的全局变量

关于看似兼容的函数指针赋值的编译器警告(const vs no-const)

具有十进制、八进制和十六进制数字的计算器

java - 将整数转换为零填充二进制字符串

c - 如何将 unsigned char 数组转换为 base64 字符串?

python - bash 或 python 脚本从 STDIN 读取二进制数据并保存到文件

opengl-es - 为什么必须为片段着色器设置精度?

用于舍入 float 的 Python 超参数