与较低范围相比,较高范围的浮点数乘/除/加/减会更准确吗?
例如,会567.56 / 345.54
比.00097854 / .00021297
?
你的问题的答案是“不”。浮点数(通常*)用归一化尾数和指数表示。乘法和除法首先对归一化尾数进行运算,然后对指数进行运算。
当然,加法和减法是另一回事。像你的例子这样的操作:
567.56 + 345.54 or .00097854 - .00021297
工作正常。但是具有不同数量级的操作,例如
567.56 + .00097854 or 345.54 - .00021297
可能会损失一些低阶精度。
对于 IEEE 754 二进制浮点数(最常见),浮点值在大部分指数范围内的有效位中具有相同的位数。然而,有一部分范围的有效数字实际上具有更少的位。舍入引起的相对误差确实会根据有效数字在其范围内的位置而有所不同。
IEEE 754 浮点数由符号(+1 或 -1,编码为 0 或 1)、指数(对于双精度,-1022 到 1023,编码为指数加 1023,因此为 1 到 2046)表示, 和一个有效数(对于双精度,小数通常从 1 到略低于 2,用 53 位表示,但用 52 位编码,因为第一位隐式为 1)。
例如,数字 6.5 用位 0(符号 +1)、10000000001(指数 2)和 10100000000000000000000000000000000000000000000000000(二进制小数 1.1010,十六进制 1.a,十进制 1.3125)进行编码。我们可以用十六进制浮点数将其写为 0x1.ap2(十六进制小数 1.a 乘以 2 的十进制 2 次方)。以十六进制浮点形式编写使人们能够相当容易地看到浮点表示。
对于指数,0 和 2047 的编码值是特殊的。编码为 0 时,指数与编码为 1 时相同(-1022),但分数的隐含位为 0 而不是 1。编码为 2047 时,浮点对象表示无穷大(如果有效位全为零)或 NaN(否则)。
当编码指数为 0 且有效位全为零时,该数字表示零(+0 和 -0 以符号区分)。如果有效位不全为零,则称该数字是非规范化的。这是因为大多数数字都是通过调整指数来“归一化”的,因此分数在 1(包括)和 2(不包括)之间。对于非规格化数,分数小于 1;它以“0”开头。而不是“1.”。
当浮点运算的结果是非规格化数字时,有效数字中的有效位数更少。因此,随着数字下降到 0x1p-1022 (2 -1022 ) 以下,有效精度会降低。
当数字在正常范围内(不下溢到非正规并且不上溢到无穷大)时,具有不同指数的数字的有效数字没有差异,因此:
但是请注意,相对误差可能会发生变化。执行浮点运算时,必须将精确的数学结果四舍五入为可表示的值。这种舍入只能以有效数字表示的单位发生。对于给定的指数,有效数字中的位具有固定值。所以有效数字的最后一位代表一个特定的值。该值在 1 附近的有效数字的部分比在 2 附近的有效数字的更大。
对于双精度结果,最小精度单位 (ULP) 是有效数字中最高位值的2 52分之一。当使用四舍五入模式(最常见的默认值)时,最大误差最多是该值的一半,因为如果一个方向上的可表示数字超过 ULP 的一半,那么另一个方向上的数字会更小距离 ULP 不到一半。更接近的数字由适当的浮点运算返回。
因此,有效数字接近 1 的结果中的最大相对误差略高于 2 -53,但有效数字接近 2 的结果中的最大相对误差略低于 2 -54。
为了完整起见,我不得不有点不同意并说Yes,它可能以某种方式很重要...
确实,如果您执行 56756.0 / 34554.0,那么您将获得最接近精确数学结果的可表示浮点数,只需一个浮点舍入“错误”。
这是因为 56756.0 和 34554.0 可以精确地表示为浮点数(单精度或双精度 IEEE 754),并且因为根据 IEEE 754 标准,操作执行精确的舍入操作(在默认模式下到最接近的值)。
如果您写 567.56 / 345.54,那么这两个数字都不会以基数 2 的浮点数精确表示,因此此操作的结果是累积 3 个浮点舍入“错误”。
让我们比较 Squeak Smalltalk 中双精度(浮点数)的结果,转换为精确算术(分子和分母处具有任意整数长度的分数):
((56756.0 / 34554.0) asFraction - (56756 / 34554)) asFloat.
-> -7.932275867322412e-17
到目前为止,一切都很好,误差的幅度小于或等于半个 ulp,正如 IEEE 754 所承诺的那样:
(56756 / 34554) asFloat ulp / 2
-> 1.1102230246251565e-16
对于累积的舍入误差,您可能会得到更大的误差(但绝不会更小):
((567.56 / 345.54) asFraction - (56756 / 34554)) asFloat
-> -3.0136736359825544e-16
((0.00056756 / 0.00034554) asFraction - (56756 / 34554)) asFloat
-> 3.647664511768385e-16
上面的例子很难概括,我完全同意其他答案:一般来说,不,你应该只关心相对精度。
...除非您想实现一些对舍入误差具有非常严格容忍度的功能...
不。从某种意义上说,无论您的数字的数量级(指数部分)是多少,都有相同数量的有效数字可用。