Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do you do an unsigned/logical shift right on an Integer in Haskell?

shiftR in Data.Bits does an arithmetic shift for Integers. Is there a standard way to do a logical shift right?

I can think of two round about ways to do this. Take the quotient by 2^n:

unsignedShiftR i n = i `quot` (2 ^ n)

Another way would be to mask the upper n bits after an arithmetic shift (although I'm not sure you can get the position for the mask).

like image 341
jp.rider63 Avatar asked Mar 05 '15 05:03

jp.rider63


2 Answers

As far as Int goes, the standard way of doing it is converting it to an unsigned type and shifting it there:

import Data.Word

ushiftR :: Int -> Int -> Int
ushiftR n k = fromIntegral (fromIntegral n `shiftR` k :: Word)
like image 69
Louis Wasserman Avatar answered Oct 31 '22 02:10

Louis Wasserman


For some reason it's not included in Data.Bits, but there's a GHC primop: uncheckedIShiftRL#. Also, GHC.Base exports a safer version as iShiftRL#:

iShiftRL# :: Int# -> Int# -> Int#
a `iShiftRL#` b | isTrue# (b >=# WORD_SIZE_IN_BITS#) = 0#
                | otherwise                          = a `uncheckedIShiftRL#` b

We can wrap either the GHC.Base version or the primop with varying amounts of checking:

{-# LANGUAGE MagicHash #-}

import GHC.Base
import GHC.Prim

uncheckedIShiftRL :: Int -> Int -> Int
uncheckedIShiftRL (I# n) (I# i) = I# (uncheckedIShiftRL# n i)

unsafeIShiftRL :: Int -> Int -> Int
unsafeIShiftRL (I# n) (I# i) = I# (iShiftRL# n i)

iShiftRL :: Int -> Int -> Int
iShiftRL (I# n) (I# i)
  | isTrue# (i >=# 0#) = I# (iShiftRL# n i)
  | otherwise = error "shift by negative amount"
like image 45
András Kovács Avatar answered Oct 31 '22 02:10

András Kovács