{-# Language BlockArguments, OverloadedStrings #-}
{-# Language BangPatterns #-}
module Cryptol.Eval.Concrete.FloatHelpers where
import Data.Ratio(numerator,denominator)
import Data.Int(Int64)
import Data.Bits(testBit,setBit,shiftL,shiftR,(.&.),(.|.))
import LibBF
import Cryptol.Utils.PP
import Cryptol.Utils.Panic(panic)
import Cryptol.Eval.Monad( EvalError(..)
, PPOpts(..), PPFloatFormat(..), PPFloatExp(..)
)
data BF = BF
{ bfExpWidth :: Integer
, bfPrecWidth :: Integer
, bfValue :: BigFloat
}
fpOpts :: Integer -> Integer -> RoundMode -> BFOpts
fpOpts e p r =
case ok of
Just opts -> opts
Nothing -> panic "floatOpts" [ "Invalid Float size"
, "exponent: " ++ show e
, "precision: " ++ show p
]
where
ok = do eb <- rng expBits expBitsMin expBitsMax e
pb <- rng precBits precBitsMin precBitsMax p
pure (eb <> pb <> allowSubnormal <> rnd r)
rng f a b x = if toInteger a <= x && x <= toInteger b
then Just (f (fromInteger x))
else Nothing
fpRound :: Integer -> Either EvalError RoundMode
fpRound n =
case n of
0 -> Right NearEven
1 -> Right NearAway
2 -> Right ToPosInf
3 -> Right ToNegInf
4 -> Right ToZero
_ -> Left (BadRoundingMode n)
fpCheckStatus :: (BigFloat,Status) -> BigFloat
fpCheckStatus (r,s) =
case s of
MemError -> panic "checkStatus" [ "libBF: Memory error" ]
_ -> r
fpPP :: PPOpts -> BF -> Doc
fpPP opts bf =
case bfSign num of
Nothing -> "fpNaN"
Just s
| bfIsFinite num -> text hacStr
| otherwise ->
case s of
Pos -> "fpPosInf"
Neg -> "fpNegInf"
where
num = bfValue bf
precW = bfPrecWidth bf
base = useFPBase opts
withExp :: PPFloatExp -> ShowFmt -> ShowFmt
withExp e f = case e of
AutoExponent -> f
ForceExponent -> f <> forceExp
str = bfToString base fmt num
fmt = addPrefix <> showRnd NearEven <>
case useFPFormat opts of
FloatFree e -> withExp e $ showFreeMin
$ Just $ fromInteger precW
FloatFixed n e -> withExp e $ showFixed $ fromIntegral n
FloatFrac n -> showFrac $ fromIntegral n
hacStr
| base == 10 || elem '.' str = str
| otherwise = case break (== 'p') str of
(xs,ys) -> xs ++ ".0" ++ ys
fpLit ::
Integer ->
Integer ->
Rational ->
BF
fpLit e p rat = floatFromRational e p NearEven rat
floatFromRational :: Integer -> Integer -> RoundMode -> Rational -> BF
floatFromRational e p r rat =
BF { bfExpWidth = e
, bfPrecWidth = p
, bfValue = fpCheckStatus
if den == 1 then bfRoundFloat opts num
else bfDiv opts num (bfFromInteger den)
}
where
opts = fpOpts e p r
num = bfFromInteger (numerator rat)
den = denominator rat
floatToRational :: String -> BF -> Either EvalError Rational
floatToRational fun bf =
case bfToRep (bfValue bf) of
BFNaN -> Left (BadValue fun)
BFRep s num ->
case num of
Inf -> Left (BadValue fun)
Zero -> Right 0
Num i ev -> Right case s of
Pos -> ab
Neg -> negate ab
where ab = fromInteger i * (2 ^^ ev)
floatToInteger :: String -> RoundMode -> BF -> Either EvalError Integer
floatToInteger fun r fp =
do rat <- floatToRational fun fp
pure case r of
NearEven -> round rat
NearAway -> if rat > 0 then ceiling rat else floor rat
ToPosInf -> ceiling rat
ToNegInf -> floor rat
ToZero -> truncate rat
_ -> panic "fpCvtToInteger"
["Unexpected rounding mode", show r]
floatFromBits ::
Integer ->
Integer ->
Integer ->
BF
floatFromBits e p bv = BF { bfValue = floatFromBits' e p bv
, bfExpWidth = e, bfPrecWidth = p }
floatFromBits' ::
Integer ->
Integer ->
Integer ->
BigFloat
floatFromBits' e p bits
| expoBiased == 0 && mant == 0 =
if isNeg then bfNegZero else bfPosZero
| expoBiased == eMask && mant == 0 =
if isNeg then bfNegInf else bfPosInf
| expoBiased == eMask = bfNaN
| expoBiased == 0 =
case bfMul2Exp opts (bfFromInteger mant) (expoVal + 1) of
(num,Ok) -> if isNeg then bfNeg num else num
(_,s) -> panic "floatFromBits" [ "Unexpected status: " ++ show s ]
| otherwise =
case bfMul2Exp opts (bfFromInteger mantVal) expoVal of
(num,Ok) -> if isNeg then bfNeg num else num
(_,s) -> panic "floatFromBits" [ "Unexpected status: " ++ show s ]
where
opts = expBits e' <> precBits (p' + 1) <> allowSubnormal
e' = fromInteger e :: Int
p' = fromInteger p - 1 :: Int
eMask = (1 `shiftL` e') - 1 :: Int64
pMask = (1 `shiftL` p') - 1 :: Integer
isNeg = testBit bits (e' + p')
mant = pMask .&. bits :: Integer
mantVal = mant `setBit` p' :: Integer
expoBiased = eMask .&. fromInteger (bits `shiftR` p') :: Int64
bias = eMask `shiftR` 1 :: Int64
expoVal = expoBiased - bias - fromIntegral p' :: Int64
floatToBits :: Integer -> Integer -> BigFloat -> Integer
floatToBits e p bf = (isNeg `shiftL` (e' + p'))
.|. (expBiased `shiftL` p')
.|. (mant `shiftL` 0)
where
e' = fromInteger e :: Int
p' = fromInteger p - 1 :: Int
eMask = (1 `shiftL` e') - 1 :: Integer
pMask = (1 `shiftL` p') - 1 :: Integer
(isNeg, expBiased, mant) =
case bfToRep bf of
BFNaN -> (0, eMask, 1 `shiftL` (p' - 1))
BFRep s num -> (sign, be, ma)
where
sign = case s of
Neg -> 1
Pos -> 0
(be,ma) =
case num of
Zero -> (0,0)
Num i ev
| ex == 0 -> (0, i `shiftL` (p' - m -1))
| otherwise -> (ex, (i `shiftL` (p' - m)) .&. pMask)
where
m = msb 0 i - 1
bias = eMask `shiftR` 1
ex = toInteger ev + bias + toInteger m
Inf -> (eMask,0)
msb !n j = if j == 0 then n else msb (n+1) (j `shiftR` 1)