异构架构:标准委员会关注的内容

176
我知道C和C++标准之所以留下许多语言实现方面的问题,是因为如果有其他特性的架构存在,那么针对该架构的标准确认编译器将需要模拟语言的这些部分,导致低效的机器代码。

当然,40年前每台计算机都有自己独特的规格。但是,我不知道今天使用的任何体系结构中是否存在以下情况:

  • CHAR_BIT != 8
  • signed不是二进制补码(我听说Java在这一点上有问题)。
  • 浮点数不符合IEEE 754标准(编辑:我指的是“不符合IEEE 754二进制编码”)。

我提出这个问题的原因是我经常向人们解释,C++不强制执行任何其他低级别的方面,例如固定大小的类型。这很好,因为与“其他语言”不同,当正确使用时,它使您的代码可移植(编辑:因为它可以被移植到更多的体系结构而不需要模拟机器的低级方面,例如在符号+大小架构上使用二进制补码算术)。但是我感到遗憾的是我自己无法指出任何特定的体系结构。

因此,问题是:哪些体系结构表现出上述属性?

uint*_t是可选的。


9
我认为你的理解是反过来了。如果C++规定,比如,有符号整数使用二进制补码表示,那么它将使得C++代码更具可移植性而不是没有可移植性。为什么C++标准委员会不强制这样做是另一回事。尤其是,尽管你所说的不可能为非标准体系结构编写编译器,但你总是可以模拟8位字符或二进制补码算术,即使你的平台不直接支持它。 - john
12
如果是这样,那么不符合标准的编译器将会比符合标准的编译器生成更快的代码,但这种方式并不实用。我仍然不明白这如何使您的代码更具可移植性。 - Yakov Galka
5
我确定标准之所以是这样的,不是因为它是某种理想的解决方案,而是因为在标准制定时已经存在许多C和C++编译器,标准委员会不想拒绝现有的编译器。 - john
5
@john: 我怀疑在创建C++标准时,“让编译器编写更容易”并不是一个重要的优先事项(如果是的话,他们会做得很糟糕,因为C++是最难解析的语言之一,而且语言的其他方面也不会让编译器编写变得简单)。然而,性能、广泛的平台支持和向后兼容性都非常重要。如果增加你提到的限制,这三个方面都会受到影响。 - Sander De Dycker
5
重点不在编译器上,而在硬件上。C++ 有一些未指定的内容,允许直接使用硬件功能。无论代码符合标准与否,您的手机应用程序也无法在大型计算机上运行,因此没有可移植性。 - Bo Persson
显示剩余7条评论
7个回答

134

看看这个:

Unisys ClearPath Dorado服务器

为那些还未迁移完其Univac软件的人提供向后兼容性。

