【问题标题】:IEE 754 total order in standard C++11标准 C++11 中的 IEEE 754 总顺序
【发布时间】:2013-11-20 13:33:02
【问题描述】:

根据IEEE floating point wikipage(在 IEEE 754 上),在双精度浮点上有一个 total order(即在具有 IEEE-754 浮点数的 C++11 实现上,如 Linux / x86-64 上的 gcc 4.8 )。

当然,double 上的operator < 经常提供总顺序,但 NaN 被认为是例外(众所周知,x != x 是一种测试 x 是否声明为 @ 987654329@ 是一个 NaN)。

我问的原因是我想拥有 a.g. std::set<double>(实际上是一组类似 JSON 或类似 Python 的值)并且我希望该集合具有一些规范表示(我实际关心的是发出可移植的 JSON 相同的数据,以相同的顺序排序,两者在 Linux/x86-64 和例如 Linux/ARM 上,即使在像 NaN 这样的奇怪情况下)。

我找不到任何简单的方法来获得总订单。我编码了

// a totally ordering function, 
// return -1 for less-than, 0 for equal, +1 for greater
int mydoublecompare(double x, double y) { 
   if (x==y) return 0;
   else if (x<y) return -1;
   else if (x>y) return 1;
   int kx = std::fpclassify(x);
   int ky = std::fpclassify(y);
   if (kx == FP_INFINITE) return (x>0)?1:-1;
   if (ky == FP_INFINITE) return (y>0)?-1:1;
   if (kx == FP_NAN && ky == FP_NAN) return 0;
   return (kx==ky)?0:(kx<ky)?-1:1;
}

实际上,我知道这不是一个真正的(从数学上讲)全序 (因为例如按位不同的 NaN 都是相等的),但我希望它具有相同的 (或非常接近的)几种常见架构上的行为。

有什么建议或建议吗?

(也许我不应该那么在意;而且我故意不关心signaling NaNs

总体动机是我正在编写一些动态类型解释器,它以 JSON 表示法保持其整个内存状态,并且我想确保持久状态在架构之间是稳定的,换句话说,如果我加载JSON 状态并转储它,它对于多种架构(尤其是所有 x86-64、ia-32、ARM 32 位......)保持幂等。

【问题讨论】:

  • set 不是只需要 SWO,而不是总订单吗?
  • 对不起,SWO 是什么意思?很快就找不到了! (一些弱命令)?
  • 您不需要特别处理无穷大,它们由内置比较服务。您确实需要处理正零和负零,因为它们比较相等但可以区分。
  • +-0.0 可能比不同风格的 NaN 更重要。 -0.0 == 0.0.

标签: c++11 ieee-754


【解决方案1】:

我会使用:

int totalcompare(double x, double y) {
    int64_t rx, ry;

    memcpy(&rx, &x, sizeof rx);
    memcpy(&ry, &y, sizeof ry);

    if (rx == ry) return 0;

    if (rx < 0) rx ^= INT64_MAX;
    if (ry < 0) ry ^= INT64_MAX;

    if (rx < ry) return -1; else return 1;
 }

这使得0.0-0.0 比较不相等,而您的版本中的if (x==y) return 0; 使它们比较相等,这意味着您的版本只是一个预购版本。 NaN 值高于其余值,不同的 NaN 比较不同。 &lt;= 的所有可比较值都应与上述关系的顺序相同。

注意:上面的函数是C,我不懂C++。

【讨论】:

  • 你确定它不依赖于架构吗? (double 的字节序不能与int64_t 不同吗?)
  • @BasileStarynkevitch 理论上,浮点值可以用不同于整数的字节序表示,但用相同的字节序表示它们有很多优点。任何注重性能的设计师的自然倾向是使它们始终具有相同的字节序,即使在双字节序处理器上也是如此。关于每个人都有的普通架构,我可以在这两种模式下保证 IA-32、x86-64 和 PowerPC。我不了解 ARM。
  • @AlanK:这对于 2 的补码机器来说是一个巧妙的技巧。你可以用sar $tmp, $rx, 63 / shr $tmp, 1 / xor $rx, $tmp`(算术右移广播符号位,然后逻辑右移1得到0或0x7fff...)无分支地做到这一点。但是 C 没有一种可移植的方式来表达算术右移。编译器通常可以将mask = (rx &lt; 0) : -1ULL : 0; 识别为具有算术右移的位广播的习语,或任何其他机器特定的 asm 技巧有用(例如 xor 到临时和 ALU 选择,结果为cmov。@987654334 @设置SF...)。
  • @chux-ReinstateMonica 我不了解 C 但联合类型双关语在 C++ 中是显式的 UB。您唯一的选择是 memcpy 或 C++20 的 bit_cast IIRC
  • @RecursiveExceptionException: C99 确实保证联合类型双关语,与 C++ 不同的是,大多数主流编译器(包括 gcc/clang/MSVC/ICC)“仅”支持作为扩展。但是,是的,C++20 bit_cast 终于成为类型双关语的非可怕可移植语法了,是时候了。
猜你喜欢
  • 2019-05-11
  • 2018-08-07
  • 1970-01-01
  • 2013-06-12
  • 2021-10-06
  • 1970-01-01
  • 1970-01-01
  • 2015-12-14
  • 2021-07-15
相关资源
最近更新 更多