Java 双倍我的钱:我的框架使用双倍的货币金额
我继承了一个项目,其中货币金额使用双重类型 更糟糕的是,它使用的框架和框架本身的类都使用double来赚钱 ORM框架还处理从数据库(和存储到数据库)检索值。在数据库中,货币值是number(19,7)类型,但框架ORM将其映射为double 除了完全绕过框架类和ORM之外,我还能做些什么来精确计算货币价值吗 编辑:是的,我知道应该使用BigDecimal。问题是,我与一个框架紧密相连,例如,类framework.commerce.pricing.ItemPriceInfo的成员是双重mRawTotalPrice;还有双份米饭。我公司的应用程序自己的代码扩展了,例如,这个ItemPriceInfoClassJava 双倍我的钱:我的框架使用双倍的货币金额,java,double,currency,Java,Double,Currency,我继承了一个项目,其中货币金额使用双重类型 更糟糕的是,它使用的框架和框架本身的类都使用double来赚钱 ORM框架还处理从数据库(和存储到数据库)检索值。在数据库中,货币值是number(19,7)类型,但框架ORM将其映射为double 除了完全绕过框架类和ORM之外,我还能做些什么来精确计算货币价值吗 编辑:是的,我知道应该使用BigDecimal。问题是,我与一个框架紧密相连,例如,类framework.commerce.pricing.ItemPriceInfo的成员是双重mRawT
事实上,我不能对我的公司说,“由于舍入错误,基于这个框架的代码浪费了两年的工作和几十万美元”我没有看到你提到重构。我想这是你最好的选择。为什么不用正确的方法来修复它,而不是拼凑一些技巧来让事情更好地运转呢
这里有一些关于你的信息。这篇文章建议使用
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表示“我的钱加倍”的标题,以了解你的情况。但是:现金和适当的会计对公司来说往往很重要。过失诉讼也是如此。你的处境很艰难:一方面,建议公司花费数年的开发时间来解决看似没有问题的问题。另一方面,也会带来大问题