ChatGPT解决这个技术问题 Extra ChatGPT

有符号整数与无符号整数

我是否正确地说有符号整数和无符号整数之间的区别是:

无符号可以持有更大的正值,没有负值。无符号使用前导位作为值的一部分,而有符号版本使用最左边的位来识别数字是正数还是负数。有符号整数可以同时包含正数和负数。

还有其他区别吗?

因为 0 is neither positive nor negative,对于无符号整数,使用术语 非负值 而不是 正值 更合适。

S
Sabito 錆兎 stands with Ukraine

无符号可以持有更大的正值,没有负值。

是的。

无符号使用前导位作为值的一部分,而有符号版本使用最左边的位来识别数字是正数还是负数。

有不同的表示有符号整数的方法。最容易可视化的是使用最左边的位作为标志 (sign and magnitude),但更常见的是 two's complement。两者都在大多数现代微处理器中使用——浮点使用符号和幅度,而整数运算使用二进制补码。

有符号整数可以同时包含正数和负数。

是的。


c
compie

我将在 x86 上讨论硬件级别的差异。除非您正在编写编译器或使用汇编语言,否则这几乎无关紧要。但很高兴知道。

首先,x86 原生支持有符号数字的 two's complement 表示。您可以使用其他表示,但这需要更多指令,并且通常会浪费处理器时间。

“本地支持”是什么意思?基本上我的意思是有一组用于无符号数字的指令和另一组用于有符号数字的指令。无符号数可以与有符号数位于相同的寄存器中,实际上您可以混合有符号和无符号指令而不必担心处理器。由编译器(或汇编程序员)来跟踪数字是否有符号,并使用适当的指令。

首先,二进制补码具有加减法与无符号数相同的性质。数字是正数还是负数都没有区别。 (因此,您只需继续ADDSUB您的号码,无需担心。)

当进行比较时,差异开始显现。 x86 有一种区分它们的简单方法:上方/下方表示无符号比较,大于/小于表示有符号比较。 (例如 JAE 表示“如果高于或等于则跳转”并且是无符号的。)

还有两组乘法和除法指令来处理有符号和无符号整数。

最后:如果你想检查溢出,你会对有符号数和无符号数做不同的检查。


G
Garbit

他只问签名和未签名。不知道为什么人们要在其中添加额外的东西。让我告诉你答案。

无符号:它仅包含非负值,即 0 到 255。 有符号:它包含负值和正值,但格式不同,例如 0 到 +127 -1 到 -128

这个解释是关于8位数字系统的。


好的。简单的。简洁的。很棒的工作。
L
Leejay Schmidt

根据我们在课堂上学到的知识,有符号整数可以表示正数和负数,而无符号整数只能表示非负数。

例如,查看一个 8 位数字:

无符号0255

有符号 值范围从 -128127


M
Michael Burr

为了完整起见,请注意几点:

这个答案只讨论整数表示。浮点数可能还有其他答案;

负数的表示可以变化。今天使用的最常见的(到目前为止 - 它在今天几乎是普遍的)是二进制补码。其他表示包括一个补码(非常罕见)和有符号幅度(非常罕见 - 可能仅用于博物馆作品),它们只是使用高位作为符号指示符,其余位表示数字的绝对值。

使用二进制补码时,该变量可以表示比正数更大的负数范围(乘一)。这是因为零包含在“正”数中(因为符号位未设置为零),而不是负数。这意味着不能表示最小负数的绝对值。

当使用一个补码或有符号幅度时,您可以将零表示为正数或负数(这是通常不使用这些表示的几个原因之一)。


如果我写 unsigned int a = -2 和 signed int b = -2 ,底层表示是否相同,我知道给无符号数赋予负值是不好的,但如果我给它,那将是什么底层表示?
Minor niggle:符号和幅度在 IEEE 浮点中使用,所以它实际上很常见。 :-)
J
Jasper Bekkers

