比较双倍产生一个和另一个相等

时间:2016-09-27 14:18:23

标签: 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)] == 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);
 }

对于我正在使用的有问题的案例,step = 1i = 83j = 124时,我正在

min = 2. 

但是,如果我取消注释其他if语句,我会得到min的正确值,但是......

less? 1
equal? 1
greater? 0
min = 1

我知道比较双打可能很挑剔,我读this answer提到从80位寄存器移动到64位内存,所以我猜测printf语句可能导致类似的东西。然而,

  1. tuple[1]更少且等于tuple[0]仍然感觉违反直觉,这怎么可能?*
  2. 我可以更改此设置,以便始终如一地提供相同的结果吗?
  3. 顺便说一句,这只发生在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]);
     }
    

    结果

    equal? 0
    less? 0
    greater? 0
    min = 1
    

    我使用printf(%a)获得的值是

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

1 个答案:

答案 0 :(得分:0)

确实,tuple中存储的结果的读取精度高于预期,导致某些比较产生矛盾的结果。

-ffloat-store标志添加到编译器可以解决这种情况,但正如评论中所提到的,它可能并不理想,显然它不可移植。

tuple声明为volatile似乎可以解决问题,但必须以

完成
volatile double *tuple = (double *)malloc(3 * sizeof(double));

然后

free((double *)tuple);
编辑:显然上面没有必要, 使用volatile double tuple[3];就足够了。 为了仅在x32编译中使用它, 我在typedef中使用了以下C++

#include <type_traits> // conditional
typedef std::conditional<sizeof(void*) == 4, volatile double, double>::type tuple_t;
tuple_t tuple[3];