4

在网上搜索,我发现了以下用于计算 IEEE 格式的浮点数符号的例程。这也可以很容易地扩展到双倍。

// returns 1.0f for positive floats, -1.0f for negative floats, 0.0f for zero
inline float fast_sign(float f) {
    if (((int&)f & 0x7FFFFFFF)==0) return 0.f; // test exponent & mantissa bits: is input zero?
    else {
        float r = 1.0f;
        (int&)r |= ((int&)f & 0x80000000); // mask sign bit in f, set it in r if necessary
        return r;
    }
}

来源:“32 位浮点数的快速符号”,Peter Schoffhauzer)

不过,由于位二进制操作,我厌倦了使用这个例程。我需要我的代码在具有不同字节顺序的机器上工作,但我不确定 IEEE 标准规定了多少,因为我找不到今年发布的最新版本。无论机器的字节顺序如何,有人可以告诉我这是否可行?

谢谢,帕特里克

4

2 回答 2

10

您如何看待fabs()fabsf()在您的系统上实施,或者就此与常数 0 进行比较?如果不是按位运算,很可能是因为编译器作者认为这不会更快。

此代码的可移植性问题是:

  1. float 和 int 可能不具有相同的字节顺序甚至相同的大小。因此,面具也可能是错误的。
  2. float 可能不是 IEEE 表示
  3. 你打破了严格的别名规则。允许编译器假定指向浮点数的指针/引用和指向 int 的指针/引用不能指向相同的内存位置。因此,例如,该标准不保证r在以下行中对其进行修改之前使用 1.0 进行初始化。它可以重新排序操作。这不是空穴来风,与 (1) 和 (2) 不同的是,它是未定义的,不是实现定义的,因此您不一定只为您的编译器查找它。通过足够的优化,我看到 GCC 跳过了浮点变量的初始化,这些变量仅通过类型双关指针引用。

我会首先做显而易见的事情并检查发出的代码。只有当这看起来很狡猾时,才值得考虑做其他事情。我没有任何特别的理由认为我比我的编译器更了解浮点数的按位表示;-)

inline float fast_sign(float f) {
    if (f > 0) return 1;
    return (f == 0) ? 0 : -1;
    // or some permutation of the order of the 3 cases
}

[编辑:实际上,即使使用-O3,GCC 也确实可以做到这一点。发出的代码不一定很慢,但它确实使用了浮点运算,所以不清楚它是否很快。#define因此,下一步是进行基准测试,测试替代方案在您可以使用的任何编译器上是否更快,如果是,则根据他们自己的基准测试结果,使其成为移植您的代码的人可以使用 a 或其他方式启用的东西.]

于 2010-03-24T14:37:55.537 回答
3

不要忘记将浮点值从 FPU 寄存器移动到整数寄存器需要先写入 RAM,然后再读取。

使用浮点代码,你总是会更好地看待更大的图景:

Some floating point code
Get sign of floating point value
Some more floating point code

在上述情况下,使用 FPU 来确定符号会更快,因为不会有写入/读取开销1。英特尔 FPU 可以:

FLDZ
FCOMP

它为 和 设置条件代码标志> 0< 0并且== 0可以与 . 一起使用FCMOVcc

将上述内容内联到编写良好的 FPU 代码中将击败任何整数位操作,并且不会丢失精度2

笔记:

  1. 英特尔 IA32 确实具有写后读优化,它不会等待数据提交到 RAM/缓存,而直接使用该值。尽管如此,它仍然会使缓存无效,因此会产生连锁反应。
  2. 英特尔 FPU 内部为 80 位,浮点数为 32,双精度数为 64,因此转换为浮点数/双精度数以重新加载为整数会损失一些精度。这些是重要的位,因为您正在寻找 0 附近的转换。
于 2010-03-24T15:23:30.203 回答