【问题标题】:C language: left shift value with negative sign [duplicate]C语言:带负号的左移值[重复]
【发布时间】:2013-08-25 13:28:53
【问题描述】:

我对移位后的否定操作感到困惑。 例如:

-(1<<7) is 0xffffff80

但为什么最高有效位用 1 填充? 我对- 操作在这里的含义感到困惑。

编辑 1:我使用 printf("%#08x\n", -(1&lt;&lt;7)) 打印出值。

【问题讨论】:

  • 因为您的平台使用 2 的补码表示负数...
  • 维基百科link补码。
  • 不是只是大约 2 的补码。还需要讨论符号位和旋转来回答这个问题。
  • @JackCColeman:但它大约是 2 的补码。符号位都是关于 2 的补码,这里没有旋转......
  • @Cornstalks,抱歉左移,不是旋转。

标签: c


【解决方案1】:

首先,感谢您提出这个问题。通常最好编写一个测试程序来说明您不确定的事情,然后尝试不同的事情来找出细节。

评论,比如 UB,通常是不准确的。在这种情况下,发生的事情是非常可预测和合理的,这正是有经验的程序员应该期待的。

我在 Eclipse/Microsoft C 编译器上运行了以下代码:

#include <stdio.h>
main()
{
    int i;
    unsigned int j;

    i = -(1<<7);
    printf("%i\n", i);
    printf("%08x\n", i);

    j = -(1<<7);
    printf("%u\n", j);
    printf("%08x\n", j);
}

得到以下输出:

-128
ffffff80
4294967168
ffffff80

这些是预期的,因为:(1 printf 正好生成了二进制形式的-128 值。

查看这一点的方法是取 ffffff80 = 0000007f + 1 = 00000080 的 2 的补码,即二进制 128。因此,您可以看到取数字的 2 的补码就是我们取整数的负数的方式。

真正大的数字是相同内容的无符号值。

尽可能写一点代码来检查东西是如何工作的!

【讨论】:

  • 我猜 UB 代表未定义的行为。
  • 非常感谢,杰克。我确实在 64 位 Linux 中编写了一个程序,并使用了 printf(%08x)。和你的结果一样。将其打印为无符号值非常有助于理解。 :)
【解决方案2】:

减号运算符(由实现定义但最有可能)将对其参数执行二进制补码(在本例中为十进制的 128)。 2 的补码是:减 1 并将所有位取反。

【讨论】:

    【解决方案3】:

    有效位用 1 填充,因为它们代表符号,重复它们不会影响值。对于正数,100100000010 都相等。并且由于 1 的最高位表示负数,因此在负数前面放置尽可能多的 1 不会影响幅度。您可以使用 2s 补码算法检查这一事实。

    【讨论】:

    • “对于这些数字,任何 1 都不会影响幅度”你必须澄清你的意思。
    【解决方案4】:

    -(1&lt;&lt;7) 不是0xffffff801&lt;&lt;7 是 1 乘以 2 的 7 次方,即 128。所以 -(1&lt;&lt;7) 是 -128。为什么您将其视为0xffffff80,最可能的解释是您通过将其传递给printf 以与%x 格式说明符一起使用来调用未定义的行为。 %x 采用 unsigned int 参数(也可以采用带符号的 int,只要值为非负数),但您传递的参数类型为 int 并且为负数。因此,会产生未定义的行为。

    【讨论】:

    • 你确定 printf( "%x", -(1&lt;&lt;7) ) 真的是 UB(这意味着“龙可能会飞出你的鼻子”)还是仅仅依赖于平台/实现?
    • 是的,它是 UB。请参阅 C11 7.21.6.1 fprintf 函数,第 9 段:“如果任何参数不是相应转换规范的正确类型,则行为未定义。”
    • 而在第 8 段之下:"o,u,x,X unsigned int 参数被转换为无符号八进制 (o)、无符号十进制 (u) 或无符号十六进制表示法(x 或 X)”(强调我的)。
    • @IngoLeonhardt UB 并非如此。它“受制于编译器开发人员的想象力和技能、编译器分发者对其客户发生的有趣事情的容忍度,以及当前管理程序运行计算机的自然法则”。我很确定所有这些都不允许鼻龙。
    • 不,不是。 -128 的值为 -128。时期。 -128 的 表示 可能与 0xffffff80u 的表示相同也可能不同,这取决于几个因素,但 C 表达式基于值,而不是表示。
    【解决方案5】:

    答案很简单,取决于负数在计算机内存中的保存方式。

    我想你很清楚 1

    但是为什么你会得到一些不同的东西?答案如下:

    一般有两种类型的变量:有符号和无符号。 两种类型都保存在实际上不知道开始使用哪种类型的内存中。只需要程序员知道存储了什么样的数字。

    当您将变量声明为无符号时,它可以存储从 0 到 n 的值,其中 n 是某种类型变量的最大值。

    当您使用有符号时,您可以在其中存储一个从定义的负值到某个定义的正值的值。

    当使用无符号变量时,计算is值非常简单。 请考虑一个 8 位(1 字节)无符号变量的简单示例:

    前面说过最小值为0,8位全部设置后最大值为255。

    对于有符号类型的变量,使用了一种特殊的格式: 从 0 到 127 的数字的保存方式与无符号类型的保存方式相同。而 127 的值是 8 位变量的最大值。最小值为 -128,存储为 0b10000000 或 0x80。下一个是-127,保存为0b10000001或0x81等。最大的负数为 -1,保存为 0b11111111 或 0xFF。

    因此,如果您有一个字节值 0xff,它可以是:255(无符号时)或 -1(有符号时)。 此处用于有符号变量类型的符号称为 U2 - 请阅读此内容。

    在您的特定情况下,您似乎有一个有符号 (-128) 值,该值被读取为无符号值。在您的情况下,使用了 32 位(4 字节)变量(可能是(无符号)int),因此它看起来有点不同(结果更长),但您可能会看到一些相似之处:U2 中 -128 的最后两位数字无论使用多少位来存储值,系统都将始终为 0x80。

    【讨论】:

    • 非常感谢!我认为 printf(%#) 打印出值 -128 如何存储在机器中的位。这就是我看到 0xFF 的原因。 :-)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2022-11-03
    • 1970-01-01
    • 1970-01-01
    • 2019-09-01
    • 2017-02-12
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多