【问题标题】:C: unary minus operator behavior with unsigned operandsC:无符号操作数的一元减运算符行为
【发布时间】:2011-12-23 00:35:47
【问题描述】:

我似乎无法在 C 标准中找到完全定义具有无符号操作数的一元减号运算符的行为的相关部分。

2003 C++ 标准(是的,C++,请耐心等待几行)在 5.3.1c7 中说:The negative of an unsigned quantity is computed by subtracting its value from 2^n, where n is the number of bits in the promoted operand.

然而,1999 年的 C 标准并没有包含这样一个明确的声明,也没有在 6.5.3.3c1,3 和 6.5c4 中明确定义一元 - 行为。在后者中它说Some operators (the unary operator ~, and the binary operators <<, >>, &, ^, and |, ...) ... return values that depend on the internal representations of integers, and have implementation-defined and undefined aspects for signed types.),它不包括一元减号并且事情似乎仍然模糊。

This earlier question 指的是 K&R ANSI C 书的 A.7.4.5 部分,其中提到了The negative of an unsigned quantity is computed by subtracting the promoted value from the largest value of the promoted type and adding one

什么是 1999 年的 C 标准,相当于书中的上述引用?

6.2.5c9 说:A computation involving unsigned operands can never overflow, because a result that cannot be represented by the resulting unsigned integer type is reduced modulo the number that is one greater than the largest value that can be represented by the resulting type.

是这样吗?还是我还缺少其他东西?

【问题讨论】:

  • <<>> 运算符的结果不依赖于整数的表示。它们被定义为乘法和除以 2 的幂,并且仅对非负操作数进行了明确定义。 << 对于负操作数是未定义的,>> 对于负操作数是实现定义的。
  • '无符号数量的负数是通过从提升类型的最大值中减去提升值并加一来计算的' - 我知道我的数学功夫与强大的 Ritchie 和Stroustrup,但这似乎是一种糟糕的,也许是疯狂的方式来处理肯定是程序员错误的情况 99.9...% 的时间。为什么不抛出一个编译错误,说“你已经尝试将符号应用于无符号类型,你 dingus”?
  • @CCJ 然后你必须以像1 + ~a 这样的怪异方式来解决这个编译器的智能问题,以关闭编译器,这样就会有人不明白这个表达式的含义。 :)

标签: c unary-operator


【解决方案1】:

是的,6.2.5c9 正是您要查找的段落。

【讨论】:

  • 另外,可以将负运算符视为产生其操作数的加法逆。如果 N 是无符号的,则 -N 等价于 1U+~N,因为这是与 N 相加的值。
  • @supercat 所以即使底层硬件使用不同的负数表示,你也能保证二进制补码行为?
  • @JAB:二进制补码表示恰好匹配无符号表示,但行为的定义与二进制补码无关。
【解决方案2】:

在我所知道的每个实现中,负数都被计算为two's complement...

int a = 12;
int b = -a;
int c = ~a + 1;
assert(b == c);

...所以负有符号整数和“负”无符号整数之间实际上没有物理区别 - 唯一的区别在于它们解释的方式。

所以在这个例子中......

unsigned a = 12;
unsigned b = -a;
int c = -a;

...bc 将包含完全相同的位。唯一的区别是b 被解释为 2^32-12(或 2^64-12),而c 被解释为“正常”-12。

因此,无论“符号”如何,都以完全相同的方式计算负数,并且无符号和有符号之间的转换实际上是无操作(并且在某些位需要的意义上永远不会导致溢出)被“切断”)。

【讨论】:

  • 我也从实践中知道这一切。问题是关于不同的事情,即符合标准的行为。
  • 我不知道这个,它似乎是基础的东西,ta。 +1
【解决方案3】:

一元减号运算符对无符号操作数的行为与机器是否对有符号数使用二进制补码算法无关。相反,给定unsigned int x,y; 语句y=-x; 将导致y 接收它必须保持的任何值以使x+y 等于0。如果x 为零,y 也将为零。对于x 的任何其他值,它将是UINT_MAX-x+1,在这种情况下,x+y 的算术值将是UINT_MAX+1+(y-y),当分配给unsigned integer 时,将从中减去UINT_MAX+1,产生零。

【讨论】:

    【解决方案4】:

    这已经晚了,但无论如何......

    C 声明(以一种相当困难的方式,正如其他答案中已经提到的那样)

    • 任何无符号类型都是具有特定类型的二进制表示 位数

    • 对无符号类型的所有算术运算都已完成 (mod 2^N), 'mod' 是模数的数学定义,“N”是 用于表示类型的位数。

    应用于无符号类型的一元减号运算符的行为就像该值已被提升为下一个更大的有符号类型,然后取反,然后再次转换为无符号并截断为源类型。 (这是一个轻微的简化,因为整数提升适用于所有位数少于 'int' 的类型,但我认为它已经足够接近了。)

    某些编译器在将一元减号应用于无符号类型时确实会发出警告,但这仅仅是为了程序员的利益。恕我直言,该构造定义明确且可移植。

    但如果有疑问,请不要使用一元减号:写 '0u - x' 而不是 '-x',一切都会好起来的。除非完全禁用优化,否则任何体面的代码生成器都会从中创建一个否定指令。

    【讨论】:

    • 如果我没记错的话,Visual Studio 也不喜欢0u - x,所以我不得不写1u + ~x 之类的。
    • 有些东西回到我的记忆中......对。但是,不管他们怎么称呼它,微软总是拒绝拥有一个符合标准的 C 编译器。 (好吧,也许是 C89。)不过,他们越来越近了。他们最近甚至承认,他们的运行时检查分配给更短的无符号整数实际上违反了 C 标准并放弃了这些检查。
    猜你喜欢
    • 2012-04-02
    • 2016-08-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-03
    • 1970-01-01
    • 1970-01-01
    • 2019-01-01
    相关资源
    最近更新 更多