Java 双倍我的钱:我的框架使用双倍的货币金额

Java 双倍我的钱:我的框架使用双倍的货币金额,java,double,currency,Java,Double,Currency,我继承了一个项目,其中货币金额使用双重类型 更糟糕的是,它使用的框架和框架本身的类都使用double来赚钱 ORM框架还处理从数据库(和存储到数据库)检索值。在数据库中,货币值是number(19,7)类型,但框架ORM将其映射为double 除了完全绕过框架类和ORM之外,我还能做些什么来精确计算货币价值吗 编辑:是的,我知道应该使用BigDecimal。问题是,我与一个框架紧密相连,例如,类framework.commerce.pricing.ItemPriceInfo的成员是双重mRawT

我继承了一个项目,其中货币金额使用双重类型

更糟糕的是,它使用的框架和框架本身的类都使用double来赚钱

ORM框架还处理从数据库(和存储到数据库)检索值。在数据库中,货币值是number(19,7)类型,但框架ORM将其映射为double

除了完全绕过框架类和ORM之外,我还能做些什么来精确计算货币价值吗

编辑:是的,我知道应该使用BigDecimal。问题是,我与一个框架紧密相连,例如,类framework.commerce.pricing.ItemPriceInfo的成员是双重mRawTotalPrice;还有双份米饭。我公司的应用程序自己的代码扩展了,例如,这个ItemPriceInfoClass


事实上,我不能对我的公司说,“由于舍入错误,基于这个框架的代码浪费了两年的工作和几十万美元”

我没有看到你提到重构。我想这是你最好的选择。为什么不用正确的方法来修复它,而不是拼凑一些技巧来让事情更好地运转呢


这里有一些关于你的信息。这篇文章建议使用
BigDecimal
,即使速度较慢。

如果可以接受,将货币类型视为整数。换句话说,如果你在美国工作,如果美分提供了你所需要的粒度,那么跟踪美分而不是美元。double可以精确地表示整数,最大为(不存在该值的舍入误差)


但实际上,正确的做法是完全绕过框架,使用更合理的方法。对于框架来说,这是一个非常业余的错误——谁知道还有什么隐藏着呢?

事实上,你没有那么多选择:

您可以重构项目以使用BigDecimal(或更适合其需要的东西)来表示金钱

要特别小心溢出/下溢和精度损失,这意味着要添加大量检查,并以不必要的方式重构更大比例的系统。更不用说如果你要这么做,需要进行多少研究了

保持现状,希望没有人注意到(这是个玩笑)

依我看,最好的解决方案是简单地重构这个。这可能是一些繁重的重构,但邪恶已经发生了,我相信这应该是你最好的选择

最好的, 瓦西里


另外,你可以把钱当作整数(计算美分),但如果你要进行货币兑换、计算利息等,这听起来不是个好主意。

很多人会建议使用BigDecimal,如果你不知道如何在项目中使用舍入,那就是你应该做的

如果您知道如何正确使用小数舍入,请使用double。它的许多数量级更快、更清晰、更简单,因此不易出错。如果您使用美元和美分(或需要两位小数),您可以获得高达70万亿美元的精确结果

基本上,如果您使用适当的舍入来纠正它,您将不会得到舍入错误

顺便说一句:舍入错误的想法触动了许多开发人员的心,但它们不是随机的,你可以相当容易地管理它们

< >编辑:考虑一个舍入错误的简单例子。

    double a = 100000000.01;
    double b = 100000000.09;
    System.out.println(a+b); // prints 2.0000000010000002E8
有许多可能的舍入策略。您可以在打印/显示时对结果进行四舍五入。e、 g

    System.out.printf("%.2f%n", a+b); // prints 200000000.10
或者对结果进行数学四舍五入

    double c = a + b;
    double r= (double)((long)(c * 100 + 0.5))/100;
    System.out.println(r); // prints 2.000000001E8
在我的例子中,当从服务器发送时(写入套接字和文件),我会舍入结果,但使用我自己的例程来避免任何对象创建

一个更通用的舍入函数如下所示,但如果可以使用printf或DecimalFormat,则更简单

private static long TENS[] = new long[19]; static { 
    TENS[0] = 1; 
    for (int i = 1; i < TENS.length; i++) TENS[i] = 10 * TENS[i - 1]; 
} 

public static double round(double v, int precision) { 
    assert precision >= 0 && precision < TENS.length; 
    double unscaled = v * TENS[precision]; 
    assert unscaled > Long.MIN_VALUE && unscaled < Long.MAX_VALUE; 
    long unscaledLong = (long) (unscaled + (v < 0 ? -0.5 : 0.5)); 
    return (double) unscaledLong / TENS[precision]; 
}
private static long TENS[]=new long[19];静态{
十[0]=1;
对于(inti=1;i=0&&precisionLong.MIN\u值和未缩放

注意:您可以使用BigDecimal执行最终舍入。esp,如果您需要一个特定的round方法。

我认为这种情况对您的代码来说至少是可以挽救的。您可以通过ORM框架获得double值。然后,在对其进行任何数学/计算之前,可以使用静态valueOf方法将其转换为BigDecimal(请参见了解原因),然后将其转换回double(仅用于存储)

因为您无论如何都要扩展这些类,所以可以为double值添加getter,在需要时将其作为BigDecimal获取

这可能不能涵盖100%的情况(我会特别担心ORM或JDBC驱动程序如何将双精度转换回数字类型),但这比只对原始双精度进行计算要好得多


然而,从长远来看,我并不认为这种方法实际上对公司更便宜。

也建议这样做(第二版,第48项)。我希望它建议
BigDecimal
。。。如果你的数字和计算因为
double
而不正确,那么速度并没有多大意义。从OP来看,他似乎无法重构,因为框架(我假设他没有源代码)使用FP算法。+1表示“我的钱加倍”的标题,以了解你的情况。但是:现金和适当的会计对公司来说往往很重要。过失诉讼也是如此。你的处境很艰难:一方面,建议公司花费数年的开发时间来解决看似没有问题的问题。另一方面,也会带来大问题