【问题标题】:Comparing floats in their bit representations比较浮点数的位表示
【发布时间】:2011-02-04 15:52:15
【问题描述】:

假设我想要一个带有两个浮点数的函数(xy),我想比较它们,而不是使用它们的 float 表示,而是使用它们作为 32 位 unsigned int 的按位表示。也就是说,像-495.5 这样的数字具有0b110000111110010111000000000000000xC3E5C000 作为float 的位表示,而我有一个具有相同位表示的unsigned int(对应于十进制值3286614016,我没有不在乎)。我有什么简单的方法可以仅使用它们各自的 unsigned int 对应对象中包含的信息对这些浮点数执行像 <= 这样的操作吗?

【问题讨论】:

  • 您可以按照 cpalmer 在下面提到的方式进行操作,但请注意,IEEE 浮点数的无符号整数表示的排序方式与其等效浮点值的排序方式不同。例如,当将它们作为无符号整数进行比较时,-0.0 与 0.0 不同。
  • 你打错字了:应该是-459.5, not -495.5

标签: c++ objective-c c floating-point


【解决方案1】:

除非您确保所有原始值都是正数,否则您必须进行有符号比较。您必须使用与原始浮点类型大小相同的整数类型。每个芯片可能具有不同的内部格式,因此将来自不同芯片的值作为整数进行比较很可能会产生误导性结果。

大多数浮点格式看起来像这样:sxxxmmmm

s 是符号位
xxx 是指数
mmmm 是尾数

表示的值将类似于:1mmm << (xxx-k)

1mmm 因为有一个隐含的前导 1 位,除非该值为零。

如果xxx < k 则将是右移。 k 接近但不等于xxx 可以表示的最大值的一半。它根据尾数的大小进行调整。

总而言之,不管NaN,将浮点值与相同大小的有符号整数进行比较将产生有意义的结果。它们是这样设计的,因此浮点比较不会比整数比较昂贵。有编译器优化关闭NaN检查,如果芯片的浮点格式支持,比较是直接整数比较。

作为整数,NaN 大于无穷大于有限值。如果您尝试无符号比较,所有负值都将大于正值,就像有符号整数转换为无符号一样。

【讨论】:

  • 我同意。我使用无符号值和符号位进行了一些测试,虽然有点难看,但完全有效。
  • 有符号整数比较通常不会给出与负数浮点比较相同的顺序。大多数 C 和 C++ 实现使用二进制补码,其中表示的负值与相同位的无符号解释具有相同的顺序(相对于其他负值)。大多数浮点表示使用符号和大小表示,其中负值与其无符号整数解释的顺序相反。
【解决方案2】:

如果您真的不关心转化产生的结果,那也不是难事。但是结果是非常不可移植的,而且您几乎肯定不会得到与直接比较浮点数所得到的完全相似的排序。

typedef unsigned int TypeWithSameSizeAsFloat; //Fix this for your platform

bool compare1(float one, float two)
    union Convert {
        float f;
        TypeWithSameSizeAsFloat i;
    }
    Convert lhs, rhs;
    lhs.f = one;
    rhs.f = two;
    return lhs.i < rhs.i;
}

bool compare2(float one, float two) {
    return reinterpret_cast<TypeWithSameSizeAsFloat&>(one) 
         < reinterpret_cast<TypeWithSameSizeAsFloat&>(two);
}

只需了解注意事项,然后仔细选择您的第二种类型。无论如何,它几乎毫无价值。

【讨论】:

  • 在 C 中很难使用 reinterpret_cast。
  • @Richard Pennington:然后用 C 风格的演员替换。但是,由于不推荐使用 C 风格的强制转换,因此默认的写作应该尽可能地使用正确的强制转换而不是传统的强制转换。 (除非您绝对必须在此处具有 C 兼容性)
  • @Billy ONeal 该问题被标记为 C、C++ 和 Objective-c。哪种演员表适用于所有情况?
  • @Billy,他实际上是在问如何做一件他无论如何都不会做的事情。他使用什么演员并不重要。
  • 走吧,不必要的打字! reinterpret_cast&lt;TypeWithSameSizeAsFloat&amp;&gt;(one) &lt; reinterpret_cast&lt;TypeWithSameSizeAsFloat&amp;&gt;(two)。 (Psst,你需要在convert2中使用onetwo。)
【解决方案3】:

总之,没有。 IEEE 754 可能允许像这样的某些类型的 hack,但它们并非始终有效并处理所有情况,并且某些平台不使用该浮点标准(例如 x87 上的双精度在内部具有 80 位精度)。

如果您出于性能原因这样做,我强烈建议您重新考虑 - 如果使用整数比较更快,编译器可能会为您执行此操作,如果不是,您需要支付浮点到整数的转换多次,可以在不将浮点数移出寄存器的情况下进行简单比较。

【讨论】:

  • 如果 IEEE 754 允许这样的黑客攻击(而且确实如此),那么它什么时候不能工作?它不处理哪些情况?
  • Martinho:在浮点单元内置到 x86 芯片之前,它们有 8087、80287 和 80387 之类的名称。所以我们将 FPU 指令称为 x87。
  • 最初的数学协处理器是一个配套芯片,例如287配80286,387配80386。现在FPU是主CPU核心的一部分,但它仍然实现旧x87芯片的指令集。
  • @gabe:有几个有效的双精度数不能像整数一样完美排序,例如 NaN(不是数字)和正/负无穷大的标准表示。如果我没记错的话,大多数“正常”数字都会在 IEEE 754 下正确排序。
  • 重要的是,这种比较可能会或可能不会带来性能优势:浮点比较本身非常快,可能有也可能没有开销设置积分比较。加上比较不可移植的事实,并且它们并非在所有情况下都有效[即使您愿意忽略 NaN 和无穷大,正零和负零很容易出错],然后您结束提出了一个非常有说服力的论点,即只信任编译器,直到你有理由不这样做。
【解决方案4】:

也许我误读了这个问题,但我想你可以这样做:

bool compare(float a, float b)
{
    return *((unsigned int*)&a) < *((unsigned int*)&b);
}

但这假设了各种各样的事情,并且还保证了为什么要比较两个浮点数的按位表示的问题。

【讨论】:

  • 啊,古老的强制指针破解。丑陋但强大。
  • 如果您要这样做,至少使用reinterpret_cast 将其标记为特定于平台的 hack。
  • @BillyONeal - 问题被标记为 C、C++ 和 Objective-C。 reinterpret_cast 仅适用于 C++。
  • @R Samuel Klatchko:我们已经在 Dennis Zickefoose 的回答中击败了这匹死马。我认为即使存在这样的标签也应该使用 C++ 版本,因为它很容易从 C++ 转换为 C 样式转换,但并不总是相反。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-10
  • 1970-01-01
  • 1970-01-01
  • 2017-02-15
  • 2011-10-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多