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 貨幣值目前都是無單位的。