跳至内容

Rebol 编程/Rebol3 金额数据类型

来自维基教科书,开放书籍,为开放世界

关于金额数据类型 - 高精度算术用于金融和科学应用。

在 Rebol3 中,金额数据类型使用编码十进制表示法,允许精确的数字表示,长度最长可达 26 位小数。由于其精度,此数据类型对于金融、银行、商业、交易,甚至某些类型的科学应用非常有用。

从外部来看,金额的表示方式与 R2 中相同。在代码和数据中,$ 表示正在指定数据类型。

 $1.23
 -$1.23

与 R2 不同的是,金额保持高达 26 位小数的精度。

>> $123456789012345678901234.56 + $0.01
== $123456789012345678901234.57

将此与使用 Rebol 十进制! (64 位 IEEE754 二进制浮点数) 值时发生的情况进行比较。

>> 123456789012345678901234.56 + 0.01
== 1.2345678901234569E+23

原始数字中的许多位数都丢失了,并且 0.01 的加法在舍入中完全丢失了。

如以下示例所示,甚至可以更早地看到差异。

>> $0.30 - $0.20 - $0.10
== $0

使用 Rebol 十进制! 数据类型时,我们得到

>> 0.30 - 0.20 - 0.10
== -2.77555756156289E-17

造成这种情况的原因是,像 0.30、0.20 或 0.10 这样的数字可以用 money! 数据类型精确表示,而它们不能用 Rebol 十进制! (64 位 IEEE754 二进制浮点数) 数据类型精确表示。

(此属性是金额数据类型在需要精度的金融和交易应用中很有用。)

此外,在进行一些需要高达 26 位小数的整数运算时,也可以使用它,例如在此示例中(精确计算 2 ** 64,这在 Rebol 整数格式中无法完成)。

>> a: $1 loop 64 [a: a * $2]
== $18446744073709551616

可以获得高达 2 ** 86 的精确结果,2 ** 87 是第一个需要超过 26 位小数的 2 的幂。

值的范围

[编辑 | 编辑源代码]

请参阅范围表。

金额数据类型使用高精度算术(也称为 bignum 算术)的变体,使用比典型 BCD(二进制编码十进制)更有效的实现。

空间效率比较:虽然 BCD 被定义为使用 4 位来表示一位十进制数字,即 26 位十进制数字需要 104 位,但金额数据类型为此目的仅使用 87 位。

主要的是,数据类型旨在利用所有现代 CPU 上的高性能 32 位整数运算。此外,数据类型针对直接存储在 Rebol 的 128 位处理引擎中进行了优化,消除了标准数值计算中的内存分配和 GC 的开销。

金额数字的表示使用符号、无符号有效数字(也称为系数/尾数)以小端序形式表示,具有 26 位十进制数字,以及范围在 -128 到 +127 之间的无偏带符号十进制指数。

96 位二进制表示由以下部分组成:

  • 87 位用于无符号有效数字(也称为系数/尾数)
  • 1 位符号
  • 8 位用于无偏带符号十进制指数

使用每位十进制数字四位的 BCD 表示法以其非常快地转换为字符串表示和十进制移位而闻名。其他操作非常慢。与之相反,为金额数据类型选择的表示法应该对通常的算术运算更快。

金额数据类型支持这些操作。

  • 添加
  • 减法
  • 乘法
  • 除法
  • 余数
  • 否定
  • 绝对值
  • 舍入
  • 相同?
  • 相等?
  • 严格相等?
  • 不等?
  • 严格不等?
  • 大于?
  • 小于?
  • 大于或等于?
  • 小于或等于?
  • 负数?
  • 正数?
  • 零?
  • 最小值
  • 最大值

此外,还支持所有相应的 infix 运算符。

支持这些转换。

  • 字符串到金额
  • 金额到字符串
  • 整数到金额
  • 金额到整数
  • 十进制到金额
  • 金额到十进制
  • 二进制到十进制
  • 十进制到二进制

当金额类型与其他数字数据类型混合使用在函数中时,也会发生自动转换。金额被认为是“更强的类型” - 产生结果的数据类型。

