小尾数格式的优点是什么?


140

英特尔处理器(也许还有其他处理器)使用小端格式存储。

我总是想知道为什么有人要以相反的顺序存储字节。这种格式比大端格式有什么优势吗?


1
6502是早期(第一个?)流水线处理器。我似乎记得有人声称由于管道问题,它在某些与性能相关的问题上是低端的,但现在我不知道该问题可能是什么。有什么建议么?
Steve314 2011年

1
@ Steve314:我的答案解释了一点点字节序对流水线CPU的性能有何帮助:programmers.stackexchange.com/q/95854/27874
Martin Vilcans,2011年

3
小端,大端-您必须选择一个。就像在道路的左侧或右侧行驶。

3
我建议您用ASM编写一些代码,最好用于“老式”架构,例如6502或Z80。您将立即明白为什么这些使用小尾数法。使用大字节序的体系结构对其指令集具有某些特征,从而使其更可取。这不是一个任意决定!
Stefan Paul Noack

2
每个字节顺序系统都有其优势。Little-endian机器使您可以先读取最低字节,而无需读取其他字节。您可以很容易地检查一个数字是奇数还是偶数(最后一位为0),如果您喜欢这种方法,那很酷。大端系统将数据存储在内存中的方式与人类对数据的思考方式(从左到右)相同,这使底层调试更加容易。
Koray Tugay

Answers:


198

无论哪种方式都有论点,但有一点是,在低端字节序系统中,内存中给定值的地址(即32位,16位或8位宽度)是相同的。

换句话说,如果您的内存中有两个字节的值:

0x00f0   16
0x00f1    0

将“ 16”作为16位值(在大多数32位系统上为c“ short”)或作为8位值(通常为c“ char”)只会更改您使用的获取指令-不会更改您获取的地址从。

在大端系统上,上述内容布置为:

0x00f0    0
0x00f1   16

您将需要增加指针,然后对新值执行更窄的获取操作。

因此,简而言之,“在小端序系统上,强制转换是不可操作的”。


3
当然,假设您可以合理地忽略未读的高位字节(例如,无论如何您都知道它们为零)。
Steve314 2011年

10
@ Steve314:如果我在C中将2的补码系统(绝大多数系统)中的位从32位向下转换为16位(例如),则字节不必为零即可被忽略。不管它们的价值如何,我都可以忽略它们,并保持其与C标准和程序员的期望相符。

9
@Stritzinger-我们谈论的是由编译器生成的汇编/机器代码,它不能移植。可以编译的高级语言代码是可移植的-只需编译为不同体系结构上的不同操作即可(就像所有操作一样)。
jimwise 2011年

7
我不赞成这种说法,因为在大端体系结构中,指针可能指向所指对象的末尾而不是起点,因此您将拥有完全相同的优势。
dan_waterworth 2011年

4
@dan_waterworth不太准确-例如,请记住C中的指针算术规则,以及增加或减少同一指针的强制转换会发生什么。您可以移动复杂性,但不能消除它。
jimwise 2011年

45

我总是想知道为什么有人要以相反的顺序存储字节。

从人的角度来看,大端和小端仅是“正常顺序”和“反向顺序”,只有在所有这些都是真实的情况下才可以...

  1. 您正在读取屏幕或纸上的值。
  2. 您将较低的内存地址放在左侧,而将较高的地址放在右侧。
  3. 您正在用十六进制编写,左侧是高阶半角或二进制,左侧是最高有效位。
  4. 您从左到右阅读。

这些都是与CPU无关的人类惯例。如果要保留#1和#2并翻转#3,则小字尾对于阅读阿拉伯语或希伯来语(从右到左书写)的人来说似乎“完全自然”。

还有其他使大尾数看起来不自然的人类惯例,例如...

  • “较高”(最高有效)字节应位于“较高”存储地址处。

回到我主要对68K和PowerPC进行编程时,我认为big-endian是“正确的”,little-endian是“错误的”。但是,由于我一直在做更多的ARM和Intel工作,所以我已经习惯了低位优先。真的没关系。


30
实际上,数字是用阿拉伯语和希伯来语从[最高有效数字]到左[最低有效数字]来写的。
2011年

