1. 引言
通常,有两种对编程语言的改进。第一种是让困难的事情变得简单,第二种是让不可能的事情变为可能。本文介绍的是任意精度算术,它属于第二类:使在ABAP中原本不可能的事情成为可能。
过去已经可以在ABAP中使用INT8或DECFLOAT34数据类型进行非常大的数字计算,但还不能进行任意精度的计算。使用这两种数据类型,也有溢出的风险。
尽管DECFLOAT34可以对非常大的数字进行操作,但其精度仅为34位。这意味着当你进行更复杂的计算时,可能会得到错误的结果。典型的问题是当加减不同大小类别的数字时,例如:
larger_number + smaller_number - larger_number = 0
原因是大数没有足够的精度来反映小数的加法。如果你再减去大数,会得到零。小并不意味着数字真的很小,只是它比较大的数字小得多。所以在这种情况下,10000可能是小的。
许多编程语言提供了任意精度算术的能力。例如,Java有BigInteger和BigDecimal类,Python的整数数据类型默认为任意精度,而C#也提供了一个BigInteger数据类型。C++语言有著名的GMP和MPFR库用于任意精度算术。
英文原文:New classes for arbitrary precision arithmetic in ABAP
本文链接:https://www.cnblogs.com/hhelibeb/p/17868032.html
2. 不同种类的任意精度算术
通常,编程语言中有三种任意精度算术库。第一种是任意精度整数算术。这允许用任意大的整数进行计算。可以假想这种类型的数字为INT∞。当然,数字并非真的可以任意长,但受限于系统的主内存的容量。然而,对于每个实际应用,你可以假想结果的数字为无穷大。
这种库的典型质量标准是乘法指令的速度和时间复杂度。经典的教科书乘法复杂度为log(N)²,因为你需要将第一个数的每一位与第二个数的每一位相乘。令人惊奇的是,有更有效的乘法算法,最简单的就是众所周知的Karatsuba算法。
一般来说,任意精度整数算术的库也提供了一些数论操作的可能性,例如,确定质数和计算GCD(最大公约数)。
下一种类型的任意精度数字是任意精度有理数。任意精度有理数是两个任意精度整数的商a / b。注意,每个具有有限小数的十进制数都是一个有理数,例如,1.01 = 101 / 100。因此,每个固定大小的ABAP类型P/DEC的十进制数都可以写成一个有理数。由于精度仍然是有限的,同样的,每个DECFLOAT34类型的数字也可以写成一个有理数。
有理数在这个意义上是复杂的,即分数必须被简化,例如,2 / 4 = 1 / 2。因此,实现有理数要求有一个好的GCD算法。
有理数算术库不提供像EXP或LOG这样的超越函数,甚至其他简单的操作,如平方根。这是因为有理数的指数和对数只在非常特定的情况下也是有理数。平方根也是如此。
对于平方根,有一个简单的技巧可以计算任意数量的数字,只需使用整数算术。然而,对于超越函数,没有类似的技巧。
为了计算超越函数,你需要一个任意精度浮点(或实数)算术库。这些库可以计算具有任意给定精度的实数。例如,你可以决定用100,1000,或10000位来计算2的对数。
3. 在ABAP中的任意精度算术
在过去,ABAP没有任何形式的任意精度算术的能力。随着2308版本和793内核的发布,ABAP引入了两个新的类:CL_ABAP_BIGINT
和CL_ABAP_RATIONAL
,用于实现任意精度的整数和有理数算术。另外,虽然CL_ABAP_RATIONAL
在内部实现中使用了任意精度的实数算术来进行其转换到DECFLOAT34,但ABAP尚未公开任意精度的实数算术功能。
这两个类都是侵入式的,这意味着如果你向一个数添加另一个数,你不会得到一个新的实例,而是原来的实例会被改变。考虑以下编码:
result_bigint = bigint->add( other_bigint )
这会改变bigint实例并返回一个自我引用以允许链接。因此,result_bigint和bigint实际上是同一个实例。这样做是为了确保高性能。当然,也有一个clone()方法,可以生成bigint的副本,允许非侵入性操作。例如,你可以写成:
result_bigint = bigint->clone()->add( other_bigint )
这两个类都已经发布到ABAP Cloud,因此可以在Steampunk、Embedded Steampunk和SAP S/4HANA Cloud中使用。(译者注:虽然我没验证,但可以想象OP版本的7.58也会有)
CL_ABAP_BIGINT
类具有以下特性:
- 基本的算术操作,如加法、减法、乘法和带余数的除法
- 快速克隆操作
- 快速乘法
- 高级操作,如最大公约数和整数平方根
- 数论操作,如质数确定、MOD、POWMOD和MOD_INVERSE
- 序列化到/从XML/JSON
- 带千位分隔符的外部表示转换
- 启用共享内存(仅限非ABAP Cloud)
- 转换到/从STRING和到DECFLOAT34
- 适用于ABAP Cloud
CL_ABAP_RATIONAL
类几乎具有与CL_ABAP_BIGINT
类相同的特性,但是它并未包含那些对有理数来说没有意义的数论函数。
4. 体验如何?
通常,数字计算是使用DECFLOAT34或INT8等内置数据类型进行的。对于相同的任务使用类似乎看似有些不寻常,但实际上非常可读。一个好例子是计算整数平方根的算法,也就是floor(sqrt(n)),如下所示:
class compute_sqrt definition.
public section.
methods compute_sqrt importing number type ref to cl_abap_bigint
returning value(sqrt) type ref to cl_abap_bigint.
endclass.
class compute_sqrt implementation.
method compute_sqrt.
data y type ref to cl_abap_bigint.
final(bits) = number->get_number_of_bits( ).
data(x) = cl_abap_bigint=>factory_from_int4( 1 )->mul_by_two_power( ( bits + 2 ) / 2 ).
do.
y = x->clone( )->add( number->clone( )->div( x )-quotient )->div_by_two_power( 1 ).
if y->is_larger_or_equal( x ).
exit.
endif.
x = y.
enddo.
return x.
endmethod.
endclass.
start-of-selection.
data(result) = new compute_sqrt( )->compute_sqrt(
cl_abap_bigint=>factory_from_string( `129341967194712394612956129461294861619246` ) ).
cl_demo_output=>display( result->to_string( ) ).
5. 可用的演示程序
任意精度整数库的一个简单演示是RSA算法的实现。RSA是一种被广泛使用的公钥/私钥加密。RSA的每个实现都使用大于一百或一千位精度的大数。
有一个演示类CL_DEMO_BIGINT_RSA
,以及一个演示报表程序DEMO_BIGINT_RSA
,它根据给定的位大小生成公钥/私钥,并使用它加密一条小消息。以前是不可能在纯ABAP中生成RSA公钥/私钥的。以下是纯ABAP实现效果:
还有另一个在CL_DEMO_BIGINT_SQRT
和DEMO_BIGINT_SQRT
中的演示,用于计算自然数的平方根的任意多位数。
6. 何时使用任意精度算术?
可以在以下情况中使用任意精度算术:
- 真的需要大于34位的大数,因为你的算法需要它。典型的例子就是RSA算法。
- 有非常大或非常小的数字的计算,不想有任何溢出或下溢。或者,有未知大小的数字的计算,不想关心溢出或下溢。
- 有复杂的计算,不想关心舍入误差。别忘了:只要坚持使用基本的算术运算,对于任意精度算术,永远不会有任何舍入误差。
- 使用一些数论算法,如质数检查或模反。
7. ABAP关键字文档和发布说明
可以在以下位置找到更多关于此主题的信息:
- CL_ABAP_BIGINT ABAP关键字文档(sap.com)
- CL_ABAP_RATIONAL ABAP关键字文档(sap.com)
- Release News 7.93 ABAP关键字文档(sap.com)