【问题标题】:the idea behind unsigned integer [duplicate]无符号整数背后的想法[重复]
【发布时间】:2012-09-28 23:46:01
【问题描述】:

可能重复:
What happens if I assign a negative value to an unsigned variable?

我是 C++ 新手,我想知道如何使用无符号类型。对于unsigned int 类型,我知道它可以取 0 到 4294967296 之间的值。但是当我想初始化一个 unsigned int 类型时,如下所示:

unsigned int x = -10;
cout << x;

输出看起来像4294967286 得到了这个output = max value - 10。所以我想了解内存中发生了什么?在此计算继续进行时,正在执行哪些过程?感谢您的回答。

【问题讨论】:

    标签: c++ unsigned-integer


    【解决方案1】:

    您遇到了环绕行为。

    无符号类型是循环的(另一方面,有符号类型可能是循环的,也可能不是循环的,但它是您不应该依赖的未定义行为)。也就是说,比最小可能值小一是最大可能值。您可以使用以下 sn-p 自己演示这一点:

    int main()
    {
        unsigned int x = 5;
        for (int i = 0; i < 10; ++i) cout << x-- << endl;
        return 0;
    }
    

    您会注意到,在达到零后,x 的值会跳到 2^32-1,即最大可表示值。减去进一步的行为符合预期。

    当你从无符号的 0 中减去 1 时,位模式会发生如下变化:

    0000 0000 0000 0000 0000 0000 0000 0000 // before (0)
    1111 1111 1111 1111 1111 1111 1111 1111 // after  (2^32 - 1)
    

    对于无符号数,负数被视为从零减去正数。所以(unsigned int) -10 将等于((unsigned int) 0) - ((unsigned int) 10)

    我喜欢将其视为无符号整数,它是高精度任意值的最低 32 位。像这样:

    v imaginary high order bit
    1 0000 0000 0000 0000 0000 0000 0000 0000 // before (2^32)
    0 1111 1111 1111 1111 1111 1111 1111 1111 // after  (2^32 - 1)
    

    在这些溢出情况下 unsigned int 的行为与从 256 中减去 1 时 unsigned int 的低 8 位的行为完全相同。查看 unsigned char(1 字节)更有意义像这样,因为如果转换为 unsigned char,值 0 和 256 是相等的,因为有限的精度会丢弃额外的位。

    0 0000 0000 0000 0000 0000 0001 0000 0000 // before (256)
    0 0000 0000 0000 0000 0000 0000 1111 1111 // before (255)
    

    正如其他人所指出的,这称为模运算。使用更高的精度值来帮助可视化环绕工作时所做的转换,因为您屏蔽了高阶位。它是什么并不重要,所以它可以是任何东西,它只是被丢弃。整数是超过模 2^32 的值,因此 2^32 的任何倍数在整数空间中都为零。这就是为什么我可以侥幸假装最后还有一点。

    模数运算有自己的专用运算符,以防您需要在程序中计算除 2^32 以外的数字,如以下语句所示:

    int forty_mod_twelve = 40 % 12;
    // value is 4: 4 + n * 12 == 40 for some whole number n
    

    对 2 的幂(如 2^32)的模运算直接简化为屏蔽高位,如果您取一个 64 位整数并以 2^32 为模计算,则该值将与您完全一样已将其转换为无符号整数。

    01011010 01011100 10000001 00001101 11111111 11111111 11111111 11111111 // before
    00000000 00000000 00000000 00000000 11111111 11111111 11111111 11111111 // after
    

    程序员喜欢使用这个属性来加速程序,因为很容易砍掉一些位数,但执行模运算要困难得多(它与除法差不多)。

    这有意义吗?

    【讨论】:

    • 哇,这对我来说真的很解释。谢谢!
    • 很好地解释了模算术,但是我认为如果你也引入常用的术语会有所帮助。
    • 另外值得注意的是,C++ 规范要求对无符号类型进行模运算(定义了溢出),而对有符号类型只有整数运算的可表示子集(溢出是 undefined)和未指定负数的位模式。因此,每当您依赖这些东西时,您都必须使用无符号数。
    • @Cdaragorn:有符号整数类型的环绕行为未定义,因此我恢复了您的编辑。
    • Cdaragorn 的编辑更糟糕——浮点类型也是数字,而且它们当然不会回绕。
    【解决方案2】:

    这涉及标准的积分转换。这是适用的规则。我们从文字10的类型开始:

    2.14.2 整数字面量 [lex.icon]

    整数字面量是没有句点或指数部分的数字序列。整数文字可能有 一个指定其基数的前缀和一个指定其类型的后缀。序列的词汇第一个数字 位数是最重要的。十进制整数文字(以十为基数)以 0 以外的数字开头,并且 由一系列十进制数字组成。八进制整数文字(以八为基数)以数字 0 和 由一系列八进制数字组成。十六进制整数文字(以 16 为基数)以 0x0X 开头,并且 由一系列十六进制数字组成,其中包括十进制数字和字母afAF,十进制值到十五进制。 [ 示例:数字十二可以写成120140XC。 —结束示例]

    整数字面量的类型是表 6 中对应列表的第一个,其中它的值可以是 代表。

    下面是一个表格,第一个类型是int,它适合。所以字面量的类型是int

    应用一元减号运算符,不会更改类型。然后应用以下规则:

    4.7 积分转换 [conv.integral]

    整数类型的纯右值可以转换为另一种整数类型的纯右值。非作用域枚举类型的纯右值可以转换为整数类型的纯右值。

    如果目标类型是无符号的,则结果值是与源整数一致的最小无符号整数(模 2n,其中 n 是用于表示无符号类型的位数)。 [ 注意:在二进制补码表示中,这种转换是概念性的,位模式没有变化(如果没有截断)。 ——尾注]

    【讨论】:

    • 我永远不会考虑十六进制转换或类似的东西。所以,这对我来说真的很令人惊讶。谢谢。
    • @w1LL1ng:这里没有十六进制转换,因为你没有 0x 前缀。重要的部分在最后一段,它需要模算术。
    • @wug 的回答很清楚,现在我说对了。还是谢谢你。
    【解决方案3】:

    不要按你的方式打印值,而是以十六进制格式打印它(抱歉,我忘记了如何使用 cout 执行此操作,但我知道这是可能的)。您会看到这两个值的表示是相同的。

    根据您的上下文,整数是 32 位(并非总是如此)。使用有符号整数时,最高有效位是符号,而不是值的一部分。使用无符号整数时,最高有效位是值的一部分。

    【讨论】:

    • 编码只有在补码机器上是一样的。 C++ 不需要二进制补码编码,但它确实需要转换为无符号才能表现得就像是二进制补码一样。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-06
    • 1970-01-01
    • 1970-01-01
    • 2014-03-02
    • 1970-01-01
    • 1970-01-01
    • 2015-03-17
    相关资源
    最近更新 更多