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