我注意到Haskell(来自Windows上的Haskell平台的ghci 7.10.2)翻转了我在C ++中看到的QNAN (0/0 :: Double)
上的标志(测试了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似乎已经翻转了。
这是Haskell中NaN未定义语义的一部分吗?即(0/0)意味着ghc可以使用他们想要的任何NaN模式吗?那么我们在Haskell中有一个精确的方法是在浮点指定QNAN或SNAN而不依赖于特殊的IEEE库4吗?我正在为一块硬件编写一个汇编程序,可能对它的NaN味道很挑剔。
我被unsafeCoerce
焚烧了吗?我在Haskell中没有 easy 方式从float转换为bit和back。
参考文献:
std::numeric_limits<double>::quiet_NaN()
给出0x7ff8000000000000
。也在cygwin gcc 4.9.2 unsafeCoerce
非感觉。答案 0 :(得分:5)
你从你的NaN那里问了太多。根据IEEE标准,NaN上的符号位可以是任何东西。因此,编译器,处理器或浮点库可以自由地做出他们想要的任何选择,并且您将在不同的编译器,处理器和库中获得不同的结果。
特别是,对于这样的程序,常量折叠可能意味着操作由编译器而不是在目标环境中执行,具体取决于编译器的运行方式。编译器可以使用本机浮点指令,也可以使用GMP或MPFR之类的东西。这并不常见。由于IEEE标准对符号位没有任何说明,因此对于不同的实现,您将最终得到不同的值。如果您能够在开启或关闭优化功能时证明值已更改,并且不包括-ffast-math
等内容,我将不会感到完全惊讶。
作为优化的示例,编译器知道您正在计算NaN
,并且可能决定不打扰之后翻转符号位。这一切都通过不断传播发生。另一个编译器没有进行那种分析,因此它会发出翻转符号位的指令,而那些使你的处理器不能使该操作对NaN表现不同的人。
简而言之,不要试图理解NaN上的符号位。
你到底想要在这里完成什么?