浮点值的范围越大,乘/除/加/减的精度会比范围越低吗。
例如,567.56 / 345.54
会比.00097854 / .00021297
更准确吗?
问题的答案是"否"。浮点数(通常为*)用标准化尾数和指数表示。乘法和除法首先对归一化尾数进行运算,然后对指数进行运算。
当然,加法和减法是另一回事。像你的例子一样的操作:
567.56 + 345.54 or .00097854 - .00021297
工作良好。但像这样具有不同数量级的操作
567.56 + .00097854 or 345.54 - .00021297
可能会失去一些低阶精度。
- IEEE浮点标准包括非规范化数字。如果你是一名天体物理学家或运行库开发人员,你可能需要了解他们。看见http://en.wikipedia.org/wiki/Denormal_number
对于IEEE 754二进制浮点数(最常见的),浮点值在大部分指数范围内的有效位数相同。然而,有一部分范围的有效位实际上更少。四舍五入引起的相对误差会随着有效位在其范围内的位置而变化。
IEEE 754浮点数由一个符号(+1或-1,编码为0或1)、一个指数(对于双精度,-1022到1023,编码为指数加1023,因此1到2046)和一个有效位(对于双精确度,一个通常从1到略低于2的分数,用53位表示,但用52位编码,因为第一位隐含为1)表示。
例如,数字6.5由位0(符号+1)、10000000001(指数2)和101000000000000000000000000000000000(二进制分数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(包括1)和2(不包括2)之间。对于非规范化数,分数小于1;它以"0."而不是"1."开头。
当浮点运算的结果是一个非规范化的数字时,它的有效位实际上更少。因此,当数字下降到0x1p-1022(2-1022)以下时,有效精度降低。
当数字在正常范围内(不下溢到非标准值,也不溢出到无穷大),则具有不同指数的数字的有效位没有差异,因此:
- (2a+2b)/2具有与a+b完全相同的结果
- (2a-2b)/2具有与a-b完全相同的结果
- (2ab)/2具有与ab完全相同的结果
但是,请注意,相对误差可能会发生变化。执行浮点运算时,必须将精确的数学结果四舍五入到可表示的值。这种舍入只能以有效位表示的单位进行。对于给定的指数,有效位中的位具有固定值。因此,有效位中的最后一位表示某个值。该值是靠近1的有效位的较大部分,而不是靠近2的有效位。
对于双精度结果,最小精度单位(ULP)是有效位中最大位值的252的1部分。当使用四舍五入到最近模式(最常见的默认值)时,最大的误差最多是其一半,因为如果一个方向上的可表示数字距离超过半个ULP,那么另一个方向的数字距离不到半个ULP。更接近的数字由正确的浮点运算返回。
因此,有效位在1附近的结果中的最大相对误差略高于2-53,但有效位在2附近的结果的最大相对错误略低于2-54。
为了完整性,我不得不有点不同意,并说是,这可能很重要
事实上,如果您执行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
上面的例子很难概括,我完全同意其他答案:一般来说,NO,你应该只关心相对精度
…除非你想实现一些对舍入误差有严格容忍度的函数。。。
编号。从某种意义上说,无论数字的数量级(指数部分)是多少,都有相同数量的有效数字可用。