Haskell 将 (0/0) 设置为 qnan

Haskell sets (0/0) as qnan

我注意到 Haskell(来自 Haskell 平台 Windows 的 ghci 7.10.2)翻转了 QNAN (0/0 :: Double) 上的符号,这与我在 C++ 中看到的不同(测试 MSVS C++ 2013 和 cygwin gcc 4.9.2)。 Haskell 为 (0/0) 生成位模式 0xfff8000000000000(并且 -(0/0) 生成 0x7ff8...)。这与 C++ 实现似乎相反。

这里有一个测试程序来说明:

import Data.Word
import Unsafe.Coerce
import Text.Printf

dblToBits :: Double -> Word64
dblToBits = unsafeCoerce

test :: Double -> IO ()
test d = putStrLn $ printf "%12f       0x%x" d (dblToBits d)

go = do
  test (0/0)
  test (-(0/0))
  test (1/0)
  test (-(1/0))

这给出了输出:

      NaN       0xfff8000000000000  <- I expect 0x7F...?
      NaN       0x7ff8000000000000  <- I expect 0xFF...?
 Infinity       0x7ff0000000000000
-Infinity       0xfff0000000000000

注意,无穷大没问题,但 NaN 似乎颠倒了。

参考文献:

  1. MSVS 2013。 中的 C++ std::numeric_limits<double>::quiet_NaN() 给出 0x7ff8000000000000。还在 cygwin gcc 4.9.2
  2. 上进行了测试
  3. std::numeric_limits::quiet_NaN。声明符号位的任何含义都是实现定义的。 Haskell 是否有类似的规则?
  4. Perl semantics与MSV C++一致
  5. 可能 Haskell library IEEE
  6. 略有相关 question 使用相同的 unsafeCoerce 废话,我回过头来。

您对 NaN 的要求过高。根据 IEEE 标准,NaN 的符号位可以是任何东西。因此编译器、处理器或浮点库可以自由地做出他们想要的任何选择,并且您将在不同的编译器、处理器和库上得到不同的结果。

特别是对于这样的程序,常量折叠可能意味着操作是由编译器而不是在目标环境中执行的,具体取决于编译器的方式 运行。编译器可能会使用本机浮点指令,也可能会改用 GMP 或 MPFR 之类的指令。这并不少见。由于 IEEE 标准对符号位只字不提,因此对于不同的实现,您最终会得到不同的值。如果你能证明当你打开或关闭优化时值发生了变化,我不会感到完全惊讶,那是 而不是 包括 -ffast-math.

之类的东西

作为优化的一个例子,编译器知道你正在计算一个NaN,并且它可能决定不在之后翻转符号位。这一切都是通过不断传播发生的。另一个编译器不做那种分析,所以它发出一条指令来翻转符号位,而制造你的处理器的人不会让这个操作对 NaN 有不同的行为。

简而言之,不要试图理解 NaN 上的符号位。

你到底想在这里完成什么?