AIX 编译器 13.1.3 对 c++ 的双重转换不正确

Incorrect double conversion by AIX compiler 13.1.3 for c++

我们将 char* 数组中的数据转换为 double,如下面的函数所示:

double getDouble(const char* szData, const size_t dataLength)
{
    double res = 0;
    if(dataLength == 8)
    {
        ub8 doubleData = *(ub8*)(szData);
        doubleData = ntohll(doubleData);
        double* pDoubleData = (double*)(&doubleData);
        res = *pDoubleData;
    }
    return res;
}

ub8 大小为 8 字节,unsigned long long。 double 值 -1.1512299550195975 转换为 3.6975400899608046。但输入和输出应该相等(等于-1.1512299550195975)。这种情况只发生在 AIX 中。在其他平台上我们得到正确的结果。

我们的项目使用优化级别 O2。如果我将优化级别用作 O1 ,则数据会正确转换。 你能帮我吗,你怎么看,为什么转换对于优化级别 O1 是正确的,为什么它对于优化级别 O2 是不正确的?可能我应该在编译中关闭或打开 aix 的某些标志?谢谢你。 Aix编译器版本是13.1.3.

我们从二进制文件中得到了char数组格式的double值。我们从 oracle 数据库的事务日志中解析数据。而oracle写的,比如double值是'40 0d 94 8f e6 10 3e 93'我们应该把double类型的值转换成'-1,1512299550195975'。但是在唯一的 aix 中,我们得到 '3,6975400899608046' 不正确的值

您的实施依赖于未定义的行为,特别是违反了严格的别名规则,正如有人评论的那样。

这就是为什么有时它可以工作(没有优化)有时不能。

为避免违反严格别名,您可以使用以下方法之一:

仅在 C 中(不是 C++,更多信息 here):

typedef union swap64{
    unsigned long long u64;
    double d64;
}swap64;
swap64 s;
memcpy(s.u64, szData, sizeof(swap64));
s.u64=ntohll(s.u64);

//Following line is UB in C++, but not in C:
return s.d64;

或没有 UB(代码在 C++ 和 C 中均有效):

unsigned long long u64;
double d64;

//Most compilers will remove the memcpy calls and replace them by simpler instructions
memcpy(&u64, szData, sizeof(u64));
u64=ntohll(u64);
memcpy(&d64, &u64, sizeof(u64));

return d64;