>> $1 + 1
== $2
>> $1 + 1.0
== $2
>> 1 + $1
== $2
>> 1.0 + $1
== $2
>> 1.2 > $1
== true

字符串到金额

[编辑 | 编辑源代码]

如上所述,money! 数据类型是一种高精度数据类型。从字符串到金额的转换是精确的,保持建议的精度,如上所示。只有当为金额值提供超过 26 位小数时,才会发生精度损失。

金额到字符串

[编辑 | 编辑源代码]

从金额到字符串的转换是精确的。

整数到金额

[编辑 | 编辑源代码]

从整数到金额的转换是精确的,money! 数据类型可以精确表示每个 64 位整数。

金额到整数

[编辑 | 编辑源代码]

从货币到整数的转换对于适合 64 位整数范围的整数值是精确的。例如

>> to integer! $100
== 100

如果值不适合整数范围,则会发生溢出。例如

>> to integer! $9'999'999'999'999'999'999
** Math error: math or number overflow
** Where: to
** Near: to integer! $9999999999999999999

如果货币值不是整数,则会被截断。例如

>> to integer! $1.50
== 1

小数到货币

[编辑 | 编辑源代码]

从小数到货币的转换通常是精确的,因为货币数据类型比小数数据类型更精确。为了确保小数值被精确地表示(即它被唯一确定),转换使用 17 位小数。例如

>> a: 0.1
== 0.10000000000000001
>> b: to money! a
== $0.10000000000000001
>> same? a to decimal! b
== true

但是,可能会发生溢出,因为 decimal! 数据类型的范围大于 money! 数据类型的范围。当转换微小的 decimal! 值时,如果小数指数低于 -128,也可能会发生精度损失(下溢),导致 $0。

货币到小数

[编辑 | 编辑源代码]

既不会发生溢出,也不会发生下溢,但小数值不能精确地表示某些货币值。例如

>> a: $0.01
== $0.01
>> b: to decimal! a
== 0.10000000000000001
>> to money! b
== $0.10000000000000001
>> same? a to money! b
== false
  • 货币值旨在保持精度(它们没有被归一化)。例如
>> a: $10
== $10
>> b: $10.00
== $10.00

如果需要不同的精度,我们可以使用 **round** 操作。例如

>> round/to $1.000 $0.01
== $1.00

在上面的例子中,我们将数字舍入到较低的精度(美分)。如果需要,我们可以使用 ROUND 操作来获得更高的精度。

>> round/to $1.1 $0.01
== $1.10

在上面的例子中,我们将数字舍入到更高的精度(美分)。

不建议将 **scale** 参数作为小数值提供,因为我们经常无法获得准确的结果。例如

>> round/to $1.15 0.1
== $1.10000000000000011

,这很可能不是我们想要的。如果我们使用作为货币值提供的准确 **scale**,我们将获得

>> round/to $1.15 $0.10 == $1.20

还要注意,使用 $0.10 作为比例,而不是 $0.1!是有意义的。

加法和乘法

[编辑 | 编辑源代码]

乘法和加法使用其操作数的精度。例如

>> $10 + $10.50
== $20.50
>> $2 * $1.10
== $2.20

如果需要不同的精度,我们可以像往常一样使用 **round** 操作。

与舍入比例类似,不建议使用小数值作为系数。在可能的情况下,最好使用货币值作为系数,因为它具有更高的准确性优势。

与上面不同,除法总是生成所有 26 位数字以提供可用的全部精度。想法是,当除以货币值时,我们通常必须根据某些标准(这可能取决于情况)来舍入结果。结果包含这么多的数字,因此根据目前使用的任何标准来舍入它没有问题,并且 **round** 操作有很多细化可供用户选择。

>> $2 / $3
== $0.66666666666666666666666667
>> $3 / 1.5
== $2.0000000000000000000000000

与 Rebol2 不同,在 Rebol3 中,货币单位没有实现,这意味着所有 Rebol3 货币值目前都是无单位的。

华夏公益教科书