除了第 2 点之外的一切都是正确的。有符号整数有许多不同的表示法,一些实现使用第一个,其他使用最后一个,还有一些使用完全不同的东西。这一切都取决于您正在使用的平台。


那是小端和大端的东西吗?
小端与大端与平台上字节的顺序有关。小端可能会执行 0xFF 0xFE 0x7F,而大端可能会执行 0x7F 0xFE 0xFF。
M
Mike Gleen

另一个区别是当您在不同大小的整数之间进行转换时。

例如,如果您要从字节流中提取一个整数(为简单起见说 16 位),使用无符号值,您可以这样做:

i = ((int) b[j]) << 8 | b[j+1]

(可能应该转换第二个字节,但我猜编译器会做正确的事情)

使用带符号的值,您将不得不担心符号扩展并执行以下操作:

i = (((int) b[i]) & 0xFF) << 8 | ((int) b[i+1]) & 0xFF

J
Jonathan Leffler

除了其他人所说的之外,在 C 中,您不能溢出无符号整数。该行为被定义为模算术。您可以溢出一个有符号整数,理论上(尽管在当前主流系统上不是实践中),溢出可能会触发错误(可能类似于除以零错误)。


请注意,有符号整数溢出确实会触发未定义的行为,现代编译器非常积极地发现这一点并利用它以意想不到但技术上合法的方式修改您的程序,因为它们被允许假设未定义的行为不会发生 - 粗略地说。现在这个问题比 7 年前要严重得多。
t
toddk

一般来说,这是正确的。在不了解您为什么要寻找差异的更多信息的情况下,我想不出有符号和无符号之间的任何其他区别。


s
supercat

中的有符号整数表示数字。如果 ab 是有符号整数类型的变量,则标准永远不会要求编译器将表达式 a+=b 存储到 a 中,而不是它们各自值的算术和。可以肯定的是,如果算术和不适合 a,处理器可能不能把它放在那里,但标准不会要求编译器截断或换行该值,或者如果值超出其类型的限制,请为此做任何其他事情。请注意,虽然标准不要求它,但允许 C 实现使用有符号值捕获算术溢出。

中的无符号整数表现为整数的抽象代数环,它们模数为 2 的某个幂全等,但涉及到更大类型的转换或操作的场景除外。将任意大小的整数转换为 32 位无符号类型将产生对应于与该整数模 4,294,967,296 一致的事物的成员。从 2 中减去 3 得到 4,294,967,295 的原因是,将与 3 相同的东西添加到与 4,294,967,295 相同的东西将得到与 2 相同的东西。

抽象代数环类型通常很方便。不幸的是,C 使用符号作为决定一个类型是否应该表现得像一个环的因素。更糟糕的是,无符号值在转换为较大类型时会被视为数字而不是环成员,而小于 int 的无符号值在对其执行任何算术运算时都会被转换为数字。如果 v 是等于 4,294,967,294uint32_t,则 v*=v; 应为 v=4。不幸的是,如果 int 是 64 位,那么不知道 v*=v; 可以做什么。

鉴于目前的标准,我建议在想要与代数环相关的行为的情况下使用无符号类型,在想要表示数字时使用有符号类型。不幸的是,C 以它的方式进行了区分,但它们就是这样。


b
bhavesh

是的,无符号整数可以存储大值。不,有不同的方式来显示正值和负值。是的,有符号整数可以包含正值和负值。


e
exitcode

(回答第二个问题)仅使用符号位(而不是 2 的补码),您可以得到 -0。不是很漂亮。


只是为了补充这个答案,基本上这意味着 10 == 00 这两个数字都是以 2 为底的。
M
Matthew

无符号整数比有符号整数更容易让你陷入特定的陷阱。陷阱来自这样一个事实,虽然上面的 1 和 3 是正确的,但两种类型的整数都可以被分配一个超出它可以“持有”的范围的值,并且它将被静默转换。

unsigned int ui = -1;
signed int si = -1;

