C 中的负数右移

发布于 2024-08-13 16:05:17 字数 513 浏览 11 评论 0原文

我有 C 代码,我在其中执行以下操作。

int nPosVal = +0xFFFF;   // + Added for ease of understanding
int nNegVal = -0xFFFF;   // - Added for valid reason

现在,当我尝试时,

printf ("%d %d", nPosVal >> 1, nNegVal >> 1);

我得到

32767 -32768

这是预期的吗?

我可以这样想

65535 >> 1 = (int) 32767.5 = 32767
-65535 >> 1 = (int) -32767.5 = -32768

,即 -32767.5 四舍五入为 -32768。

这种理解正确吗?

I have C code in which I do the following.

int nPosVal = +0xFFFF;   // + Added for ease of understanding
int nNegVal = -0xFFFF;   // - Added for valid reason

Now when I try

printf ("%d %d", nPosVal >> 1, nNegVal >> 1);

I get

32767 -32768

Is this expected?

I am able to think something like

65535 >> 1 = (int) 32767.5 = 32767
-65535 >> 1 = (int) -32767.5 = -32768

That is, -32767.5 is rounded off to -32768.

Is this understanding correct?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

冬天旳寂寞 2024-08-20 16:05:17

看来您的实现可能正在使用二进制补码进行算术位移。在这个系统中,它将所有位向右移动,然后用最后一位的副本填充高位。因此,对于您的示例,此处将 int 视为 32 位:

nPosVal = 00000000000000001111111111111111
nNegVal = 11111111111111110000000000000001

移位后,您将得到:

nPosVal = 00000000000000000111111111111111
nNegVal = 11111111111111111000000000000000

如果将其转换回十进制,则分别得到 32767 和 -32768。

实际上,右移会转向负无穷大。

编辑:根据最新标准草案,负数的这种行为取决于实现:

E1 >> 的结果E2是E1右移E2位的位置。如果 E1 为无符号类型,或者 E1 为有符号类型且为非负值,则结果值为 E1 / 2E2 商的整数部分。如果 E1 具有有符号类型和负值,则结果值是实现定义的。

他们声明的基本原理

C89委员会确认了 K&R 授予的实施自由,不要求带符号右移操作进行符号扩展,因为这样的要求可能会减慢快速代码的速度,并且符号扩展移位的用处很小。 (移动负数的补码
整数算术右一位与除以二相同!)

所以理论上它依赖于实现。在实践中,我从未见过在左操作数有符号时执行算术右移的实现。

It looks like your implementation is probably doing an arithmetic bit shift with two's complement numbers. In this system, it shifts all of the bits to the right and then fills in the upper bits with a copy of whatever the last bit was. So for your example, treating int as 32-bits here:

nPosVal = 00000000000000001111111111111111
nNegVal = 11111111111111110000000000000001

After the shift, you've got:

nPosVal = 00000000000000000111111111111111
nNegVal = 11111111111111111000000000000000

If you convert this back to decimal, you get 32767 and -32768 respectively.

Effectively, a right shift rounds towards negative infinity.

Edit: According to the Section 6.5.7 of the latest draft standard, this behavior on negative numbers is implementation dependent:

The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1 has an unsigned type or if E1 has a signed type and a nonnegative value, the value of the result is the integral part of the quotient of E1 / 2E2. If E1 has a signed type and a negative value, the resulting value is implementation-defined.

Their stated rationale for this:

The C89 Committee affirmed the freedom in implementation granted by K&R in not requiring the signed right shift operation to sign extend, since such a requirement might slow down fast code and since the usefulness of sign extended shifts is marginal. (Shifting a negative two’s complement
integer arithmetically right one place is not the same as dividing by two!)

So it's implementation dependent in theory. In practice, I've never seen an implementation not do an arithmetic shift right when the left operand is signed.

蓝眼睛不忧郁 2024-08-20 16:05:17

不,在处理整数时,您不会得到像 0.5 这样的小数。当您查看两个数字的二进制表示时,结果可以很容易地解释:

      65535: 00000000000000001111111111111111
     -65535: 11111111111111110000000000000001