5
那么,为什么一个字节中的位以“大端”格式存储?为什么不一致?
tskuzzy 2011年

11
它们不是-按照惯例,位0最低,而位7最高。而且,由于位不能单独寻址,因此通常不能对字节中的位进行排序。当然,它们在给定的通信协议或存储介质中可能具有物理顺序,但是除非您在低级协议或硬件级别上工作,否则您无需担心此顺序。
斯图尔特

3
BlueRaja:仅按照书面约定。这与CPU体系结构没有共同之处。您可以将字节写为0-7 LSB-MSB而不是7-0 MSB-LSB,从算法的角度来看,没有任何变化。
SF。

2
@SF:“推短,流行什么,但短 ”将让你一个惊喜呢。例如,即使您没有通过压入永不弹出的字节来破坏堆栈,反之亦然... x86(32位),确实非常希望堆栈以双字对齐,并压入或弹出导致堆栈指针不能为4的倍数会导致对齐问题。即使没有,东西也会一次压入整个单词/双字/ qword /等-因此低字节仍然是弹出时获得的第一个。
cHao 2011年

41

好的,这就是我向我解释的原因:加法和减法

加或减多字节数字时,必须从最低有效字节开始。例如,如果要添加两个16位数字,则可能从最低有效字节到最高有效字节有一个进位,因此必须从最低有效字节开始才能查看是否有进位。这与您进行长加法时从最右边的数字开始的原因相同。您不能从左边开始。

考虑一个8位系统,该系统从内存中顺序提取字节。如果取最低显著字节第一,它可以开始做加法,而最显著字节从存储器中取出。这种并行性是为什么在诸如此类的系统上使用低端字节顺序时性能会更好的原因。如果必须等到从内存中取出两个字节,或者以相反的顺序取出它们,则将需要更长的时间。

这是在旧的8位系统上。在现代CPU上,我怀疑字节顺序是否会有所不同,并且仅出于历史原因才使用Little Endian。


3
嗯-这与我对大整数使用小尾数块排序的原因大致相同。我应该已经解决了。人们真的需要努力控制论现在 -我的大脑已经在一些零件和一些激进的升级的迫切需要,我不能永远等待!
Steve314 2011年

2
一个想法-6502在硬件上没有做太多的16位数学运算-毕竟是8位处理器。但是它确实使用相对于16位基址的8位有符号偏移来进行相对寻址。
Steve314 2011年

2
请注意,这个想法对于多精度整数算术(如Steve314所说)仍然很重要,但在字级别上。现在,大多数操作都不受处理器字节序的直接影响:仍然可以像GMP一样,在big-endian系统上首先存储最低有效字。小尾数处理器对于少数操作(例如,某些字符串转换?)仍然具有优势,通过一次读取一个字节可以更轻松地完成操作,因为只有在小尾数系统中,此类数字的字节顺序才是正确的。
vinc17 2014年

如果内存带宽受到限制,小端字节处理器具有优势,例如在某些具有16位内存总线的32位ARM处理器或具有8位数据总线的8088中:处理器可以只加载低半字节并执行在等待上半部分的同时添加/
订阅

13

使用8位处理器,无疑可以提高效率,您可以执行8位或16位操作,而无需使用不同的代码,也不需要缓冲额外的值。

如果您一次处理一个字节,则对于某些加法运算仍然更好。

但是没有理由大尾数会更自然-在英语中,您使用十三(小尾数)和二十三个(大尾数)


