双倍我的钱:我的框架使用双倍来表示货币金额

发布于 2024-09-25 21:48:41 字数 438 浏览 1 评论 0原文

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

更糟糕的是,它使用的框架,以及框架自己的类,使用 double 来赚钱。

ORM 框架还处理从数据库检索值(以及存储到数据库)的操作。在数据库中,货币值的类型为 number(19, 7),但框架 ORM 将它们映射为双精度数。

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

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

实际上,我不能对我的公司说,“因为舍入错误,放弃了两年的工作,花费了数十万美元,在这个框架上编写代码”

I've inherited a project in which monetary amounts use the double type.

Worse, the framework it uses, and the framework's own classes, use double for money.

The framework ORM also handles retrieval of values from (and storage to) the database. In the database money values are type number(19, 7), but the framework ORM maps them to doubles.

Short of entirely bypassing the framework classes and ORM, is there anything I can do to calculate monetary values precisely?

Edit: yeah, I know BigDecimal should be used. The problem is that I am tightly tied to a framework that where, e.g., the class framework.commerce.pricing.ItemPriceInfo has members double mRawTotalPrice; and double mListPrice. My company's application's own code extends, e.g, this ItemPriceInfoClass.

Realistically, I can't say to my company, "scrap two years of work, and hundreds of thousands of dollars spent, basing code on this framework, because of rounding errors"

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

独自←快乐 2024-10-02 21:48:41

如果可以忍受,请将货币类型视为整体。换句话说,如果您在美国工作,如果美分可以提供您所需的粒度,请跟踪美分而不是美元。双精度数可以准确地表示非常大的值 (2^53) 的整数(没有舍入误差到该值)。

但实际上,正确的做法是完全绕过框架并使用更合理的东西。对于框架来说,这是一个如此业余的错误 - 谁知道还潜伏着什么?

If tolerable, treat the monetary type as integral. In other words, if you're working in the US, track cents instead of dollars, if cents provides the granularity you need. Doubles can accurately represent integers up to a very large value (2^53) (no rounding errors up to that value).

But really, the right thing to do is bypass the framework entirely and use something more reasonable. That's such an amateur mistake for the framework to make - who knows what else is lurking?

影子的影子 2024-10-02 21:48:41

我没看到你提到重构。我认为这是你最好的选择。与其拼凑一些技巧来让事情暂时变得更好,为什么不以正确的方式修复它呢?

以下是有关 double 与 double 的一些信息BigDecimal。这篇文章建议使用 BigDecimal,尽管它速度较慢。

I didn't see you mention refactoring. I think that's your best option here. Instead of throwing together some hacks to get things working better for now, why not fix it the right way?

Here's some information on double vs BigDecimal. This post suggests using BigDecimal even though it is slower.

私藏温柔 2024-10-02 21:48:41

很多人会建议使用 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

在我的例子中,我在从服务器发送时舍入结果(写入套接字和文件),但使用我自己的例程来避免任何对象创建。

更通用的round函数如下,但如果可以使用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 来执行最终舍入。特别是如果您需要特定的舍入方法。

Plenty of people will suggest using BigDecimal and if you don't know how to use rounding in your project, that is what you should do.

If you know how to use decimal rounding correctly, use double. Its many orders of magnitude faster, much clear and simpler and thus less error prone IMHO. If you use dollars and cents (or need two decimal places), you can get an accurate result for values up to 70 trillion dollars.

Basically, you won't get round errors if you correct for it using approriate rounding.

BTW: The thought of rounding errors strikes terror into the heart of many developers, but they are not random errors and you can manage them fairly easily.

EDIT: consider this simple example of a rounding error.

    double a = 100000000.01;
    double b = 100000000.09;
    System.out.println(a+b); // prints 2.0000000010000002E8

There are a number of possible rounding strategies. You can either round the result when printing/displaying. e.g.

    System.out.printf("%.2f%n", a+b); // prints 200000000.10

or round the result mathematically

    double c = a + b;
    double r= (double)((long)(c * 100 + 0.5))/100;
    System.out.println(r); // prints 2.000000001E8

In my case, I round the result when sending from the server (writing to a socket and a file), but use my own routine to avoid any object creation.

A more general round function is as follows, but if you can use printf or DecimalFormat, can be simpler.

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]; 
}

note: you could use BigDecimal to perform the final rounding. esp if you need a specifc round method.

方圜几里 2024-10-02 21:48:41

好吧,实际上您没有那么多选择:

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

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

保持现状并希望没有人注意到(这是一个笑话)。

恕我直言,最好的解决方案就是简单地重构它。这可能是一些繁重的重构,但邪恶已经完成,我相信这应该是你最好的选择。

最好的,
瓦西尔

​哦,你可以将金钱视为整数(数美分),但如果你要进行货币兑换、计算利息等,这听起来不是一个好主意。

Well, you don't have that many options in reality:

You can refactor the project to use e.g. BigDecimal (or something that better suits its needs) to represent money.

Be extremely careful for overflow/underflow and loss of precision, which means adding tons of checks, and refactoring even larger proportion of the system in an unnecessary way. Not to mention how much research would be necessary if you are to do that.

Keep things the way they are and hope nobody notices (this is a joke).

IMHO, the best solution would be to simply refactor this out. It might be some heavy refactoring, but the evil is already done and I believe that it should be your best option.

Best,
Vassil

P.S. Oh and you can treat money as integers (counting cents), but that doesn't sound like a good idea if you are going to have currency conversions, calculating interest, etc.

心奴独伤 2024-10-02 21:48:41

我认为这种情况至少对您的代码来说是可以挽救的。您可以通过 ORM 框架获取双精度值。然后,您可以使用静态 valueOf 方法将其转换为 BigDecimal (请参阅

由于无论如何您都在扩展这些类,因此您可以为 double 值添加 getter,以便在需要时将它们获取为 BigDecimal。

这可能无法覆盖 100% 的情况(我特别担心 ORM 或 JDBC 驱动程序正在做什么将双精度数转换回 Number 类型),但它比仅对原始双精度数进行数学计算要好得多。

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

I think this situation is at least minimally salvageable for your code. You get the value as a double via the ORM framework. You can then convert it to BigDecimal using the static valueOf method (see here for why) before doing any math/calculations on it, and then convert it back to double only for storing it.

Since you are extending these classes anyway, you can add getters for your double value that gets them as BigDecimal when you need it.

This may not cover 100% of the cases (I would be especially worried about what the ORM or JDBC driver is doing to convert the double back to a Number type), but it is so much better than just doing the math on the raw doubles.

However, I am far from convinced that this approach is actually cheaper for the company in the long run.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文