我继承了一个项目,其中货币金额使用双精度类型。
更糟糕的是,它使用的框架,以及框架自己的类,都使用double来赚钱。
框架ORM还处理从数据库中检索值(以及将值存储到数据库中)。在数据库中,钱的值是类型number(19,7),但是框架ORM将它们映射为双精度。
除了完全绕过框架类和ORM之外,我还能做些什么来精确地计算货币价值呢?
编辑:是的,我知道应该使用BigDecimal。问题是,我与一个框架紧密相关,例如,类framework.commerce.pricing.ItemPriceInfo具有成员double mRawTotalPrice;和double mListPrice。我公司的应用程序自己的代码扩展了,例如,这个ItemPriceInfoClass。
实际上,我不能对我的公司说,“放弃两年的工作,花费数十万美元,基于这个框架的代码,因为四舍五入误差。”
发布于 2010-10-06 04:31:53
如果可以接受,请将货币类型视为整型。换句话说,如果你在美国工作,跟踪美分而不是美元,如果美分提供了你需要的粒度。Double可以精确地表示直到a very large value (2^53)的整数(在该值之前没有舍入误差)。
但实际上,正确的做法是完全绕过框架,使用更合理的东西。对于这个框架来说,这是一个业余的错误--谁知道还潜伏着什么呢?
发布于 2010-10-06 04:29:54
我没看到你提到重构。我认为这是你最好的选择。与其拼凑一些技巧来让事情现在运行得更好,为什么不以正确的方式修复它呢?
这里有一些关于double vs BigDecimal的信息。这篇文章建议使用BigDecimal,尽管它更慢。
发布于 2010-10-06 04:55:39
很多人会建议使用BigDecimal,如果你不知道如何在你的项目中使用舍入,那就是你应该做的。
如果您知道如何正确使用小数舍入,请使用double。它的数量级更快,更清晰,更简单,因此不太容易出错。如果您使用美元和美分(或需要两位小数点),您可以获得高达70万亿美元的准确结果。
基本上,如果你使用适当的舍入来修正它,你就不会得到舍入误差。
顺便说一下:舍入错误的想法让许多开发人员感到恐惧,但它们不是随机错误,您可以相当容易地管理它们。
编辑:考虑这个舍入误差的简单示例。
double a = 100000000.01;
double b = 100000000.09;
System.out.println(a+b); // prints 2.0000000010000002E8有许多可能的舍入策略。您可以在打印/显示时对结果进行舍入。例如:
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];
}注意:您可以使用BigDecimal来执行最后的舍入。如果您需要特定的舍入方法,请使用esp。
https://stackoverflow.com/questions/3867445
复制相似问题