1
大尾数确实对人类来说更容易,因为它不需要重新排列字节。例如,在PC 0x12345678上存储为,78 56 34 12而在BE系统上存储为12 34 56 78(字节0在左侧,字节3在右侧)。请注意,数字越大(按位计),它需要的交换就越多。一个单词需要交换一次;一个DWORD,两次通过(总共三次交换);一个QWORD三遍(共7遍),依此类推。即(bits/8)-1交换。另一种选择是向前向后读取它们(向前读取每个字节,但向后扫描整个#)。
Synetech

一百一十三是中间字节序,或者是大字节序,其中“十三”本质上是一个非十进制数字。当我们拼出来的数字,还有来自我们使用的数字不断基约定一些小的偏差,但一旦你去掉这些特殊情况下,剩下的就是大端-数以百万计前十万,十万上百前等
Steve314

@ Synetech-幸运的是,计算机不必关心人类如何阅读它们。这就像宣称NAND闪存是因为OT”更好的
马丁贝克特

1
@ Steve314,数字的拼写单词无关紧要,这是我们编程时使用的数字读数。马丁,没有计算机不必关心人类如何阅读数字,但是如果人类易于阅读,那么编程(或其他相关工作)将变得更加容易,并且可以减少或避免某些缺陷和错误。
Synetech

@ steve314在丹麦语中,“ 95”的发音是“ fem halvfems”(5个,加上20分之四十)。
Vatine 2012年

7

日语日期约定为“ big endian”-yyyy / mm / dd。这对于排序算法非常方便,该算法可以使用简单的字符串比较与通常的“最重要的字符”规则。

类似的情况适用于存储在最重要的字段优先记录中的大端数字。字段中字节的重要性顺序与记录中字段的重要性相匹配,因此您可以使用a memcmp比较记录,而不必关心比较两个长字,四个字还是八个单独的字节。

翻转字段的重要性顺序,您将获得相同的优势,但是对于小端数字而不是大端数字。

当然,这没有什么实际意义。无论您的平台是大端还是小端,如果您确实需要,都可以订购记录字段来利用此技巧。如果您需要编写可移植的代码,这只是一种痛苦。

我还可能会提供经典呼吁的链接...

http://tools.ietf.org/rfcmarkup?url=ftp://ftp.rfc-editor.org/in-notes/ien/ien137.txt

编辑

一个额外的想法。我曾经写了一个大的整数库(看是否可以),为此,不管平台如何排序这些位中的32位宽的块,都以低位字节序存储。原因是...

  1. 许多算法自然会在最低端开始工作,并希望匹配这些端。例如,此外,进位符号会增加到越来越多的有效数字,因此从最低有效位开始是有意义的。

  2. 增大或减小值仅表示在末尾添加/删除块-无需向上/向下移动块。由于内存重新分配,可能仍需要复制,但并非经常如此。

当然,这与处理器没有明显的相关性-直到CPU由硬件大整数支持制成为止,这纯粹是库的事情。


7

没有人回答为什么这样做可能会产生后果。

考虑一个8位处理器,它可以在给定的时钟周期内从内存中加载单个字节。

现在,如果您想将一个16位值加载到(例如)您拥有的一个唯一的16位寄存器(即程序计数器)中,那么一种简单的方法是:

  • 从获取位置加载一个字节
  • 将该字节向左移8位
  • 将内存提取位置增加1
  • 加载下一个字节(到寄存器的低位部分)

结果:您只需要增加获取位置,就可以加载到更宽的寄存器的低位部分,并且只需要向左移动即可。(当然,向右移动对其他操作很有帮助,所以这有点像是侧面表演。)

这样的结果是16位(双字节)的内容按Most..Least的顺序存储。即,较小的地址具有最高有效字节-大字节序。

如果您改为尝试使用Little Endian进行加载,则需要将一个字节加载到宽寄存器的下部,然后将下一个字节加载到暂存区,将其移位,然后将其弹出到较宽寄存器的顶部。或者使用门的更复杂的安排,以便能够有选择地加载到顶部或底部字节中。

尝试使用低字节序的结果是您需要更多的硅(开关和门),或者需要更多的操作。

换句话说,就过去获得巨大回报而言,您可以在最大的性能和最小的硅片面积上获得更多的回报。

这些天来,这些考虑和几乎无关紧要,但是管道填充之类的事情可能仍然有些大问题。

说到写软件,使用小字节序寻址通常会使生活更轻松。

(并且大字节序处理器在字节顺序方面倾向于大字节序,而在字节字节方面则很少。但是有些处理器很奇怪,并且将使用大字节序位排序以及字节序。这使生活变得非常美好。对于硬件设计人员来说,添加内存映射的外围设备很有趣,但对程序员没有其他影响。)


3

吉姆韦斯(Jimwise)提出了一个好观点。还有另一个问题,在小尾数法中,您可以执行以下操作:

byte data[4];
int num=0;
for(i=0;i<4;i++)
    num += data[i]<<i*8; 

OR 

num = *(int*)&data; //is interpreted as

mov dword data, num ;or something similar it has been some time

对于不受内存交换位置的明显缺点影响的程序员而言,更为直接。我个人发现big endian与自然现象相反:)。12应该存储并写为21 :)


