rounded-hw-0.1.0.0: src/Numeric/Rounded/Hardware/Internal/Conversion.hs
{-# LANGUAGE HexFloatLiterals #-}
{-# LANGUAGE BangPatterns #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
module Numeric.Rounded.Hardware.Internal.Conversion
( fromInt
, fromIntF
, intervalFromInteger_default
, fromRatio
, fromRatioF
, intervalFromRational_default
) where
import Numeric.Rounded.Hardware.Internal.Rounding
import Numeric.Rounded.Hardware.Internal.RoundedResult
import Numeric.Rounded.Hardware.Internal.FloatUtil
import Data.Bits
import Data.Functor.Product
import Math.NumberTheory.Logarithms (integerLog2')
import Data.Ratio
import Control.Exception (assert)
-- import GHC.Integer.Logarithms.Internals (integerLog2IsPowerOf2#)
-- integerLog2IsPowerOf2# :: Integer -> (# Int#, Int# #)
intervalFromInteger_default :: RealFloat a => Integer -> (Rounded 'TowardNegInf a, Rounded 'TowardInf a)
intervalFromInteger_default x = case fromIntF x of Pair a b -> (a, b)
{-# SPECIALIZE intervalFromInteger_default :: Integer -> (Rounded 'TowardNegInf Float, Rounded 'TowardInf Float) #-}
{-# SPECIALIZE intervalFromInteger_default :: Integer -> (Rounded 'TowardNegInf Double, Rounded 'TowardInf Double) #-}
intervalFromRational_default :: RealFloat a => Rational -> (Rounded 'TowardNegInf a, Rounded 'TowardInf a)
intervalFromRational_default x = case fromRatioF (numerator x) (denominator x) of Pair a b -> (a, b)
{-# SPECIALIZE intervalFromRational_default :: Rational -> (Rounded 'TowardNegInf Float, Rounded 'TowardInf Float) #-}
{-# SPECIALIZE intervalFromRational_default :: Rational -> (Rounded 'TowardNegInf Double, Rounded 'TowardInf Double) #-}
fromInt :: RealFloat a => RoundingMode -> Integer -> a
fromInt r n = withRoundingMode (fromIntF n) r
{-# SPECIALIZE fromInt :: RoundingMode -> Integer -> Float #-}
{-# SPECIALIZE fromInt :: RoundingMode -> Integer -> Double #-}
fromIntF :: forall a f. (RealFloat a, Result f) => Integer -> f a
fromIntF !_ | floatRadix (undefined :: a) /= 2 = error "radix other than 2 is not supported"
fromIntF 0 = exact 0
fromIntF n | n < 0 = negate <$> withOppositeRoundingMode (fromPositiveIntF (- n))
| otherwise = fromPositiveIntF n
{-# INLINE fromIntF #-}
-- n > 0
fromPositiveIntF :: forall a f. (RealFloat a, Result f) => Integer -> f a
fromPositiveIntF !n
= let !k = integerLog2' n -- floor (log2 n)
-- 2^k <= n < 2^(k+1)
!fDigits = floatDigits (undefined :: a) -- 53 for Double
in if k < fDigits
then exact (fromInteger n)
else let e = k - (fDigits - 1)
-- (!q, !r) = n `quotRem` (1 `unsafeShiftL` e)
q = n `unsafeShiftR` e
r = n .&. ((1 `unsafeShiftL` e) - 1)
-- 2^52 <= q < 2^53, 0 <= r < 2^(k-52)
(_expMin, !expMax) = floatRange (undefined :: a) -- (-1021, 1024) for Double
in if k >= expMax
then
-- infinity
inexact (1 / 0) -- ToNearest
(1 / 0) -- TowardInf
maxFinite_ieee -- TowardNegInf
maxFinite_ieee -- TowardZero
else
if r == 0
then exact $ encodeFloat q e -- exact
else
-- inexact
let down = encodeFloat q e
up = encodeFloat (q + 1) e
toNearest = case compare r (1 `unsafeShiftL` (e-1)) of
LT -> down
EQ | even q -> down
| otherwise -> up
GT -> up
in inexact toNearest up down down
{-# SPECIALIZE fromPositiveIntF :: Integer -> DynamicRoundingMode Float #-}
{-# SPECIALIZE fromPositiveIntF :: Integer -> OppositeRoundingMode DynamicRoundingMode Float #-}
{-# SPECIALIZE fromPositiveIntF :: Rounding r => Integer -> Rounded r Float #-}
{-# SPECIALIZE fromPositiveIntF :: Rounding r => Integer -> OppositeRoundingMode (Rounded r) Float #-}
{-# SPECIALIZE fromPositiveIntF :: Integer -> Product (Rounded 'TowardNegInf) (Rounded 'TowardInf) Float #-}
{-# SPECIALIZE fromPositiveIntF :: Integer -> OppositeRoundingMode (Product (Rounded 'TowardNegInf) (Rounded 'TowardInf)) Float #-}
{-# SPECIALIZE fromPositiveIntF :: Integer -> DynamicRoundingMode Double #-}
{-# SPECIALIZE fromPositiveIntF :: Integer -> OppositeRoundingMode DynamicRoundingMode Double #-}
{-# SPECIALIZE fromPositiveIntF :: Rounding r => Integer -> Rounded r Double #-}
{-# SPECIALIZE fromPositiveIntF :: Rounding r => Integer -> OppositeRoundingMode (Rounded r) Double #-}
{-# SPECIALIZE fromPositiveIntF :: Integer -> Product (Rounded 'TowardNegInf) (Rounded 'TowardInf) Double #-}
{-# SPECIALIZE fromPositiveIntF :: Integer -> OppositeRoundingMode (Product (Rounded 'TowardNegInf) (Rounded 'TowardInf)) Double #-}
fromRatio :: (RealFloat a)
=> RoundingMode
-> Integer -- ^ numerator
-> Integer -- ^ denominator
-> a
fromRatio r n d = withRoundingMode (fromRatioF n d) r
{-# SPECIALIZE fromRatio :: RoundingMode -> Integer -> Integer -> Float #-}
{-# SPECIALIZE fromRatio :: RoundingMode -> Integer -> Integer -> Double #-}
fromRatioF :: forall a f. (RealFloat a, Result f)
=> Integer -- ^ numerator
-> Integer -- ^ denominator
-> f a
fromRatioF !_ !_ | floatRadix (undefined :: a) /= 2 = error "radix other than 2 is not supported"
fromRatioF 0 _ = exact 0
fromRatioF n 0 | n > 0 = exact (1 / 0) -- positive infinity
| otherwise = exact (- 1 / 0) -- negative infinity
fromRatioF n d | d < 0 = error "fromRatio: negative denominator"
| n < 0 = negate <$> withOppositeRoundingMode (fromPositiveRatioF (- n) d)
| otherwise = fromPositiveRatioF n d
{-# INLINE fromRatioF #-}
-- n > 0, d > 0
fromPositiveRatioF :: forall a f. (RealFloat a, Result f)
=> Integer -> Integer -> f a
fromPositiveRatioF !n !d
= let ln, ld, e :: Int
ln = integerLog2' n
ld = integerLog2' d
e = ln - ld - fDigits
q, r, d_ :: Integer
d_ | e >= 0 = d `unsafeShiftL` e
| otherwise = d
(!q, !r) | e >= 0 = n `quotRem` d_
| otherwise = (n `unsafeShiftL` (-e)) `quotRem` d
-- e >= 0: n = q * (d * 2^e) + r, 0 <= r < d * 2^e
-- e <= 0: n * 2^(-e) = q * d + r, 0 <= r < d
-- n / d * 2^^(-e) = q + r / d_
-- 52 <= log2 q < 54
q', r', d' :: Integer
e' :: Int
(!q', !r', !d', !e') | q < (1 `unsafeShiftL` fDigits) = (q, r, d_, e)
| otherwise = let (q'', r'') = q `quotRem` 2
in (q'', r'' * d_ + r, 2 * d_, e + 1)
-- n / d * 2^^(-e') = q' + r' / d', 2^52 <= q' < 2^53, 0 <= r' < d'
-- q' * 2^^e' <= n/d < (q'+1) * 2^^e', 2^52 <= q' < 2^53
-- (q'/2^53) * 2^^(e'+53) <= n/d < (q'+1)/2^53 * 2^^(e'+53), 1/2 <= q'/2^53 < 1
-- normal: 0x1p-1022 <= x <= 0x1.fffffffffffffp+1023
in assert (n % d * 2^^(-e) == fromInteger q + r % d_) $
assert (n % d * 2^^(-e') == fromInteger q' + r' % d') $
if expMin <= e' + fDigits && e' + fDigits <= expMax
then
-- normal
if r' == 0
then
exact $ encodeFloat q' e' -- exact
else
-- inexact
let down = encodeFloat q' e'
up = encodeFloat (q' + 1) e' -- may be infinity
toNearest = case compare (2 * r') d' of
LT -> down
EQ | even q' -> down
| otherwise -> up -- q' + 1 is even
GT -> up
in inexact toNearest up down down
else
-- infinity or subnormal
if expMax <= e' + fDigits
then
-- infinity
inexact (1 / 0) -- ToNearest
(1 / 0) -- TowardInf
maxFinite_ieee -- TowardNegInf
maxFinite_ieee -- TowardZero
else
-- subnormal
-- e' + fDigits < expMin (or, e' < expMin - fDigits = -1074)
-- 0 <= rounded(n/d) <= 2^(expMin - 1) = 0x1p-1022, minimum (positive) subnormal: 0x1p-1074
let (!q'', !r'') = q' `quotRem` (1 `unsafeShiftL` (expMin - fDigits - e'))
-- q' = q'' * 2^(expMin - fDigits - e') + r'', 0 <= r'' < 2^(expMin - fDigits - e')
-- 2^(fDigits-1) <= q' = q'' * 2^(expMin - fDigits - e') + r'' < 2^fDigits
-- n / d * 2^^(-e') = q' + r' / d' = q'' * 2^(expMin - fDigits - e') + r'' + r' / d'
-- n / d = q'' * 2^^(expMin - fDigits) + (r'' + r' / d') * 2^^e'
-- 0 <= r'' < 2^(expMin - fDigits - e')
in if r' == 0 && r'' == 0
then exact $ encodeFloat q'' (expMin - fDigits) -- exact
else let down = encodeFloat q'' (expMin - fDigits)
up = encodeFloat (q'' + 1) (expMin - fDigits)
toNearest = case compare r'' (1 `unsafeShiftL` (expMin - fDigits - e' - 1)) of
LT -> down
GT -> up
EQ | r' /= 0 -> up
| even q' -> down
| otherwise -> up
in inexact toNearest up down down
where
!fDigits = floatDigits (undefined :: a) -- 53 for Double
(!expMin, !expMax) = floatRange (undefined :: a) -- (-1021, 1024) for Double
{-# SPECIALIZE fromPositiveRatioF :: Integer -> Integer -> DynamicRoundingMode Float #-}
{-# SPECIALIZE fromPositiveRatioF :: Integer -> Integer -> OppositeRoundingMode DynamicRoundingMode Float #-}
{-# SPECIALIZE fromPositiveRatioF :: Rounding r => Integer -> Integer -> Rounded r Float #-}
{-# SPECIALIZE fromPositiveRatioF :: Rounding r => Integer -> Integer -> OppositeRoundingMode (Rounded r) Float #-}
{-# SPECIALIZE fromPositiveRatioF :: Integer -> Integer -> Product (Rounded 'TowardNegInf) (Rounded 'TowardInf) Float #-}
{-# SPECIALIZE fromPositiveRatioF :: Integer -> Integer -> OppositeRoundingMode (Product (Rounded 'TowardNegInf) (Rounded 'TowardInf)) Float #-}
{-# SPECIALIZE fromPositiveRatioF :: Integer -> Integer -> DynamicRoundingMode Double #-}
{-# SPECIALIZE fromPositiveRatioF :: Integer -> Integer -> OppositeRoundingMode DynamicRoundingMode Double #-}
{-# SPECIALIZE fromPositiveRatioF :: Rounding r => Integer -> Integer -> Rounded r Double #-}
{-# SPECIALIZE fromPositiveRatioF :: Rounding r => Integer -> Integer -> OppositeRoundingMode (Rounded r) Double #-}
{-# SPECIALIZE fromPositiveRatioF :: Integer -> Integer -> Product (Rounded 'TowardNegInf) (Rounded 'TowardInf) Double #-}
{-# SPECIALIZE fromPositiveRatioF :: Integer -> Integer -> OppositeRoundingMode (Product (Rounded 'TowardNegInf) (Rounded 'TowardInf)) Double #-}