C 比较两倍的结果得出一个更小,另一个相等

C 比较两倍的结果得出一个更小,另一个相等,c,32bit-64bit,C,32bit 64bit,因此,我有以下代码: double which_min(const int i, const int j, const int nx, const double step, const double local_cost, double *D) { double tuple[3]; // DIAG, LEFT, UP tuple[0] = (D[d2s(i-1, j-1, nx)]

因此,我有以下代码:

 double which_min(const int i, const int j, const int nx,
                  const double step, const double local_cost,
                  double *D)
 {
      double tuple[3];

      // DIAG, LEFT, UP
      tuple[0] = (D[d2s(i-1, j-1, nx)] == NOT_VISITED) ? DBL_MAX : D[d2s(i-1, j-1, nx)] + step * local_cost;
      tuple[1] = (D[d2s(i, j-1, nx)] == NOT_VISITED) ? DBL_MAX : D[d2s(i, j-1, nx)] + local_cost;
      tuple[2] = (D[d2s(i-1, j, nx)] == NOT_VISITED) ? DBL_MAX : D[d2s(i-1, j, nx)] + local_cost;
      /*
      if (i == 83 && j == 124) printf("less? %d\n", tuple[1] < tuple[0]);
      if (i == 83 && j == 124) printf("equal? %d\n", tuple[1] == tuple[0]);
      if (i == 83 && j == 124) printf("greater? %d\n", tuple[1] > tuple[0]);
      */
      int min = (tuple[0] <= tuple[1]) ? 0 : 1;
      min = (tuple[min] <= tuple[2]) ? min : 2;

      if (i == 83 && j == 124) printf("min = %d\n", min + 1);

      return ((double) min + 1.0);
 }
但是,如果我取消对其他
if
语句的注释,我会得到
min
的正确值,但是

less? 1
equal? 1
greater? 0
min = 1
我知道比较双精度可能很挑剔,我读到其中提到从80位寄存器移动到64位内存,所以我猜
printf
语句可能会导致类似的情况。但是,

  • 人们仍然觉得,
    tuple[1]
    既小于又等于
    tuple[0]
    ,这怎么可能呢*
  • 我是否可以更改此设置,使其一致地给出相同的结果
  • 顺便说一下,这只发生在32位系统中,64位版本不会给我带来任何问题

    编辑:我猜第一个
    printf
    会导致精度损失,因此
    =
    比较之后的结果是
    1
    ,但主要问题是我是否可以使结果一致

    EDIT2:事实上,将
    if
    语句的顺序更改为

     if (i == 83 && j == 124)
     {
          printf("equal? %d\n", tuple[1] == tuple[0]);
          printf("less? %d\n", tuple[1] < tuple[0]);
          printf("greater? %d\n", tuple[1] > tuple[0]);
     }
    
    通过
    printf(%a)
    得到的值是

    tuple[0] = 0x1.2594a8056d275p+5
    tuple[1] = 0x1.2594a8056d275p+5
    tuple[2] = 0x1.fffffffffffffp+1023
    

    实际上,存储在
    tuple
    中的结果的读取精度比预期的要高,导致一些比较产生了矛盾的结果

    向编译器添加
    -ffloat-store
    标志可以纠正这种情况,但正如注释中所述,它可能并不理想,而且显然不可移植

    tuple
    声明为
    volatile
    似乎可以做到这一点,但必须按照

    volatile double *tuple = (double *)malloc(3 * sizeof(double));
    
    然后

    free((double *)tuple);
    
    编辑:显然上述内容是不必要的, 使用
    volatile双元组[3]就足够了。
    为了仅在x32编译中使用它,
    我在
    C++
    中使用了以下
    typedef

    #include <type_traits> // conditional
    typedef std::conditional<sizeof(void*) == 4, volatile double, double>::type tuple_t;
    tuple_t tuple[3];
    
    #包含//条件
    typedef std::conditional::type tuple\t;
    tuple_t tuple[3];
    
    您可以使用
    printf(“%a”)准确打印出双精度
    也许您正在使用一个编译器选项,该选项旨在提供更快的DP算法,但会以您描述的偶尔异常为代价。有几个编译器提供了这样的选项。作为对您编辑的注释。
    printf
    与此无关。精度没有损失。printf接收一个0或1的整数参数,it不参与比较。您要查找的gcc标志是
    -ffloat store
    。但它使浮点运算速度非常慢。只需说该代码仅在
    amd64
    和更新的计算机上受支持。缩小问题的思路:1)重新迭代:查看FP编译器选项2)使用
    易失性双元组[3]强制编译器不使用扩展精度中间结果3)使用
    int lt=tuple[1]
    强制(鼓励)在所有
    printf()之前进行比较计算。
    s。4) 注意
    (lt+eq+gt)!=1
    。依我看,问题要么是#1,要么是编译器错误,要么是代码中的UB。不要抛出
    malloc()
    和friends的结果。这是不必要的,可以掩盖缺乏适当的原型。
    free((double *)tuple);
    
    #include <type_traits> // conditional
    typedef std::conditional<sizeof(void*) == 4, volatile double, double>::type tuple_t;
    tuple_t tuple[3];