4
#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

最后两位小数之间的差异是什么原因?

4

2 回答 2

2

C 标准(99 和 11)说:

带有浮点操作数的运算值和经过通常算术转换的值以及浮点常量的值被评估为范围和精度可能大于类型要求的格式。

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

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

浮动表达式可以被压缩,也就是说,像原子操作一样进行评估,从而忽略源代码和表达式评估方法隐含的舍入错误。

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

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

关于宏的 cppreference 页面FLT_EVAL_METHOD更详细地讨论了这两个问题。找出您的价值FLT_EVAL_METHOD并使用#pragma STDC FP_CONTRACT OFF.

于 2017-10-11T01:13:58.930 回答
0

答案是因为在浮点数计算中,等式 a=b c d e 不必等于 x = b c y = d e 和 a.= x y。

这是因为浮点运算的精度有限。

于 2017-10-11T00:55:52.907 回答