Haskell中的IEEE浮点信令NaN(sNaN)
有没有办法在Haskell中定义NaN?我找到了两种处理NAN的方法: 1) 使用0/0,这会产生相当多的错误 2) 包,它也没有信号NANHaskell中的IEEE浮点信令NaN(sNaN),haskell,floating-point,ieee-754,Haskell,Floating Point,Ieee 754,有没有办法在Haskell中定义NaN?我找到了两种处理NAN的方法: 1) 使用0/0,这会产生相当多的错误 2) 包,它也没有信号NAN PS有没有办法在不写C库的情况下将Word64位逐位转换成Double?使用数据怎么样?也许 您将使用可能浮点作为数据类型(假设您想使用浮点),并且仅x作为非NaN值x,而无表示NaN 但是,您需要至少添加一个Num实例,以便能够使用可能是Float而不是Float进行计算。您可以将fromJust用作此功能的实用函数 这表示为qNaN还是sNaN完全取决
PS有没有办法在不写C库的情况下将Word64位逐位转换成Double?使用
数据怎么样?也许
您将使用可能浮点
作为数据类型(假设您想使用浮点
),并且仅x
作为非NaN值x
,而无
表示NaN
但是,您需要至少添加一个Num
实例,以便能够使用可能是Float
而不是Float进行计算。您可以将fromJust
用作此功能的实用函数
这表示为qNaN还是sNaN完全取决于您的实现。您可以使用自定义运算符,而不是像这样的自定义类型(这样可以避免替换代码中的任何
Float
)
第二次打印
会触发错误
注意:我不确定是否有更好的格式化方法,您可能不应该在函数签名中使用具体类型。您可以使用比率1/0(无穷大)或0/0(Nan)来生成Nan/无穷大
一种更快但便携性较差的方法是使用GHC.Real
,它导出infinity
和notANumber
infinity, notANumber :: Rational
infinity = 1 :% 0
notANumber = 0 :% 0
用法:
Prelude Data.Ratio GHC.Real> fromRational notANumber :: Float
NaN
为了检查
NaN
/无限
,Prelude有两个功能isNaN
和isfinite
,您可以执行以下操作:
newtype SNaN a = SNaN { unSNaN :: a}
liftSNaN :: RealFloat a => (a -> a) -> (SNaN a -> SNaN a)
liftSNaN f (SNaN x)
| isNaN x = error "NaN"
| otherwise = SNaN . f $ x
liftSNaN' :: RealFloat a => (a -> b) -> (SNaN a -> b)
liftSNaN' f (SNaN x)
| isNaN x = error "NaN"
| otherwise = f $ x
liftSNaN2 :: RealFloat a => (a -> a -> a) -> (SNaN a -> SNaN a -> SNaN a)
liftSNaN2 f (SNaN x) (SNaN y)
| isNaN x || isNaN y = error "NaN"
| otherwise = SNaN $ f x y
liftSNaN2' :: RealFloat a => (a -> a -> b) -> (SNaN a -> SNaN a -> b)
liftSNaN2' f (SNaN x) (SNaN y)
| isNaN x || isNaN y = error "NaN"
| otherwise = f x y
instance RealFloat a => Eq (SNaN a)
where (==) = liftSNaN2' (==)
(/=) = liftSNaN2' (/=)
instance RealFloat a => Ord (SNaN a)
where compare = liftSNaN2' compare
(<) = liftSNaN2' (<)
(>=) = liftSNaN2' (>=)
(>) = liftSNaN2' (>)
(<=) = liftSNaN2' (<=)
max = liftSNaN2 max
min = liftSNaN2 min
instance (Show a, RealFloat a) => Show (SNaN a)
where show = liftSNaN' show
instance RealFloat a => Num (SNaN a)
where (+) = liftSNaN2 (+)
(*) = liftSNaN2 (*)
(-) = liftSNaN2 (-)
negate = liftSNaN negate
abs = liftSNaN abs
signum = liftSNaN signum
fromInteger = SNaN . fromInteger
instance RealFloat a => Fractional (SNaN a)
where (/) = liftSNaN2 (/)
recip = liftSNaN recip
fromRational = SNaN . fromRational
虽然我敢打赌,这最终会给某些人带来困惑!但是,如果您已经填写了所需的所有类型类,并且没有调用任何其他代码,您就不能修改硬代码特定的具体类型(而不是接受适当类型类中的任何类型),那么应该编译并使用完全相同的源代码
这基本上是对Uli Köhler的第一个建议的详细阐述,即为Maybe Float
提供Num
实例。我只是直接使用了NaN来表示NaN,而不是Nothing
,并使用isNan
来检测它们,而不是对Maybe
(或isJust
)进行案例分析
与Maybe
相比,使用新类型包装器的优点是:
Just NaN
vsNothing
)SNaN浮点
在运行时的表示方式与相应的浮点
相同。因此,只是
单元格没有额外的空间,在SNaN Float
和Float
之间来回转换是自由操作SNaN
只是一个标记,用于确定是否希望在操作中插入隐式的“if-NaN-then-explode”检查我发现了一种不可移动的方式:
{-# LANGUAGE ForeignFunctionInterface #-}
import Data.Word (Word64, Word32)
import Unsafe.Coerce
import Foreign
import Foreign.C.Types
foreign import ccall "fenv.h feenableexcept" -- GNU extension
enableexcept :: CInt -> IO ()
class HasNAN a where
signalingNaN :: a
quietNaN :: a
instance HasNAN Double where
signalingNaN = unsafeCoerce (0x7ff4000000000000::Word64)
quietNaN = unsafeCoerce (0x7ff8000000000000::Word64)
instance HasNAN Float where
signalingNaN = unsafeCoerce (0x7fa00000::Word32)
quietNaN = unsafeCoerce (0x7fc00000::Word32)
main = do
enableexcept 1 -- FE_INVALID in my system
print $ show $ 1 + (quietNaN :: Float) -- works
print $ show $ 1 + (signalingNaN :: Float) -- fails
这完全失败了。事实证明,FPU异常对Haskell来说是个坏主意。它们在默认情况下被禁用是有充分理由的。如果您在gdb中调试C/C++/其他东西,它们就可以了。我不想调试Haskell核心转储,因为它是非命令性的。启用FE\u INVALID
异常会导致0/0并添加到Data.Number.Transfinite
和GHC.Real
中的NAN崩溃。但是在enableexcept之前计算的0/0不会另外产生异常
我将在任务中使用一些简单的错误检查。我只需要在一个地方安装sNaN。可能是代替NaN,而不是信号。1+sNaN=异常,1+qNaN=qNaN。我更喜欢用一些技巧来定义sNaN。它看起来比定义Num instance简单。@user3161163简单地定义一个抛出
错误的Num
实例会有什么问题?一点也不难。使用哪种类型的NaN可能完全取决于操作符的定义……我的意思是,如果我使用Monad,我会得到qNaN@user3161163也许我没有正确理解你的问题,但为什么你会得到qNaN?也许它本身没有定义任何数值运算<代码>实例Num(可能是Float)
将是您声明的内容。例如,(+)Nothing=error“signal nan”
,(+)\Nothing=error“signal nan”
,(+)(Just a)(Just b)=(fromJust a)+(fromJust b)
?我的意思是,作为Monad和Num实例,可能以不同的方式工作,这是令人惊讶的。也许语义学比sNaN更接近qNaN。当然我可以做这样的例子。也许是我自己的也许。无论如何,这不是我问题的解决方案,我不想在我的代码中将所有浮点替换为可能的浮点。PS你的答案是非常哈斯克尔的方式,并适用于其他一些情况。支持使用isNaN
检查数字是否为NaN
,甚至RealFloat
也具有isIEEE
功能。您可以将用于更具体的功能。@bheklillr,ieee754模块也没有信号NAN。检查NAN非常简单:x==x为False。如果要将Word64
逐位转换为Double
,可以使用unsecfect
。对于大小(字节)不相同或装箱级别不同的其他类型,您可能会获得未定义的行为。您可以使用unsafeccoerce
以非常不可移植的方式在GHC中的Word64
和Double
之间逐位强制转换,但是sNaN仍然不受支持,因为它没有在Num
实例中实现。NaN
就像0.0/0
one一样。我想OP知道这一点。我发现很难解释这个问题,所以我可能回答错了。OP是问Haskell/GHC是否支持信令NaN
(第1个
import Prelude hiding (Float)
import qualified Prelude as P
type Float = SNaN P.Float
{-# LANGUAGE ForeignFunctionInterface #-}
import Data.Word (Word64, Word32)
import Unsafe.Coerce
import Foreign
import Foreign.C.Types
foreign import ccall "fenv.h feenableexcept" -- GNU extension
enableexcept :: CInt -> IO ()
class HasNAN a where
signalingNaN :: a
quietNaN :: a
instance HasNAN Double where
signalingNaN = unsafeCoerce (0x7ff4000000000000::Word64)
quietNaN = unsafeCoerce (0x7ff8000000000000::Word64)
instance HasNAN Float where
signalingNaN = unsafeCoerce (0x7fa00000::Word32)
quietNaN = unsafeCoerce (0x7fc00000::Word32)
main = do
enableexcept 1 -- FE_INVALID in my system
print $ show $ 1 + (quietNaN :: Float) -- works
print $ show $ 1 + (signalingNaN :: Float) -- fails