I have been looking at an OCaml implementation of Hash Array Mapped Tries and noticed that three distinct bit-level operators are used:
lsl
: left shiftlsr
: right shiftasr
: unsigned right shiftI would like to port this to F# but am unsure of the exact behavior of the F# bitwise operators.
The F# language reference gives two bitwise operators:
<<<
: left shift>>>
: right shiftHow would I use these operators to replicate the OCaml originals?
Regards,
Michael
Inspecting the implementation of operator <<<
and >>>
hints at a solution (https://github.com/Microsoft/visualfsharp/blob/fsharp4/src/fsharp/FSharp.Core/prim-types.fs)
let inline mask (n:int) (m:int) = (# "and" n m : int #)
[<NoDynamicInvocation>]
let inline (<<<) (x: ^T) (n:int) : ^T =
(^T: (static member (<<<) : ^T * int -> ^T) (x,n))
when ^T : int32 = (# "shl" x (mask n 31) : int #)
when ^T : uint32 = (# "shl" x (mask n 31) : uint32 #)
when ^T : int64 = (# "shl" x (mask n 63) : int64 #)
when ^T : uint64 = (# "shl" x (mask n 63) : uint64 #)
when ^T : nativeint = (# "shl" x n : nativeint #)
when ^T : unativeint = (# "shl" x n : unativeint #)
when ^T : int16 = (# "conv.i2" (# "shl" x (mask n 15) : int32 #) : int16 #)
when ^T : uint16 = (# "conv.u2" (# "shl" x (mask n 15) : uint32 #) : uint16 #)
when ^T : sbyte = (# "conv.i1" (# "shl" x (mask n 7 ) : int32 #) : sbyte #)
when ^T : byte = (# "conv.u1" (# "shl" x (mask n 7 ) : uint32 #) : byte #)
[<NoDynamicInvocation>]
let inline (>>>) (x: ^T) (n:int) : ^T =
(^T: (static member (>>>) : ^T * int -> ^T) (x,n))
when ^T : int32 = (# "shr" x (mask n 31) : int32 #)
when ^T : uint32 = (# "shr.un" x (mask n 31) : uint32 #)
when ^T : int64 = (# "shr" x (mask n 63) : int64 #)
when ^T : uint64 = (# "shr.un" x (mask n 63) : uint64 #)
when ^T : nativeint = (# "shr" x n : nativeint #)
when ^T : unativeint = (# "shr.un" x n : unativeint #)
when ^T : int16 = (# "conv.i2" (# "shr" x (mask n 15) : int32 #) : int16 #)
when ^T : uint16 = (# "conv.u2" (# "shr.un" x (mask n 15) : uint32 #) : uint16 #)
when ^T : sbyte = (# "conv.i1" (# "shr" x (mask n 7 ) : int32 #) : sbyte #)
when ^T : byte = (# "conv.u1" (# "shr.un" x (mask n 7 ) : uint32 #) : byte #)
This code uses inline IL as well type-overloading only available to F# devs but the interesting rows seems to be:
when ^T : int32 = (# "shr" x (mask n 31) : int32 #)
when ^T : uint32 = (# "shr.un" x (mask n 31) : uint32 #)
To shift right the int32
version then uses shr
(signed shift right) which corresponds to asr
, the uint32
uses shr.un
(unsigned shift right) which corresponds to lsr
.
The shift left versions all uses shl
.
So for int32
a potential solution could be:
module ShiftOps =
let inline ( lsl ) (x: int32) (n:int) : int32 = x <<< n
let inline ( lsr ) (x: int32) (n:int) : int32 = int32 (uint32 x >>> n)
let inline ( asr ) (x: int32) (n:int) : int32 = x >>> n
open ShiftOps
[<EntryPoint>]
let main argv =
printfn "0x%x" <| -1 lsr 4
printfn "0x%x" <| -1 asr 4
printfn "0x%x" <| -1 lsl 4
0
In F# the kind of right shift is determined by the first operand. If it is unsigned, then the shift will be zero-padding, i.e., asr
, otherwise it will be logical.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With