位向右移动一位,并向左扩展(请注意,这取决于实现,谢谢 Trent):

 65535 >> 1: 00000000000000000111111111111111
-65535 >> 1: 11111111111111111000000000000000

转换回十进制:

 65535 >> 1 = 32767
-65535 >> 1 = -32768

No, you don't get fractional numbers like 0.5 when working with integers. The results can be easily explained when you look at the binary representations of the two numbers:

      65535: 00000000000000001111111111111111
     -65535: 11111111111111110000000000000001

Bit shifting to the right one bit, and extending at the left (note that this is implementation dependant, thanks Trent):

 65535 >> 1: 00000000000000000111111111111111
-65535 >> 1: 11111111111111111000000000000000

Convert back to decimal:

 65535 >> 1 = 32767
-65535 >> 1 = -32768
我偏爱纯白色 2024-08-20 16:05:17

C 规范没有指定符号位是否移位。它取决于实现。

The C specification does not specify if the sign bit is shifted over or not. It is implementation dependent.

橘虞初梦 2024-08-20 16:05:17

当右移时,最低有效位将被丢弃。

0xFFFF = 0 1111 1111 1111 1111,右移为 0 0111 1111 1111 1111 = 0x7FFF

-0xFFFF = 1 0000 0000 0000 0001(2 补码),右移为 1 1000 0000 0000 0000 = -0x8000

When you right-shift, the least-significant-bit is discarded.

0xFFFF = 0 1111 1111 1111 1111, which right-shifts to give 0 0111 1111 1111 1111 = 0x7FFF

-0xFFFF = 1 0000 0000 0000 0001 (2s complement), which right-shifts to 1 1000 0000 0000 0000 = -0x8000

少女净妖师 2024-08-20 16:05:17

A-1:是的。 0xffff>> 1 是 0x7fff 或 32767。我不确定 -0xffff 的作用。这很奇特。

A-2:移位与除法不同。它是位移位——一种原始的二进制运算。它有时可用于某些类型的除法,这很方便,但并不总是相同。

A-1: Yes. 0xffff >> 1 is 0x7fff or 32767. I'm not sure what -0xffff does. That's peculiar.

A-2: Shifting is not the same thing as dividing. It is bit shifting—a primitive binary operation. That it sometimes can be used for some types of division is convenient, but not always the same.

烧了回忆取暖 2024-08-20 16:05:17

在 C 级别之下,机器具有完全整数或标量的 CPU 核心。尽管如今每个桌面 CPU 都有一个 FPU,但情况并非总是如此,即使在今天,嵌入式系统也是没有浮点指令的。

今天的编程范式、CPU 设计和语言可以追溯到 FPU 甚至可能不存在的时代。

因此,CPU 指令实现定点运算,通常被视为纯整数运算。仅当程序声明 floatdouble 项时,才会存在分数。 (好吧,你可以使用带有分数的“定点”CPU 运算,但现在这种情况一直很少见。)

无论多年前语言标准委员会的要求是什么,所有合理的机器都会在右移上传播符号位有符号的数字。无符号值的右移将在左移零。右边移出的位掉在地板上。

为了进一步理解,您需要研究“补码算术”。

Beneath the C level, machines have a CPU core which is entirely integer or scalar. Although these days every desktop CPU has an FPU, this was not always the case and even today embedded systems are made with no floating point instructions.

Today's programming paradigms and CPU designs and languages date from the era where the FPU might not even exist.

So, CPU instructions implement fixed point operations, generally treated as purely integer ops. Only if a program declares items of float or double will any fractions exist. (Well, you can use the CPU ops for "fixed point" with fractions but that is now and always was quite rare.)

Regardless of what was required by a language standard committee years ago, all reasonable machines propagate the sign bit on right shifts of signed numbers. Right shifts of unsigned values shift in zeroes on the left. The bits shifted out on the right are dropped on the floor.

To further your understanding you will need to investigate "twos-complement arithmetic".

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文