if (ui < 0) {
    printf("unsigned < 0\n");
}
if (si < 0) {
    printf("signed < 0\n");
}
if (ui == si) {
    printf("%d == %d\n", ui, si);
    printf("%ud == %ud\n", ui, si);
}

当你运行它时,即使这两个值都被赋值为 -1 并且声明不同,你也会得到以下输出。

signed < 0
-1 == -1
4294967295d == 4294967295d

C
Clearer

中有符号和无符号值之间唯一保证的区别是有符号值可以是负数、0 或正数,而无符号数只能是 0 或正数。问题是 C 没有定义类型的格式(所以你不知道你的整数是二进制补码)。严格来说,你提到的前两点是不正确的。


F
Fahad Naeem

在嵌入式系统上编程时必须使用无符号整数。在循环中,当不需要有符号整数时,使用无符号整数将节省设计此类系统所需的安全性。


A
Aaron A.

我在这方面找到的最佳答案是 IBM 引用了 XDR standard

整数 XDR 有符号整数是一段 32 位数据,它对 [-2147483648,2147483647] 范围内的整数进行编码。整数以二进制补码表示。最高和最低有效字节分别是 0 和 3。整数的数据描述是整数。无符号整数 XDR 无符号整数是一段 32 位数据,它对 [0,4294967295] 范围内的非负整数进行编码。它由一个无符号二进制数表示,其最高和最低有效字节分别为 0 和 3。无符号整数的数据描述是无符号的。

请参阅 Wikipedia 上的 XDR 标准


h
hl037_

这都是关于建模的:当你想设计一台计算机时,你需要采用约定来表示数据以及如何计算它们。当然,为不同的模型提供不同的操作和属性(性能、所需的内存空间、硬件实现复杂性等)

事实证明,对于基于电力(因此是电子)的计算,我们发现表示信息的最方便的方法是使用电压电平。 ...用这些电压电平计算最方便的方法是考虑两种状态:有电压和没有电压。 “位”来了。

这就是为什么我们使用二进制来表示数字的原因:一连串具有高电压 (1) 或低电压 (0) 的电子引脚。

但是,如果使用二进制数,则只能表示自然数(0、1、2、...)。正好 2^n(其中 n 是您拥有的位数)数字。

如果您确保第一个操作数大于第二个操作数,则这允许您进行加法乘法、除法和减法,如果您检查结果不会超过您拥有的位数,则结束。

然后,一些聪明人来了,但是:“当你用完全相同的算法做 n - m 时会发生什么?”

...而且发生的情况是它实际上有点工作:如果您之后有进位(环绕),您只需在您的号码上加一个,并考虑 0...0 和 1...1 代表 0 . 这是一个补码 Ones'_complement 但是,这样做,您必须为符号保留一位。从技术上讲,您可以表示 -(2^(n-1)-1) ≤ n ≤ 2^(n-1)-1 的值,它们是:(2^n)-1(0 的两种表示形式)。在这种表示中,您只需交换所有位即可否定一个数字。

然后更聪明的人来告诉“如果我们认为当我们否定这个数字时总是有一个环绕怎么办?” ...这意味着您在交换位后添加一个。你得到 2 的补码 Two's complement 使用它,你的零只有一个表示,你可以再次表示 2^n 个数字(其中 2^(n-1) ≤ n ≤ 2^(n-1)-1 )。另外,a-b的计算实际上就是a+(-b),它只需要两种操作:add(a, add(swap(b), 1)))

的补码的另一个好处是加法算法与无符号算法相同。因此,您可以获得相同的属性,并使用相同的硬件来完成这两项工作。这就是为什么它是大多数计算机中使用的表示。

简而言之,有符号和无符号可以表示相同的数字计数,但在不同的范围内,现在,您知道确切的数字和原因。有关获得的代数结构的更多详细信息,请阅读此回复:https://stackoverflow.com/a/23304179/1745291

然后根据上下文使用一种或另一种(但请注意,对于某些操作,如 <,转换时的处理是不同的:((signed) -1) < 5((unsigned) -1) > 5