浮点数运算的结果是否总是规格化的?

3

我对以下内容感到好奇:

  1. C语言是否始终以标准形式存储浮点数?(也就是说,规范化是否总是应用?)
  2. 这个问题在一些算术运算(加法、乘法)的结果中也适用吗?
  3. 这取决于语言还是硬件-FPU?

如果您能引用任何来源,将非常有帮助。我已查看了IEEE-754文档,但未能找到关于实现方面的具体说明。

编辑:假设符合IEEE-754规范。


2
非常小的数字被非规格化。否则,有一个隐式1位是有效数字的MSB,因此数字必须被规范化。 - user3386109
据我所知,这是IEEE规范中的内容。 - Dúthomhas
如果一个数用前导1位表示,即1.001(2) x 22,则称其为“规格化(normalized)”结果。(同样地,当数字0.000000001101(2) x 23被规格化时,它会出现为1.101(2) x 2-6)。省略左侧的隐含1会给我们浮点数的尾数。规格化数比相应的非规格化数提供更高的精度。隐含的最高有效位可用于表示更准确的有效数字(23 + 1 = 24位),这称为次规范(subnormal)表示。浮点数应以规格化形式表示。 - shrey deshwal
@shreydeshwal:“省略左端的隐含1会给我们浮点数的尾数”:首选术语是“有效数字”。尾数是对数的分数部分。有效数字是线性的;尾数是对数的。有效数字包括前导数字。省略1会给出用于IEEE-754二进制格式中主要有效数字字段编码的位。没有前导位的这种编码不是实际的有效数字。此外,通常为了涵盖十进制格式和二进制格式,规范化意味着前导数字不是0,而不仅仅是1。 - Eric Postpischil
@shreydeshwal:“隐含的最高有效位可以用于表示更精确的有效数字(23 + 1 = 24位),这被称为次正规表示”:这不是次正规的意思。次正规意味着该值低于正常范围;它低于可以用最小指数和最小正常有效数字表示的数字。关于“浮点数应以规范化形式表示”:次正规数不能以规范化形式表示。 - Eric Postpischil
2个回答

7
C语言是否总是以规范化形式存储浮点数?
“这取决于情况。”正如我们所看到的,这更多地取决于硬件而不是C语言。
如果实现使用的不是IEEE-754,那么我们无法说太多。
如果实现确实使用IEEE-754,则除了subnormals之外,所有数字始终以规范化形式存储。
这是否也适用于执行某些算术运算(加法,乘法)后获得的结果?
是的。(下面会详细说明。)
它是依赖于语言还是硬件-FPU吗?
通常它取决于硬件。大多数情况下,假设目标处理器支持浮点运算,C程序将被编译为本机浮点指令,而没有任何语言或编译器强制执行的额外处理。(相比之下,Java具有一种由JVM部分实现的语言强制执行的浮点定义。)
C标准确实有一个可选的部分,称为“附录F”,其中指定了一堆特定的浮点行为,符合IEEE-754标准。如果C实现采用了Annex F并符合IEEE-754(通常是因为底层硬件也符合),那么对于你的前两个问题的答案甚至更容易得出。在IEEE-754二进制算术中,除了一个例外,没有表示模糊的情况。每个可以用规范化形式表示的数字都有唯一的规范化表示。每个无法用规范化形式表示但可以表示为子规范化的数字都有唯一的子规范化表示。这些约束适用于每个IEEE-754浮点数,包括其他操作的结果(自然而然)。 (正如Eric和Chux在评论中提醒我的那样,唯一的例外是零,IEEE-754有两个零,正零和负零。)
因为IEEE-754有子规范和零,所以“结果总是规范化吗?”的答案是“不”,但如果问题是“每个数字是否都有唯一的表示?”,答案基本上是“是”。(除了零。或者如果您是那些使用IEEE-754-2008十进制格式的少数人之一,这些格式要不那么独特)。另请参见如何区分1和零浮点值? 我想最后一个问题是“有多少C实现采用Annex F?”,或者换句话说,“有多少处理器符合IEEE-754标准?”对于通用计算机(大型机和个人计算机)上的CPU,据我所知,现在的答案是“所有的CPU都符合标准”。另一方面,GPU故意与IEEE-754不完全兼容(因为它们可以更加高效)。对于“嵌入式”工作的微处理器,我不太确定。(通常它们根本没有可行的浮点数。)

