【问题标题】:In C, how to get calculate the negative of an unsigned quantity在C中,如何计算无符号数量的负数
【发布时间】:2009-11-09 12:31:09
【问题描述】:

在 K&R ANSI C 书的 A.7.4.5 节(一元减号运算符)中指出:

... 无符号数量的负数是通过从提升类型的最大值中减去提升值并加一来计算的; ...

这是如何计算的?你能举一个简短的C例子吗?

我不明白这怎么会产生负数,比如 200u:从任何整数类型(有符号或无符号)的最大值中减去 200,然后加 1 不会得到 -200。

我知道一元减号的作用——问题是我看不到结果是如何根据描述计算出来的。

【问题讨论】:

  • 显然,您知道一元减号的作用。您可能很清楚它对 signed 操作数的作用。你只是假设 unsigned 操作数的行为是相似的。你假设不正确。实际上,它对无符号操作数的行为是完全不同的(至少在语言级别上)。上面的引用正确地描述了无符号操作数的行为。

标签: c operators unary-operator


【解决方案1】:

无符号值不能负数,因此-200 不是可能的结果。

它的意思是如果UINT_MAX在你的系统上是65535,那么结果是:

unsigned a = 200;
unsigned b = -a;
long c = -a;

将在bc 中留下65336。

如果您的系统有UINT_MAX > LONG_MAX(通常是因为intlong 的大小相同),您将需要使用long long 来代替c(尽管请注意,甚至没有任何保证它很长够了)。

如果你不理解这个细节(否定一个无符号数的结果是另一个,必然是正的,无符号数)可能会导致一些意想不到的效果。例如,在此代码中,第一个示例打印"true",但第二个示例打印"false"

int a = 200;
unsigned b = 200;
if (-a < 100) printf("true\n"); else printf("false\n");
if (-b < 100) printf("true\n"); else printf("false\n");

(请注意,我们不会在任何地方存储否定运算符的结果 - 这不是问题)。

【讨论】:

  • 我不是要在无符号类型中存储负值。
  • @Ree:我不管你在哪里存储它。当您将算术运算应用于 unsigned 值时,整个评估将作为 unsigned 评估执行。这再次意味着,在您的情况下,您永远不会得到负值。同样,-200u不是是否定的。您实际上可以将它存储在一个“long int”变量(有符号)中,您仍然会得到一个值。
  • 感谢 AndreyT,我添加了 long 示例来解决这个问题。
【解决方案2】:

显然您在引用的描述中错过了 unsigned 这个词。这是本案的关键词。在 C 语言中,unsigned 量的“负数”仍然是无符号数,这意味着它并不是真正的负数。根据定义,无符号值永远不能是负数。它们总是正数或 0。C 中无符号值的算术是模算术,或者简单地说,当您对它们执行算术运算时,无符号量“环绕”。一元否定也不例外。当n 无符号时计算-n 与计算0 - n 没有区别。如果nunsigned int 并且它的值是200,那么预期的结果不是-200,而是UINT_MAX - 200 + 1,这正是这句话告诉你的。

【讨论】:

【解决方案3】:

它描述了实现modular arithmetic 的操作,即它计算一个值

a + (-a) == 0

这使得取反的无符号数的行为与取反的有符号数非常接近。

在数字表示为 two's complement 的机器上(例如 x86),只需将无符号数的位模式视为普通有符号数,并使用机器的标准“取反”指令即可完成。

【讨论】:

  • 当 C 也可以与其他表示一起使用时,为什么这本书描述了二进制补码系统?如果你有一个补码机器,结果将是 -0(实际上只是一个 0)。
  • 答案不正确。首先,引用是在谈论 unsigned 值。对于无符号值,没有“2 的补码”之类的东西。有符号表示的概念仅适用于有符号值。其次,引用中描述的行为是无符号值的标准行为(模算术),它不以任何方式与任何特定表示相关联。就我而言,这台机器可以是三元的。
  • ... 此外,-1u 的实际值当然是 65535u(在 UINT_MAX = 65535 的实现中),而不是 -1,这是在原帖中引用。
  • AndreyT 的评论绝对准确,尤其是关于该子句对非二进制整数类型的影响。
  • 我认为没有必要删除;公平地说,模数行为与二进制补码没有什么不同,许多人相对互换使用该术语。不过,一点澄清也不会放错地方=)
【解决方案4】:

另一个question 已经触及这个话题

例子

unsigned char i = -10;
printf("%u\n",i);

结果

246

【讨论】:

    【解决方案5】:

    无符号整数类型的运算使用模运算。算术模 m 与常规算术非常相似,除了结果是除以 m 时的正余数,如果你在学校没有遇到过它(更多详细信息,请参阅Wikipedia article。例如,7 - 3 模 10 是 4,而 3 - 7 模 10 是 6,因为 3 - 7 是 -4,除以 10 得到 -1 的商和 6 的余数(也可以用 0 的商来表示和 -4 的余数,但这不是它在模算术中的工作方式)。可能的整数值模 m 是从 0 到 m-1 的整数,包括 0 到 m-1。负值是不可能的,-200 不是有效的任何情况下的无符号值。

    现在,一元减号表示负数,它不是模 m 的有效值。在这种情况下,我们知道它在 0 和 m-1 之间,因为我们从一个无符号整数开始。因此,我们正在研究将 -k 除以 m。由于一个可能的值是商 0 和余数 -k,另一个可能是商 -1 和余数 m-k,所以正确答案是 m-k。

    C 中的无符号整数通常由最大值而不是模数来描述,这意味着无符号 16 位数通常被描述为 0 到 65535,或者最大值为 65535。这是描述通过指定 m-1 而不是 m。

    你的引文说负数的值是从m-1中减去它然后加1,所以-k是m - 1 - k + 1,即m - k。描述有点迂回,但它根据预先存在的定义指定了正确的结果。

    【讨论】:

      【解决方案6】:

      让我们保持简单,看一个无符号字符... 8 位,取值范围为 0-255。

      什么是 (unsigned char)-10 以及它是如何计算的?

      按照您引用的 K & R 声明,我们有:

      -10 的提升值是 10 减去 提升类型的最大值是 2551 = 246

      so (unsigned char)-10 实际上是 246。这有意义吗?

      【讨论】:

        猜你喜欢
        • 2016-05-16
        • 1970-01-01
        • 1970-01-01
        • 2018-06-24
        • 1970-01-01
        • 1970-01-01
        • 2017-10-25
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多