C++ ULP比较码

C++ ULP比较码,c++,floating-point,bitwise-operators,C++,Floating Point,Bitwise Operators,以下代码片段分散在web上,似乎在多个不同的项目中使用,变化很小: union Float_t { Float_t(float num = 0.0f) : f(num) {} // Portable extraction of components. bool Negative() const { return (i >> 31) != 0; } int RawMantissa() const { return i & ((1 <

以下代码片段分散在web上,似乎在多个不同的项目中使用,变化很小:

union Float_t {
    Float_t(float num = 0.0f) : f(num) {}
    // Portable extraction of components.
    bool Negative()    const { return (i >> 31) != 0; }
    int  RawMantissa() const { return i & ((1 << 23) - 1); }
    int  RawExponent() const { return (i >> 23) & 0xFF; }

    int i;
    float f;
};

inline bool AlmostEqualUlpsAndAbs(float A, float B, float maxDiff, int maxUlpsDiff)
{
    // Check if the numbers are really close -- needed
    // when comparing numbers near zero.
    float absDiff = std::fabs(A - B);
    if (absDiff <= maxDiff)
        return true;

    Float_t uA(A);
    Float_t uB(B);

    // Different signs means they do not match.
    if (uA.Negative() != uB.Negative())
        return false;

    // Find the difference in ULPs.
    return (std::abs(uA.i - uB.i) <= maxUlpsDiff);
}
union Float\t{
Float_t(Float num=0.0f):f(num){
//便携式组件提取。
bool Negative()常量{return(i>>31)!=0;}
int rawmanissa()常量{return i&((1>23)&0xFF;}
int i;
浮动f;
};
内联布尔ALMOSTEQUALLPSANDABS(浮点A、浮点B、浮点maxDiff、int maxUlpsDiff)
{
//检查数字是否真的很接近——需要
//当比较接近零的数字时。
浮点数absDiff=std::fabs(A-B);

if(absDiff这里有很多未定义的行为被利用。第一个假设是可以访问联合字段来代替彼此,这本身就是UB。此外,编码器假设:
sizeof(int)==sizeof(float)
,浮点数有一个给定长度的尾数和指数,所有工会成员都对齐为零,浮点数的二进制表示形式与int的二进制表示形式以一种非常特定的方式一致。简而言之,只要您在x86上,有特定的int和float类型,并且您在每天日出时都会祈祷,这将起作用日落时分


您可能没有注意到的是,这是一个并集,因此大多数编译器通常在公共内存数组中以特定的方式对齐
inti
float f
。一般来说,这仍然是UB,您甚至不能安全地假设在不限制自己的特定内存的情况下使用相同的物理内存位fic编译器和特定的体系结构。可以保证的是,两个成员的地址是相同的(但可能存在对齐和/或类型问题)。假设您的编译器使用相同的物理位(这绝不是标准所保证的)它们都从偏移量0开始并具有相同的大小,然后
i
将表示
f
的二进制存储格式。只要您的体系结构没有变化。忠告?在不需要之前不要使用它。对于
AlmostEquals(),你可以像这样实现它。当我们考虑这些特性时,它是最后一道优化,我们通常是在一个单独的分支中完成的,你不应该在它周围规划你的代码。

看起来有些不可移植的比特欺骗正在进行。我同意。不幸的是,我必须重写和清理这段代码,所以我想了解一下。首先,到底发生了什么:-)@carsten,仅供参考,代码源于Bruce Dawson的。这里给出了完整的解释。我知道我最好用这样的方式交换这段代码()但是我需要保持接口兼容性,所以我需要理解参数到底是怎么做的,例如如何将代码> Max LulsDIFF</代码>翻译成可移植的东西。你会对此进行评论吗?更糟的类型是双关语,即在C++中不允许读一种类型的“不同类型”。这包括使用<代码>NION <代码>尝试。在C中,你可以使用<代码>联合/代码>。在C++中,它是纯UB。因此即使类型的位表示被保证,也不会保存这个代码。它是不可保存的。它可能在大多数编译器上工作,而不是麻烦地让它修剪草坪,但它是一个不可移植的黑客,因此应该是。直接委托给
/dev/null
。不客气。我没有投票,但如果你在第2段中把这个警告说得再清楚一点,我会+1:P我担心其他地方的人会设法不建立连接。重申:“
I
将代表
f
的二进制存储格式”这可能是高效编译器实现的一个明显的偶然事件,但这并不是必需的。例如,我个人会说这样的话:“正如这种代码的不幸流行所证明的那样,在非反复无常的编译器中,这是很有可能的,但根据标准,这并不能保证……”下划线_d:ever顺便说一句,我在考虑反例和可能的体系结构……是否保证将并集分配为连续范围?如果不是,编译器可能将其分配为内存地址和浮点寄存器(或int寄存器和fp寄存器).你认为有可能吗?@lorro
union
成员必须有相同的地址,所以他们必须有一个地址,这样无论是
union
还是它的任何成员都不能存储在寄存器中。以下是一条线索,它简洁地总结了标准对地址的描述: