比较双打产生一个少一个和等于另一个
Comparing doubles yields one less AND equal to the other
所以我有以下代码:
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 = 1
、i = 83
和 j = 124
时,我得到
min = 2.
但是,如果我取消注释其他 if
语句,我会得到 min
的正确值,但是...
less? 1
equal? 1
greater? 0
min = 1
我知道比较双精度数可能很挑剔,我读到 this answer 提到从 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]);
}
结果
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
确实,存储在 tuple
中的结果的读取精度高于预期,导致一些比较产生了相互矛盾的结果。
向编译器添加 -ffloat-store
标志可以解决这种情况,但如评论中所述,它可能并不理想,而且显然不可移植。
将 tuple
声明为 volatile
似乎可以解决问题,但必须将其声明为
volatile double *tuple = (double *)malloc(3 * sizeof(double));
然后
free((double *)tuple);
编辑:显然上面没有必要,
使用 volatile double tuple[3];
就足够了。
为了仅在 x32 编译中使用它,
我在 C++
中使用了以下 typedef
:
#include <type_traits> // conditional
typedef std::conditional<sizeof(void*) == 4, volatile double, double>::type tuple_t;
tuple_t tuple[3];
所以我有以下代码:
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 = 1
、i = 83
和 j = 124
时,我得到
min = 2.
但是,如果我取消注释其他 if
语句,我会得到 min
的正确值,但是...
less? 1
equal? 1
greater? 0
min = 1
我知道比较双精度数可能很挑剔,我读到 this answer 提到从 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]);
}
结果
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
确实,存储在 tuple
中的结果的读取精度高于预期,导致一些比较产生了相互矛盾的结果。
向编译器添加 -ffloat-store
标志可以解决这种情况,但如评论中所述,它可能并不理想,而且显然不可移植。
将 tuple
声明为 volatile
似乎可以解决问题,但必须将其声明为
volatile double *tuple = (double *)malloc(3 * sizeof(double));
然后
free((double *)tuple);
编辑:显然上面没有必要,
使用 volatile double tuple[3];
就足够了。
为了仅在 x32 编译中使用它,
我在 C++
中使用了以下 typedef
:
#include <type_traits> // conditional
typedef std::conditional<sizeof(void*) == 4, volatile double, double>::type tuple_t;
tuple_t tuple[3];