Haskell中的IEEE浮点信令NaN(sNaN)

Haskell中的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完全取决

有没有办法在Haskell中定义NaN?我找到了两种处理NAN的方法:

1) 使用0/0,这会产生相当多的错误

2) 包,它也没有信号NAN


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
相比,使用新类型包装器的优点是:

  • 在转换为常规浮点时或从常规浮点转换为常规浮点时,避免将另一个“无效”值引入doing(
    Just NaN
    vs
    Nothing
  • 新类型在GHC中解除绑定;
    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