diff options
author | Ian Lynagh <igloo@earth.li> | 2011-04-22 15:21:21 +0100 |
---|---|---|
committer | Ian Lynagh <igloo@earth.li> | 2011-04-22 15:21:21 +0100 |
commit | 17f1654367b96b9dfeb44b91ab4d1214d38b2ad1 (patch) | |
tree | 38156a0a7de02a50151ce640d3a4ebddc4b6a042 /libraries/integer-simple/GHC | |
parent | 6ec3bbca8ae20b01a1a8e39d322e2fd6d5f82ef5 (diff) | |
download | haskell-17f1654367b96b9dfeb44b91ab4d1214d38b2ad1.tar.gz |
Part of #5122 "Faster conversion between Rational and Double/Float" fix
From daniel.is.fischer.
Diffstat (limited to 'libraries/integer-simple/GHC')
-rw-r--r-- | libraries/integer-simple/GHC/Integer/Logarithms.hs | 43 | ||||
-rw-r--r-- | libraries/integer-simple/GHC/Integer/Logarithms/Internals.hs | 166 |
2 files changed, 209 insertions, 0 deletions
diff --git a/libraries/integer-simple/GHC/Integer/Logarithms.hs b/libraries/integer-simple/GHC/Integer/Logarithms.hs new file mode 100644 index 0000000000..cfafe14226 --- /dev/null +++ b/libraries/integer-simple/GHC/Integer/Logarithms.hs @@ -0,0 +1,43 @@ +{-# LANGUAGE MagicHash, UnboxedTuples, NoImplicitPrelude #-} +module GHC.Integer.Logarithms + ( integerLogBase# + , integerLog2# + , wordLog2# + ) where + +import GHC.Prim +import GHC.Integer +import qualified GHC.Integer.Logarithms.Internals as I + +-- | Calculate the integer logarithm for an arbitrary base. +-- The base must be greater than 1, the second argument, the number +-- whose logarithm is sought, should be positive, otherwise the +-- result is meaningless. +-- +-- > base ^ integerLogBase# base m <= m < base ^ (integerLogBase# base m + 1) +-- +-- for @base > 1@ and @m > 0@. +integerLogBase# :: Integer -> Integer -> Int# +integerLogBase# b m = case step b of + (# _, e #) -> e + where + step pw = + if m `ltInteger` pw + then (# m, 0# #) + else case step (pw `timesInteger` pw) of + (# q, e #) -> + if q `ltInteger` pw + then (# q, 2# *# e #) + else (# q `quotInteger` pw, 2# *# e +# 1# #) + +-- | Calculate the integer base 2 logarithm of an 'Integer'. +-- The calculation is more efficient than for the general case, +-- on platforms with 32- or 64-bit words much more efficient. +-- +-- The argument must be strictly positive, that condition is /not/ checked. +integerLog2# :: Integer -> Int# +integerLog2# = I.integerLog2# + +-- | This function calculates the integer base 2 logarithm of a 'Word#'. +wordLog2# :: Word# -> Int# +wordLog2# = I.wordLog2# diff --git a/libraries/integer-simple/GHC/Integer/Logarithms/Internals.hs b/libraries/integer-simple/GHC/Integer/Logarithms/Internals.hs new file mode 100644 index 0000000000..c7aab33e53 --- /dev/null +++ b/libraries/integer-simple/GHC/Integer/Logarithms/Internals.hs @@ -0,0 +1,166 @@ +{-# LANGUAGE CPP, MagicHash, UnboxedTuples, NoImplicitPrelude #-} +{-# OPTIONS_HADDOCK hide #-} + +#include "MachDeps.h" + +-- (Hopefully) Fast integer logarithms to base 2. +-- integerLog2# and wordLog2# are of general usefulness, +-- the others are only needed for a fast implementation of +-- fromRational. +-- Since they are needed in GHC.Float, we must expose this +-- module, but it should not show up in the docs. + +module GHC.Integer.Logarithms.Internals + ( integerLog2# + , integerLog2IsPowerOf2# + , wordLog2# + , roundingMode# + ) where + +import GHC.Prim +import GHC.Integer.Type +import GHC.Integer + +default () + +-- When larger word sizes become common, add support for those, +-- it's not hard, just tedious. +#if (WORD_SIZE_IN_BITS != 32) && (WORD_SIZE_IN_BITS != 64) + +-- We don't know whether the word has 30 bits or 128 or even more, +-- so we can't start from the top, although that would be much more +-- efficient. +wordLog2# :: Word# -> Int# +wordLog2# w = go 8# w + where + go acc u = case u `uncheckedShiftRL#` 8# of + 0## -> case leadingZeros of + BA ba -> acc -# indexInt8Array# ba (word2Int# u) + v -> go (acc +# 8#) v + +#else + +-- This one at least can also be done efficiently. +-- wordLog2# 0## = -1# +{-# INLINE wordLog2# #-} +wordLog2# :: Word# -> Int# +wordLog2# w = + case leadingZeros of + BA lz -> + let zeros u = indexInt8Array# lz (word2Int# u) in +#if WORD_SIZE_IN_BITS == 64 + case uncheckedShiftRL# w 56# of + a -> + if a `neWord#` 0## + then 64# -# zeros a + else + case uncheckedShiftRL# w 48# of + b -> + if b `neWord#` 0## + then 56# -# zeros b + else + case uncheckedShiftRL# w 40# of + c -> + if c `neWord#` 0## + then 48# -# zeros c + else + case uncheckedShiftRL# w 32# of + d -> + if d `neWord#` 0## + then 40# -# zeros d + else +#endif + case uncheckedShiftRL# w 24# of + e -> + if e `neWord#` 0## + then 32# -# zeros e + else + case uncheckedShiftRL# w 16# of + f -> + if f `neWord#` 0## + then 24# -# zeros f + else + case uncheckedShiftRL# w 8# of + g -> + if g `neWord#` 0## + then 16# -# zeros g + else 8# -# zeros w + +#endif + +-- Assumption: Integer is strictly positive, +-- otherwise return -1# arbitrarily +-- Going up in word-sized steps should not be too bad. +integerLog2# :: Integer -> Int# +integerLog2# (Positive digits) = step 0# digits + where + step acc (Some dig None) = acc +# wordLog2# dig + step acc (Some _ digs) = + step (acc +# WORD_SIZE_IN_BITS#) digs + step acc None = acc -- should be impossible, throw error? +integerLog2# _ = negateInt# 1# + +-- Again, integer should be strictly positive +integerLog2IsPowerOf2# :: Integer -> (# Int#, Int# #) +integerLog2IsPowerOf2# (Positive digits) = couldBe 0# digits + where + couldBe acc (Some dig None) = + (# acc +# wordLog2# dig, word2Int# (and# dig (minusWord# dig 1##)) #) + couldBe acc (Some dig digs) = + if eqWord# dig 0## + then couldBe (acc +# WORD_SIZE_IN_BITS#) digs + else noPower (acc +# WORD_SIZE_IN_BITS#) digs + couldBe acc None = (# acc, 1# #) -- should be impossible, error? + noPower acc (Some dig None) = + (# acc +# wordLog2# dig, 1# #) + noPower acc (Some _ digs) = + noPower (acc +# WORD_SIZE_IN_BITS#) digs + noPower acc None = (# acc, 1# #) -- should be impossible, error? +integerLog2IsPowerOf2# _ = (# negateInt# 1#, 1# #) + +-- Assumption: Integer and Int# are strictly positive, Int# is less +-- than logBase 2 of Integer, otherwise havoc ensues. +-- Used only for the numerator in fromRational when the denominator +-- is a power of 2. +-- The Int# argument is log2 n minus the number of bits in the mantissa +-- of the target type, i.e. the index of the first non-integral bit in +-- the quotient. +-- +-- 0# means round down (towards zero) +-- 1# means we have a half-integer, round to even +-- 2# means round up (away from zero) +-- This function should probably be improved. +roundingMode# :: Integer -> Int# -> Int# +roundingMode# m h = + case smallInteger 1# `shiftLInteger` h of + c -> case m `andInteger` + ((c `plusInteger` c) `minusInteger` smallInteger 1#) of + r -> + if c `ltInteger` r + then 2# + else if c `gtInteger` r + then 0# + else 1# + +-- Lookup table +data BA = BA ByteArray# + +leadingZeros :: BA +leadingZeros = + let mkArr s = + case newByteArray# 256# s of + (# s1, mba #) -> + case writeInt8Array# mba 0# 9# s1 of + s2 -> + let fillA lim val idx st = + if idx ==# 256# + then st + else if idx <# lim + then case writeInt8Array# mba idx val st of + nx -> fillA lim val (idx +# 1#) nx + else fillA (2# *# lim) (val -# 1#) idx st + in case fillA 2# 8# 1# s2 of + s3 -> case unsafeFreezeByteArray# mba s3 of + (# _, ba #) -> ba + in case mkArr realWorld# of + b -> BA b |