【问题标题】:subtraction between unsigned values - unexpected result无符号值之间的减法 - 意外结果
【发布时间】:2016-03-27 06:41:19
【问题描述】:

我有两个变量(test1test2),都是无符号的。 我需要检查其中哪个更大。

我试图了解如果发生溢出会发生什么。

我的第一个测试是使用 uint8_t (char) 数据类型完成的:

#include <stdio.h>
#include <stdint.h>
#include <math.h>

int main()
{
    uint8_t test1 = 0;
    printf("test1 = %d\n", test1);

    uint8_t test2 = pow(2, 8 * sizeof(test1)) - 1; //max holdable value of uint8_t
    printf("test2 = %d\n", test2);

    uint8_t test3 = test1 - test2;
    printf("test1 - test2 = %d\n", test3);

    if ((test1 - test2) == 0)
        printf("test1 == test2\n");
    if ((test1 - test2) > 0)
        printf("test1 > test2\n");
    if ((test1 - test2) < 0)
        printf("test1 < test2\n");

    if (test3 == 0)
        printf("test1 == test2\n");
    if (test3 > 0)
        printf("test1 > test2\n");
    if (test3 < 0)
        printf("test1 < test2\n");

    return 0;
}

输出:

test1 = 0                                                                                                                                                       
test2 = 255                                                                                                                                                     
test1 - test2 = 1                                                                                                                                               
test1 < test2                                                                                                                                                   
test1 > test2

什么?进行减法并将其保存在变量中,然后检查它,不同于动态检查减法吗?

我的第二个测试是使用 uint32_t (long) 数据类型完成的:

#include <stdio.h>
#include <stdint.h>
#include <math.h>

int main()
{
    uint32_t test1 = 0;
    printf("test1 = %d\n", test1);

    uint32_t test2 = pow(2, 8 * sizeof(test1)) - 1; //max holdable value of uint32_t
    printf("test2 = %lu\n", test2);

    uint32_t test3 = test1 - test2;
    printf("test1 - test2 = %d\n", test3);

    if ((test1 - test2) == 0)
        printf("test1 == test2\n");
    if ((test1 - test2) > 0)
        printf("test1 > test2\n");
    if ((test1 - test2) < 0)
        printf("test1 < test2\n");

    if (test3 == 0)
        printf("test1 == test2\n");
    if (test3 > 0)
        printf("test1 > test2\n");
    if (test3 < 0)
        printf("test1 < test2\n");

    return 0;
}

输出:

test1 = 0                                                                                                                                                       
test2 = 4294967295                                                                                                                                              
test1 - test2 = 1                                                                                                                                               
test1 > test2                                                                                                                                                   
test1 > test2

什么???现在进行减法并将其保存在变量中,然后检查它,在运行中检查减法相同吗?

所以 我期待无符号值之间的减法(没有显式转换)总是返回一个值> = 0。 但是在 IF 内做减法会导致意想不到的结果。

现在我很困惑。 有人可以向我解释这种行为吗?

【问题讨论】:

  • 在 main 正文的第 3 行:uint8_t test2 = pow(2, 8 * sizeof(test1)) - 1; 这不是很明智,如果您将 2 的幂为 8 * sizeof(test1),这将溢出 uint8_t 并获得零值.由于它是无符号的,溢出总是具有明确定义的值。因此,为了清楚起见,您应该写-1,而不是写这个。
  • 您正在努力工作 - 您认为这个问题可以简化为仅分配测试 1 和测试 2,然后是产生您期望的结果的表达式示例不期望?这里似乎有大量多余的代码要趟过。
  • @ChrisBeck : 或uint8_t test1 = ~0; 而不是将负值分配给无符号类型,或者最好还是使用UCHAR_MAXstd::numeric_limits&lt;uint8_t&gt;::max()
  • @user2104739:这里还有很多其他问题。特别是,将%dunsigned int 一起使用是不行的。而且您显然也没有在编译时发出警告。如果你这样做了,编译器会为你解释很多这些问题。当您混合有符号和无符号整数时,编译器可能会做一些令人惊讶的事情,在这种情况下,如果您最终得到 signed... 比较有符号和无符号... 是不好的,您应该采取措施确保0 文字有正确的类型,static_cast 结果是 test1-test2 等等,真正探索这个。

标签: c++ c


【解决方案1】:

有点神秘的type promotion rules 适用。在操作数为uint8_t 的第一个示例中,对于表达式:

test1 - test2

两个操作数都被隐式提升为int之前减法,并且表达式本身的类型为int

【讨论】:

  • 不幸的是,它也是不正确的。 “神秘类型提升规则”实际上并不适用于 unsigned charunsigned 8 位类型。这实际上是一个案例,其中提供的链接底部的警告“不幸的是,这不是这些规则的精​​确陈述”是相关的。无符号类型的数学运算使用模运算。
  • @Peter :正如我所说,它们是神秘的,我建议期待意外,永远不要使用 char 或 short 类型作为算术对象,并避免混合有符号和无符号表达式。也就是说,我不相信你是正确的。从 C99 6.3.1.1 开始:“如果 int 可以表示原始类型的所有值,则将该值转换为 int;否则,将其转换为 unsigned int。”。因此,虽然模运算确实发生在无符号类型上,但在计算 - 运算符时,操作数不再是无符号的。
【解决方案2】:

由于类型提升,您的uint8 将被提升为基于平台的int,在当今大多数情况下可能大于8 位(通常为32 位) ' 计算机。

这一行:

if ((test1 - test2) < 0)

如果等价于

if ((int)((int)test1 - (int)test2) < 0) = -255 < 0

这是真的。

但是,对于这一行,

uint8_t test3 = test1 - test2;

相当于

uint8_t test3 = (uint8_t)((int)test1 - (int)test2); //(uint8_t)(-255) = 1!!;

所以test1 - test2 &lt; 0test3 &gt; 0 都是(欢迎来到计算机世界!)正确!

这解释了你的结果。

但是对于uint32,因为它比原生int的等级“更高”,所以它没有被“提升”并且一直保持为uint32,或者换句话说

这一行:

if ((test1 - test2) > 0)

如果等价于

if ((uint32_t)((uint32_t)test1 - (uint32_t)test2) > 0) = large positive number > 0

还有这一行

uint32_t test3 = test1 - test2;

等价于

uint32_t test3 = (uint32_t)test1 - (uint32_t)test2; //also large positive number

因此您同时拥有test1 - test2 &gt; 0test3 &gt; 0

因此,在您的两种情况下,只有在 8 位机器上运行时,两者都是正确的。或者,假设将来原生 int 是 64 位的,那么这两种情况也都是正确的......

【讨论】:

  • 在提升后的第一个示例中,表达式为-255 &lt; 0
  • 我喜欢@Clifford 的答案,但我选择了那个答案,因为它有一个基于我的测试代码的很好的例子。谢谢大家!
  • 即使在 8 位机器上,您也不会得到与 int 的最小大小为 16 位相同的结果。
  • @user2104749 :无需解释您的偏好 - 您的问题,无论对您有用。我特意简短地说。
猜你喜欢
  • 2014-07-14
  • 1970-01-01
  • 1970-01-01
  • 2015-12-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多