与 OCaml 的 'lsr' 和 'asr' 等效的 F# 按位运算符是什么?
What are the equivalent F# bitwise operators to OCaml's 'lsr' and 'asr'?
我一直在查看 Hash Array Mapped Tries 的 OCaml 实现并注意到使用了三个不同的位级运算符:
lsl
:左移
lsr
:右移
asr
: 无符号右移
我想将其移植到 F#,但不确定 F# 按位运算符的确切行为。
F# 语言参考给出了两个按位运算符:
<<<
:左移
>>>
:右移
我将如何使用这些运算符来复制 OCaml 原件?
此致,
迈克尔
在F#中,右移的类型由第一个操作数决定。如果它是无符号的,那么移位将是零填充,即 asr
,否则它将是合乎逻辑的。
检查运算符 <<<
和 >>>
的实现提示解决方案 (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 #)
此代码使用内联 IL 以及仅适用于 F# 开发人员的类型重载,但有趣的行似乎是:
when ^T : int32 = (# "shr" x (mask n 31) : int32 #)
when ^T : uint32 = (# "shr.un" x (mask n 31) : uint32 #)
要右移 int32
版本然后使用 shr
(有符号右移)对应于 asr
,uint32
使用 shr.un
(无符号右移)右移)对应于 lsr
.
左移版本全部使用shl
。
因此对于 int32
一个潜在的解决方案可能是:
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
我一直在查看 Hash Array Mapped Tries 的 OCaml 实现并注意到使用了三个不同的位级运算符:
lsl
:左移lsr
:右移asr
: 无符号右移
我想将其移植到 F#,但不确定 F# 按位运算符的确切行为。
F# 语言参考给出了两个按位运算符:
<<<
:左移>>>
:右移
我将如何使用这些运算符来复制 OCaml 原件?
此致,
迈克尔
在F#中,右移的类型由第一个操作数决定。如果它是无符号的,那么移位将是零填充,即 asr
,否则它将是合乎逻辑的。
检查运算符 <<<
和 >>>
的实现提示解决方案 (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 #)
此代码使用内联 IL 以及仅适用于 F# 开发人员的类型重载,但有趣的行似乎是:
when ^T : int32 = (# "shr" x (mask n 31) : int32 #)
when ^T : uint32 = (# "shr.un" x (mask n 31) : uint32 #)
要右移 int32
版本然后使用 shr
(有符号右移)对应于 asr
,uint32
使用 shr.un
(无符号右移)右移)对应于 lsr
.
左移版本全部使用shl
。
因此对于 int32
一个潜在的解决方案可能是:
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