为什么 F# 按位运算符对于有符号类型用 1 填充?
我正在查看有关按位运算的 F# 文档:
按位右移运算符。这 结果是第一个带有位的操作数 右移了位数 第二个操作数。位已移位 最不重要的位置不是 旋转到最重要的 位置。对于无符号类型,最 重要位填充为 零。对于有符号类型,最 重要位用 1 填充。 第二个参数的类型是 int32。
与 MSB 用零填充的 C++ 语言(也可能是 C)相比,这种设计选择背后的动机是什么?例如:
int mask = -2147483648 >> 1; // C++ code
其中 -2147483648 =
10000000 00000000 00000000 00000000
并且掩码等于 1073741824
其中 1073741824 =
01000000 00000000 00000000 00000000
现在,如果您在 F#(或 C#)中编写相同的代码,这确实会用 1 填充 MSB,您将得到 -1073741824。
其中-1073741824 =
11000000 00000000 00000000 00000000
I was looking at F# doc on bitwise ops:
Bitwise right-shift operator. The
result is the first operand with bits
shifted right by the number of bits in
the second operand. Bits shifted off
the least significant position are not
rotated into the most significant
position. For unsigned types, the most
significant bits are padded with
zeros. For signed types, the most
significant bits are padded with ones.
The type of the second argument is
int32.
What was the motivation behind this design choice comparing to C++ language (and probably C too) where MSB are padded with zeros? E.g:
int mask = -2147483648 >> 1; // C++ code
where -2147483648 =
10000000 00000000 00000000 00000000
and mask is equal to 1073741824
where 1073741824 =
01000000 00000000 00000000 00000000
Now if you write same code in F# (or C#), this will indeed pad MSB with ones and you'll get -1073741824.
where -1073741824 =
11000000 00000000 00000000 00000000
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
有符号移位有一个很好的特性,即 x 右移 n 对应于 Floor(x/2n)。
在 .NET 上,两种类型的操作都有 CIL 操作码(
shr
执行有符号移位,shr.un
执行无符号移位)。 F# 和 C# 根据要移位的类型的符号来选择要使用的操作码。这意味着如果您想要其他行为,您只需要在移位之前和之后执行数字转换(由于数字在 CLR 上的存储方式,这实际上没有运行时影响 - 堆栈上的 int32 与 uint32 没有区别) 。The signed shift has the nice property that shifting x right by n corresponds to floor(x/2n).
On .NET, there are CIL opcodes for both types of operations (
shr
to do a signed shift andshr.un
to do an unsigned shift). F# and C# choose which opcode to use based on the signedness of the type which is being shifted. This means that if you want the other behavior, you just need to perform a numeric conversion before and after shifting (which actually has no runtime impact due to how numbers are stored on the CLR - an int32 on the stack is indistinguishable from a uint32).回答改革后的问题(在评论中):
C 和 C++ 标准没有定义右移负值的结果(它要么是实现定义的,要么是未定义的,我不记得是哪一个)。
这是因为该标准的定义是为了反映底层指令集的最低公分母。例如,如果指令集不包含
asr
原语,则强制执行真正的算术移位需要多条指令。由于该标准要求或一个或两个补码表示,这一事实使情况变得更加复杂。To answer the reformed question (in the comments):
The C and C++ standards do not define the result of right-shifting a negative value (it's either implementation-defined, or undefined, I can't remember which).
This is because the standard was defined to reflect the lowest common denominator in terms of underlying instruction set. Enforcing a true arithmetic shift, for instance, takes several instructions if the instruction set doesn't contain an
asr
primitive. This is further complicated by the fact that the standard mandates either one's or two's complement representation.