Java 中的货币精度(不是 BigDecimal)

Posted

技术标签:

【中文标题】Java 中的货币精度(不是 BigDecimal)【英文标题】:Monetary precision in Java (not BigDecimal) 【发布时间】:2015-02-03 16:57:06 【问题描述】:

我们知道使用 float 或 double 不是需要正确精度的选项,并且我们知道 BigDecimal 可以达到此目的,但我们也知道它比常规原始操作慢大约 100 倍。

如果速度对我们来说至关重要,而我们确实需要精确度,我们该怎么办?

我尝试以最慢的单位存储货币的价值并将其转换存储为 1 BTC = 100000000 satoshi,但经过几次实验,很明显你根本无法长期存储 100BTC,超过了可能的最大值。是的,可以选择牺牲精度,例如存储 microBTC 等,但问题更加全球化,我们如何使用原语设计这样的东西?

【问题讨论】:

我还没有发现 BigDecimal 与原语相比成为瓶颈。您确定它对您的需求来说太慢了吗? 如果您需要任意精度,请使用BigDecimal(或String)。没有 Java 原始类型可以满足您的要求,并且您无法在不使用 Object 的情况下创建新类型。 一百倍的非常小的值不会成为瓶颈。原始操作的规模为一纳秒。微基准与识别瓶颈无关。 Java 的 long 类型的最大值 (2^63)-1。最终将只有 2100 万比特币在流通。 1 BTC = 10^8 satoshis,这意味着总共 2.1*10^15 satoshis。使用 2.1*10^15 学习 D.E.Knuth 的第 2 卷。您可以使用“半数值算法”非常有效地实现定点算法。我已经经历了这些,并且我已经制作了一个类似于 BigInteger 的工作,而无需为每个新的操作结果重新创建一个对象。是的,这意味着工作,但如果你真的认为你需要它......不确定你计划什么算法,但 BigDecimal 并没有那么慢。 【参考方案1】:

正如 D.E.Knuth 在他的第 2 卷“计算机编程的艺术”中充分记录的那样,以任意精度实现算术并不是“魔法” - 请参阅“半数值算法”一章。根据实现 COBOL 计算的必要性(因此:不仅是整数),我已经经历了这一点。

此外,在不为每个新的操作结果重新创建对象的情况下,制作类似于 BigInteger 或 BigDecimal 的工作是一种选择。是的,这意味着工作,但如果你真的认为你需要它......

我发现 BigInteger 就算术而言并没有那么慢。对我来说真正扼杀的是必须经常在二进制和十进制(十进制数字数组)之间进行转换。

【讨论】:

Seminumerical Algorithms 是整卷的标题,而不是一章。您所指的章节标题为算术。【参考方案2】:

您的选择是:

BigDecimal - 准确且有效 - 比原语慢,但可能无法衡量 - 大部分是不可变的(即可以改变准确性但不能改变价值)。

long - 准确但有价值限制 - 原始,因此在速度方面无法超越 - 不可变 - 数学更容易编写,更清晰。

BigInteger - 可能是您在上述两者之间最好的折衷方案 - 不可变,因此每当您更改值时都必须创建新的 - 您不太可能达到它的极限。

【讨论】:

longBigInteger 一样可变,真的。 我不必担心可变性,因为这些值不会在多线程模式下运行...... @Vach - 你会惊讶于 JVM 在重用你放手的对象方面的效率。别担心了。如果存在可衡量的速度问题,请稍后处理。看看here。 @vach 关于 HotSpot 的内存管理,您还需要了解更多信息——例如 Escape Analysis 和根本不在堆上分配的前景,以及常规 Minor GC 和 Major GC 之间的巨大差异。 那是我们的 Peter Lawrey,这里的***人物之一。关于 GC,你错了它必须不运行——Major GC 必须不运行;没有人会担心未成年人。 Major GC 的频率与您创建的垃圾的数量 几乎没有关系——它与对象的生命周期 有关。唯一要避免的是一次保留大量对象,而您的用例根本没有表明这种风险。

以上是关于Java 中的货币精度(不是 BigDecimal)的主要内容,如果未能解决你的问题,请参考以下文章

BigDecimal的用法

如何处理 Java BigDecimal 性能?

java中的精度计算--BigDecimal

货币金额的计算 - Java中的BigDecimal

Java 中的 BigDecimal 运算,如何解决精度丢失问题?

Java中的货币转换器具有精度和浓缩问题