要点如下:

  • 36位字长
  • CHAR_BIT == 9
  • 移码(one's complement)
  • 72位非IEEE浮点数
  • 代码和数据有独立的地址空间
  • 按字寻址
  • 没有专用的堆栈指针

不确定它们是否提供C++编译器,但他们可能会提供。


现在他们的C语言手册的最新版本链接已经出现:

Unisys C编译器编程参考手册

第4.5节有一个数据类型表格,其中包含9、18、36和72位。

size and range of data types in USC C compiler


17
我想在那种架构中使用void*一定非常困难。 - luiscubal
16
我认为 char*void* 必须是相同的大小,并且足够大,可以容纳任何其他指针。其余部分取决于实现。 - Bo Persson
28
在旧的Win16编译器中,常规指针是近指针,只包含一个16位偏移量,因此sizeof(int*) == 2,但是远指针还有一个16位选择器,所以sizeof(void*) == 4 - Adam Rosenfield
12
曾经有一份他们的C++编译器在线手册。值得指出的是,这只是Unisys大型机体系结构中的一个:另一个是48位带符号幅值标记体系结构(我只找到了C手册,没有找到C++手册)。至于其余部分:我认为这里sizeof(int*) != sizeof(char*)不正确:两者都是36位。但是,在char*中的字节选择器位于高阶位,并在int*中被忽略。(但我也用过其他机器,其中sizeof(char*) > sizeof(int*)。) - James Kanze
17
在MS/DOS 16位编译器上,存在不同的“模式”,数据指针和函数指针的大小可能是不同的。但是在我使用的编译器中,所有数据指针(包括void*)的大小总是相同的。(当然,你不能将函数指针转换为void*,因为void*可能更小。但根据标准,今天也不能这样做。) - James Kanze
显示剩余11条评论

58

对于大型机而言,你提出的假设都是不成立的。首先,据我所知,没有一台大型机使用IEEE 754:IBM使用基于16进制的浮点数,而Unisys的两台大型机则使用基于8进制的浮点数。Unisys的机器在许多方面都有些特殊:Bo已经提到了2200架构,但MPS架构更加奇怪:48位带标记的字。 (字中是否包含指针取决于字中的某个位。)而且数字表示被设计为浮点算术和整数算术之间不存在真正的区别:浮点数是基于8进制的;它不需要规范化,与我所见过的每种其他浮点数不同,它将小数放在尾数的右侧,而不是左侧,并使用带符号的幂(除尾数外)。结果是,整数浮点值具有(或可以具有)与带符号整数完全相同的比特表示。而且没有浮点算术指令:如果两个值的幂均为0,则指令执行整数算术,否则执行浮点算术。(这是体系结构中标记哲学的延续。)这意味着虽然int可能占用48位,但其中的8位必须为0,否则该值将不被视为整数。


5
IBM主机(z/Architecture)支持IEE754浮点数。 - Nikita Nemkin
1
请参考此推特评论:[https://twitter.com/stephentyrone/status/798216634645250049] - Shafik Yaghmour
6
现在他们做到了。最初,它是一个(昂贵的)附加组件来支持Java。 - Bo Persson

45

我找到了一个链接列出一些CHAR_BIT != 8的系统。它们包括:

一些TI DSP具有CHAR_BIT == 16

来自剑桥硅谷的蓝牙芯片BlueCore-5有CHAR_BIT == 16

当然,Stack Overflow上也有一个问题:什么平台具有不同于8位字符的东西

至于非二补码系统,comp.lang.c ++. Moderated上有一篇有趣的文章。总结:有一些平台具有单补码或符号和大小表示法。


7
模拟器件公司的32位SHARC DSP使用CHAR_BIT=32,而德州仪器的TMS32F28xx系列DSP使用CHAR_BIT=16。PDP-10上的GCC 3.2使用CHAR_BIT=9。我认为,IBM的S/360也可能存在非8位字符。 - osgx
2
我仍然希望有一个“非二补码”架构的示例,特别是因为CHAR_BITS是部分重复的。 - Yakov Galka
TI DSPs只有16位字符,是因为实现者选择了这样做(要让它正常工作需要多做一些工作,但不是非常难——可能只是基础编译器中的代码生成脚手架中有一些“空洞”)。所以这并不是什么深层次的架构原因。C语言代码运行在抽象机器上。如果你只有16位的INT,那么每个里面存储两个字符,并在展开优化器中添加读取-修改-写入合并(至少如此)。当然,这需要更多的工作,但只需看看在那些从未出现过的奇怪类型的地方处理它们需要多少工作。恶心。 - Kuba hasn't forgotten Monica

44

浮点数实现中很少有完全符合IEEE 754标准的,而放宽规范在这方面允许了很多优化。

例如,子规范支持在x87和SSE之间有所不同。

像将原始代码中分离的乘法和加法融合起来这样的优化会略微改变结果,但对某些架构来说是一项好的优化。

或者在x86上,严格的IEEE符合性可能需要设置某些标志或在浮点寄存器和正常内存之间进行额外的传输,以强制使用指定的浮点类型而不是其内部的80位浮点数。

而且,一些平台根本没有硬件浮点数,因此需要在软件中模拟它们。而IEEE 754的某些要求可能在软件中实现起来代价高昂,特别是舍入规则可能存在问题。

我的结论是,您不需要奇特的架构就能遇到不想始终保证严格IEEE符合性的情况。因此,很少有编程语言保证严格的IEEE符合性。


7
另一种“奇特”的硬件是IBM大型计算机,其中浮点格式早于IEEE标准。与Java不同,C++仍然可以使用现有的硬件。 - Bo Persson
7
GPU并不完全支持IEEE 754标准。 - kerem
3
IEEE 754严格的遵从度对一些人来说可能会感到困扰,但我认为这并不是OP真正关心的问题范围。 - Omnifarious
3
@Matthieu, 由于这也标记了“C”,我应该提及一款C分析器,它可以告诉您所有80位浮点寄存器溢出到内存的值,而这是由C编译器决定的。请参考以下链接: http://blog.frama-c.com/index.php?post/2011/03/03/cosine-for-real - Pascal Cuoq
2
@MatthieuM.:很遗憾ISO/ANSI没有允许可变参数指定浮点和整数参数的最小/最大大小;如果他们这样做了,80位的long double本可以成为一个有用且长寿的类型,因为它唯一的真正问题是与printf的兼容性不佳。扩展双精度存储显式地存储前导1,在非FPU系统上加速计算,并且还可以消除任何其他类型转换上下文中除去denormal外的特殊处理需求。太糟糕了,C的printf把一切都搞砸了。 - supercat
显示剩余3条评论

28

我相当确定 VAX 系统仍在使用。它们不支持 IEEE 浮点数;它们使用自己的格式。Alpha 支持 VAX 和 IEEE 浮点数格式。

Cray 向量机,如 T90,也有自己的浮点数格式,尽管新一代 Cray 系统采用 IEEE 格式。(我使用过的 T90 已于几年前退役,我不知道是否仍在积极使用。)

T90 还有/有一些有趣的指针和整数表示方法。本地地址只能指向一个64位的字。C 和 C++ 编译器具有 CHAR_BIT==8 (因为它运行 Unicos,Unix 的一种口味,并且必须与其他系统互操作),但本地地址只能指向一个64位的字。所有字节级操作都是由编译器合成的,而 void* 或 char* 在字的高3位中存储了一个字节偏移量。我认为一些整数类型还有填充位。

IBM 主机是另一个例子。

另一方面,这些特定系统不必必然排除对语言标准的更改。Cray 没有表现出升级其 C 编译器到 C99 的特别兴趣;可能适用于 C++ 编译器。可以合理地加强托管实现的要求,例如要求 CHAR_BIT==8、IEEE 格式浮点数,如果不是全部语义,则需要有 2 的补码表示法,而带符号整数不能有填充位。旧系统可以继续支持早期的语言标准(C99 发布后,C90 并没有死亡),并且对于自由实现(嵌入式系统)例如 DSPs,要求可以更宽松。

另一方面,未来系统可能有好的理由来做今天认为奇怪的事情。


7
最后提到了一个好观点,即过于严格的标准会阻碍创新。当我们拥有三进制状态的量子(或有机)计算机时,unsigned整型的模算术要求将成为一个主要问题,而带符号的算术将是完全可行的。 - Ben Voigt
@BenVoigt 为什么无符号算术会很麻烦?这些计算机中的3^n模加法器不可能实现吗? - phuclv
2
@LưuVĩnhPhúc:这正是关键所在,使用模3 ** n执行硬件操作,提供C ++无符号类型,其操作定义为模2 ** n将会很困难。 - Ben Voigt
3
我知道还有一台VAX 11/780正在被用作交叉编译器的主机,以针对具有专有架构的特殊嵌入式系统。为了维持这台特定的VAX,管理员们一直在向博物馆寻求零部件。 - Peter
2
@Keith - 从技术上讲,唯一的障碍是通过提供证据的过程来满足监管要求,因为目标嵌入式系统具有高度重要性。然而,存在大量非技术障碍(组织政治等),迄今为止这些障碍是不可逾越的。目前更容易提出攻击博物馆的理由,而不是更新主机。 - Peter
显示剩余2条评论

20

字符位数(CHAR_BITS)

根据gcc源代码:

1750a, dsp16xx 架构中的 CHAR_BIT16 位。
dsp56k 架构中的 CHAR_BIT24 位。
c4x 架构中的 CHAR_BIT32 位。

您可以通过执行以下操作轻松查找更多信息:

find $GCC_SOURCE_TREE -type f | xargs grep "#define CHAR_TYPE_SIZE"
或者
find $GCC_SOURCE_TREE -type f | xargs grep "#define BITS_PER_UNIT"
如果CHAR_TYPE_SIZE定义得当。

IEEE 754 兼容性

如果目标架构不支持浮点指令,默认情况下gcc可能会生成不符合标准的软件回退。此外,还可以使用特殊选项(如-funsafe-math-optimizations,也会禁用零的符号保留)。


3
因为直接引导OP查看一个流行编译器的来源,所以我点赞了。在这种情况下,这就是RFTM的定义,因此人们应该首先查看这个地方。 - underscore_d

12
IEEE 754二进制表示法直到最近在GPU上并不常见,详见GPU浮点数偏执
编辑:评论中提出了一个问题,即GPU浮点数是否与通常的计算机编程无关。当然有关!今天大多数工业计算中的高性能计算都是在GPU上完成的;这个列表包括人工智能、数据挖掘、神经网络、物理模拟、天气预报等等。评论中的一个链接展示了为什么:GPU具有数量级的浮点数优势。
另外,我想要补充的是,更与OP问题相关的是:在GPU浮点数还不符合IEEE标准、也没有像今天的OpenCL或CUDA这样的API来编程GPU的10-15年前,人们都是怎么做的呢?信不信由你,早期GPU计算先驱们设法在没有API的情况下编程GPU!我在公司中遇到了其中一位。他所做的是:将他需要处理的数据编码为图像,使用像OpenGL这样的工具执行所需的操作(例如,“高斯模糊”表示使用正态分布进行卷积等),然后将结果图像解码成一组结果数组。而这还比使用CPU更快!类似这样的事情促使NVidia最终使其内部数据与IEEE二进制兼容,并引入了一个面向计算而非图像处理的API。

1
@ybungalobill,将重复的工作转移到GPU上是目前处理大规模计算的首选方法。事实上,我正在使用C++开发一个这样的项目。幸运的是,我们只使用具有IEEE 754兼容二进制浮点表示的NVidia CUDA GPU。 - Michael
6
有几个回答。首先,CUDA支持C、C++和Fortran语言,请参见同一个链接以获取2048线程GPU比典型8线程CPU具有的巨大性能优势。其次,只支持这些语言的子集(虽然很大),包括缺少对CUDA编程模型适当的支持,例如递归(称为“动态并行性”),直到CUDA 5.0版本。第三,递归通常可以被循环替换,这在多线程性能方面是必要的。 - Michael

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接