1
这只是证明以CPU固有的任何格式工作更快/更容易。它没有说是否更好。大字节序也是如此:for(i=0; i<4; i++) { num += data[i] << (24 - i * 8); }对应move.l data, num于大字节序CPU。
马丁·维尔坎斯

@martin:我的书中减去减法会更好
Cem Kalyoncu

其实并不重要,因为编译器还是会展开循环。无论如何,许多CPU都有字节交换指令来处理此问题。
Martin Vilcans 2011年

我不同意大尾数法,我会做{num << = 8; num | = data [i]; }至少这不必使用mul来计算左移计数
HayriUğurKoltuk 2011年

@ali:您的代码将执行我编写的确切操作,并且在big endian上不起作用。
Cem Kalyoncu

1

我总是想知道为什么有人会想以相反的顺序存储字节

十进制数写为大端。这也是用英语书写的方式。您从最高有效位开始,然后从最高有效位到最低有效位。例如

1234

是一千零二十四。

大尾数法有时被称为自然顺序。

用小尾数法,该数字将是一,二十,三十四和四千。

但是,当您执行加法或减法之类的算术运算时,您将从结尾开始。

  1234
+ 0567
  ====

您从4和7开始,写下最低的数字并记住进位。然后加3和6等。对于加,减或比较,如果数字已经取反,如果您已经具有按顺序读取存储器的逻辑,则实现起来更简单。

为了以这种方式支持大字节序,您需要逻辑来反向读取存储器,或者您具有仅在寄存器上运行的RISC进程。;)

许多Intel x86 / Amd x64设计都是历史悠久的。


0

大尾数法对于某些操作很有用(比较“八进制数”的八位字节长的弹簧)。小尾数表示其他字符(可能添加两个“ bignums”)。最后,这取决于设置的CPU硬件,通常是一个或另一个(某些MIPS芯片是IIRC,可在启动时切换为LE或BE)。


0

如果只涉及可变长度的存储和传输,而没有涉及多个值的算术运算,则LE通常更易于编写,而BE则更易于阅读。

让我们以int到字符串的转换为例。

int val_int = 841;
char val_str[] = "841";

将int转换为字符串时,最低有效位比最高有效位更容易提取。所有这些都可以在具有简单结束条件的简单循环中完成。

val_int = 841;
// Make sure that val_str is large enough.

i = 0;
do // Write at least one digit to care for val_int == 0
{
    // Constants, can be optimized by compiler.
    val_str[i] = '0' + val_int % 10;
    val_int /= 10;
    i++;
}
while (val_int != 0);

val_str[i] = '\0';
// val_str is now in LE "148"
// i is the length of the result without termination, can be used to reverse it

现在按BE顺序尝试相同的操作。通常,您需要另一个除数,该除数对特定数字(此处为100)具有10的最大幂。当然,您首先需要找到它。还有更多要做的事情。

将字符串转换为int作为反向写入操作时,在BE中更容易实现。写操作最后存储最高有效位,因此应先读取。

val_int = 0;
length = strlen(val_str);

for (i = 0; i < length; i++)
{
    // Again a simple constant that can be optimized.
    val_int = 10*val_int + (val_str[i] - '0');
}

现在按LE顺序执行相同的操作。同样,您需要一个附加因子,以1开头,然后将每个数字乘以10。

因此,我通常更喜欢使用BE进行存储,因为一个值只被写入一次,但至少被读取一次,也许被读取多次。对于其更简单的结构,即使它第二次写入该值,我通常也将其转换为LE,然后反转结果。

BE存储的另一个示例是UTF-8编码,等等。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.