这个答案似乎假设IEEE-754二进制格式。对于十进制格式,表示中存在歧义并不是不正确的。(此外,在二进制中,实数零有两种表示方式,在标准的“浮点数据”级别(级别2)上有所区别。) - Eric Postpischil
每个可以用规范化形式表示的数字都有唯一的规范化表示,除了讨厌的+0.0和-0.0。它们都具有相同的值,但是有两种形式。如果我没记错,IEEI-754认为0.0是正常的,但我的isnormal(0.0)返回0。此外,IEEI-754的十进制类型对于许多相同的值具有多种形式。 - chux - Reinstate Monica
@EricPostpischil和Chux:谢谢你们的提醒。我本来想提到±0,但是我完全忘记了十进制。 - Steve Summit

2
如果你能引用任何来源,那将非常有帮助。
C 2018 5.2.4.2.2 3 定义了一个浮点数表示为 x = sbe Σ1≤=kp fkbk,其中 s 是符号 (±1),b 是基数,e 是整数指数范围从 eminemaxp 是精度(基-b 数字的数量),fk 是尾数的基-b 数字。
然后第4段说:
除了规格化的浮点数 (f1 > 0 if x ≠ 0) 之外,浮点类型可能还包含其他类型的浮点数,例如次规格化浮点数 (x ≠ 0, e = emin, f1 = 0) 和非规格化浮点数 (x ≠ 0, e > emin, f1 = 0),以及不是浮点数的值,例如无穷大和 NaN…
这就是全部内容;C 标准在规格化浮点数方面沉默无言,除了附录 F 提供了对 IEC 60559(实际上是 IEEE 754)的可选绑定。因此,这回答了以下问题:
C 语言是否总是以规格化形式存储浮点数?(即,是否总是应用规格化?)
不是。
这也适用于一些算术运算 (加法、乘法) 后得到的结果吗?
不是。
它是否取决于语言或硬件 - FPU?
这取决于每个 C 实现。C 实现可能受硬件影响,也可能采用浮点算术的软件实现。

我其实只是对IEEE-754表示法(二进制)感到好奇...不过,还是谢谢你提供的另一个详细答案。关于最后一点,我有一个问题。通常情况下是什么样子的?当我编写C程序(或例如从Cython代码进行转换)时会发生什么?机器码似乎并没有执行任何软件操作... - Harsh Kumar
此外,在IEEE-754规范https://ieeexplore.ieee.org/stamp/stamp.jsp?tp=&arnumber=8766229第3.4 c节中,他们说:“尾数的最高位d0在偏置指数E中隐式编码。” 图表显示尾数从d1...dp-1开始。 - Harsh Kumar
1
@HarshKumar:一个例子是,当C实现针对不支持浮点运算的硬件时,它可能会使用浮点运算的软件实现。(据我回忆,在GNU标准C库实现或GCC或相关软件中有一些软件可以实现这个功能。)另一个例子是,C实现旨在支持为某个与当前硬件不同的旧平台编写的C源代码,该C源代码利用了与当前硬件不同的浮点行为。 - Eric Postpischil
@HarshKumar 当 C 语言还很年轻的时候,由 Dennis Ritchie 在 PDP-11 上首次实现,如果你的程序中有 任何 浮点代码,通常需要链接一个浮点库。我记得有 四个 不同的变体:一个在软件中模拟所有浮点运算(甚至是基本的加减法);一个使用可选的硬件浮点单元;一个检查浮点单元是否存在,如果存在则使用它,否则回退到软件模拟;我忘记第四个是什么了。 - Steve Summit
@SteveSummit:这不是Unix的特性。我认为这可能是GCC或Binutils配置的一部分。在Unix操作系统macOS上,您不需要使用“-lm”。 - Eric Postpischil
显示剩余3条评论

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