我现在所知,十进制比双精度大 2 倍(128 位对 64 位)。因此,它可以以更高的精度表示数字。但它也使用以 10 为基数的数字系统,而不是二进制。也许十进制的最后一个特征会影响以下结果?
Microsoft (R) F# Interactive version 11.0.60610.1
Copyright (c) Microsoft Corporation. All Rights Reserved.
For help type #help;;
> let x = 1.0m / 3.0m ;;
val x : decimal = 0.3333333333333333333333333333M
> x * 3.0m ;;
val it : decimal = 0.9999999999999999999999999999M
> let y = 1.0 / 3.0 ;;
val y : float = 0.3333333333
> y * 3.0 ;;
val it : float = 1.0
> it = 1.0 ;;
val it : bool = true
如您所见,除法和乘以 3.0 后再次双倍打印为 1.0。我尝试了不同的除数,情况是一样的。
(对于不了解 F# 的人请注意 - float
基本上是 F# 中double
的同义词)
当用我们通常的十进制表示法显示时,类型decimal
的好处是它是所见即所得的:打印足够的十进制数字(0.3333333333333333333333333333M
看起来肯定足够了),你可以看到机器正在使用的确切数字。毫不奇怪,三次这会产生0.9999999999999999999999999999M
:你可以用笔和纸来做到这一点并重现结果(2)。
在二进制中,需要更多的十进制数字才能看到所表示的确切数字,并且它们通常不会全部打印出来(但情况会像它们一样简单)。在这种情况下,3.0
乘以1.0 / 3.0
的二进制乘以1.0
,这只是巧合。该属性保留某些数字,但不必保留所有数字。事实上,结果可能不是 1.0,并且您的语言打印的十进制数字可能少于显示这一点。指数形式 1.DD...点后有 16 位数字的 DDEXXX 足以区分所有双精度数字,尽管它不显示数字的确切值。
所以,总结一下:
- 十进制是所见即所得,你得到 0.99...因为你乘以 0.33...由 3 二进制
- 的结果可能不是 1.0,而只能使用您的语言中二进制数的默认有限小数位数进行打印
- 即使它是 1.0,这也是一个巧合,另一个数字而不是 3.0 可能不会发生。
杂项说明
- 如果 F# 在这方面类似于 OCaml,则可以打印足够的小数来区分 1.0 和另一个带有
Printf.printf "%.16e"
float
。 - F# 的
decimal
类型是所见即所得,但您必须记住,有些数字的精度为 28 位,而大多数数字的精度为 29 位。有关详细信息,请参阅超级猫的答案或下面的评论。 - 十六进制表示法对二进制浮点具有相同的 WYSIWYG 属性,与十进制表示法对
decimal
具有相同的 WYSIWYG 属性。在所有语言和年份中,C99 对精细浮点操作具有最好的支持,并且它支持十六进制的输入和输出。
举个例子:
#include <stdio.h>
int main(){
double d = 1 / 3.0;
printf("%an%an", d, 3*d);
}
执行产品:
$ gcc -std=c99 t.c && ./a.out
0x1.5555555555555p-2
0x1p+0
用笔和纸,我们可以将0x1.5555555555555p-2
乘以3
.我们得到0x3.FFFFFFFFFFFFFp-2
,或归一化后的0x1.FFFFFFFFFFFFF8p-1
。这个数字不能完全表示为二进制 64 浮点数(它的有效数字太多),并且乘法返回的"最接近"可表示的数字是 1.0
。(应用领带必须舍入到最接近的偶数的规则。在两个同样接近的备选方案0x1.FFFFFFFFFFFFFp-1
和1.0
中,1.0
的结果是"偶数"。
您在double
中观察到的行为归因于以下事实:将 1/3 乘以 3 的结果与 1/3 的比例不同。 这种情况类似于,如果总是保持三个十进制数字,一个人计算 1.00/7.00(产生 .143)并将该结果乘以 7(其确切值将是 1.001,但四舍五入为 1.00)。 从本质上讲,除法会得到一个有效数字(精确到 0.001,即使原始数字只精确到 0.01),这使得乘法产生正确的结果。
对于类型 Decimal
,即使不能精确存储x/y
,如果乘法导致需要舍入步长的小数位数变化,则 (x/y)*y
的值(其中 1 < y < 10
)通常等于 x
。 (1D/3D)*3D 不产生 1D 的原因是,虽然高于大约 7.923 的值对于超过该数量的每 10 次方,Decimal
值在右边失去一个小数位,但低于 0.7922 的值不会获得位置。 因此,除以 3 7.923 到 23.76 范围内的值,然后乘以 3 将产生原始值;同样,如果使用值 79.23 到 233.76 等。 将低于 7.923 的值除以任何大于 1 的值通常不是可逆运算,除非结果是 10^-28 的精确倍数。