为什么这段代码会返回两个不同的值呢?

时间:2017-10-11 00:37:17

标签: 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

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

2 个答案:

答案 0 :(得分:2)

C标准(99和11)说:

  

具有浮动操作数的操作值和通常算术转换以及浮动常量的值将被评估为其范围和精度可能大于该类型所需的格式。

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

另一种可能性是编译器已完成&#34;浮点收缩&#34;。再次引用C标准,

  

浮动表达式可能简化,即,它被评估为原子操作,从而省略源代码和表达式评估方法隐含的舍入错误。

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

假设其中一个或两个是原因,您的值b可能更精确地表示您指定的计算(假设所有输入都限制为可以用{{1表示的值] }})。

关于宏FLT_EVAL_METHOD的cppreference页面更详细地讨论了这两个问题。找出double的价值并与FLT_EVAL_METHOD一起玩可能会很有趣。

答案 1 :(得分:0)

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

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