Lightweight Java Decimal Class

生来就可爱ヽ(ⅴ<●) 提交于 2021-02-18 20:27:31

问题


I am considering writing two limited precision alternatives to BigDecimal, namely DecimalInt and DecimalLong. These would be capable of dealing with numbers within the real bounds of int and long with an arbitrary number of decimal places, creatable in both mutable and immutable form. My plan is to make DecimalInt support +/-999,999,999 to +/- 0.999999999 and DecimalLong the same, but with up to 18 digits.

This would be done by maintaining a decimal digit count value of 0-9 for DecimalInt and 0-18 for DecimalLong along side the actual value stored as a scaled int or long. The normal use would be for small numbers of decimals such as for money and stock prices, typically 2-4 decimal places.

The essential requirements are (a) lean footprint (2 classes, plus OverflowException), and (b) full support of all basic operations plus all of Math that makes sense.

Googling for results did not return any obvious hits - they all seemed to pertain to arbitrary decimals.

My questions are: Has this already been done? Are there hidden subtleties in this which is why it has not already been done? Has anyone heard rumors of Java supporting a decimal type like DotNet's.

EDIT: This is different from BigDecimal because it should be (a) a hell of a lot more efficient to not deal with an array of ints, and (b) it won't wrap BigInteger so it will be leaner on memory too, and (c) it will have a mutable option so it will be faster there as well. In summary - less overhead for the simple use cases like "I want to store a bank balance without the overhead of BigDecimal and the inaccuracy of double".

EDIT: I intend on doing all the math using int or long to avoid the classic problem of: 1586.60-708.75=877.8499999999999 instead of 877.85


回答1:


I strongly suspect the reason why this has not been done is that the overhead of BigDecimal and BigInteger is not as relevant as you think, and avoiding it not worth the effort and the risk of getting it wrong in some subtle way.

To use your example: for any financial application, saving a few dozen bytes is a non-issue and limited precision a deal-breaker (stock prices my have typically 2-4 digits in the USA, but if you want to deal with emerging markets, you'll encounter currencies with runaway inflation, where a 15-digit sum buys you half a loaf of bread).

Basically, it sounds like just another case of premature optimization.




回答2:


Most people who are particularly concerned about rounding errors use BigDecimal and BigInteger which performs well enough in most situations.

However, cases where performance is more critical, using double with rounding does the job. This is often forgotten by newbies, but you can't just take a double result without sensible round and expect to get a sensible answer.

In the vast majority of cases doubles with rounding is all you need.

System.out.printf("%.2f%n", 1586.60-708.75);

prints

877.85



回答3:


If you are looking at a fixed, small number of decimal places for handling money, then this is usually done by holding integer (long if necessary) numbers of cents, or hundredths of a cent.

If you are dealing with money then you will need to be careful of how you handle rounding. If your calculations are going to be audited there are rules for how that sort of thing is done. Also I assume you are aware that some operations can't be done precisely (division being the obvious example).




回答4:


If your focus is for portable devices look at Real. Real allows for the precision of the number to be set from 0 to 16. It is designed for MIDP cell phones.

Also of interest, look at the constructive reals library. It's not lightweight though.

In reference to the comment below, can you not use the Apache Commons Math Library to work with fractions? Is there some reason that won't work?




回答5:


It seems to me that if you want arbitrary precision then you are going to need an undefined number of bits to represent the mantissa. That means that some sort of array allocation strategy is going to be necessary for the mantissa. You could craft your own here, but BigInteger does it fairly efficiently and it works

You need to specify what the smallest (non-zero) value you need to represent is. This will be 10^-(2^n), where n+1 is the number of bits you allocate to the exponent. With BigDecimal this is 10^-(2^31). You could use an arbitrary size exponent, but that range should be enough for anybody.

So what you need is an unbounded integer mantissa to give you the arbitrary precision, and a fixed size exponent, depending on what you want your minimum representable value to be. Essentially this is BigDecimal; the only change is you will use some smaller object rather than the int used by BigDecimal. I would doubt whether the space savings are worth it. I would think that BigDecimal is going to do what you need with barely any more memory usage than any solution you craft yourself.

Of course you could choose a maximum number of significant figures that you will need; then you need fixed size storage for both mantissa and exponent, and that's a whole lot less storage. Just use a fixed number of longs as the mantissa.



来源:https://stackoverflow.com/questions/354045/lightweight-java-decimal-class

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!