I have a number in the format of (13,2) 13 digits and 2 decimal places. I need to do some calculation on it (like multiplication, division). I am planning to use BigDecimal for the calculations. Shall i use double or float for the calculation as BigDecimal is bit on slower side?
The most important consideration is not speed but correctness.
If your value is a sample of a continuous value, like a measurement of a real-world property like size, distance, weight, angle, etc., then an IEEE-754 double or float is probably a better choice. This is because in this case powers of ten are not necessarily "rounder" than other values (e.g. angular measurements in radians can be transcendental numbers but still "round").
If your value is a discrete value like a measurement of money, then double is incorrect and a floating-point decimal type like BigDecimal is correct. This is because, in this case, discrete increments are meaningful, and a value of "0.01" is "rounder" and more correct than a number like "0.009999999999999" or "0.010000000000000001".
The simplest, most natural representation for data with two decimal places is BigDecimal with scale factor 2. Start with that. In most cases it will be fast enough.
If, when measured, it really is a serious performance problem, there are two more options:
Use long to represent the number of hundredths. For example, US currency can be represented exactly as a long number of cents. Be very careful to ensure variable names and comments make it clear where dollars are being used, and where cents are being used.
Use double to represent the amount as a fraction. This avoids the dollars-vs-cents bookkeeping, at the expense of rounding issues. You may need to periodically correct the rounding by multiplying by 100, rounding to nearest integer, and dividing by 100 again
Related
Immutable, arbitrary-precision signed decimal numbers. A BigDecimal consists of an arbitrary precision integer unscaled value and a 32-bit integer scale. If zero or positive, the scale is the number of digits to the right of the decimal point. If negative, the unscaled value of the number is multiplied by ten to the power of the negation of the scale. The value of the number represented by the BigDecimal is therefore (unscaledValue × 10-scale).
That is what the doc says. Based on the docs it appears that BigDecimal i useful when
You are dealing with super large numbers
You are concerned about precision
But are there any other scenarios where BigDecimal is a better choice ?
Double is a floating value, meaning that it is not an exact value. Therefore, you need to use BigDecimal which gives you the exact value.
Double will display only 15 significant decimal digits but you can have as many significant digits in BigDecimal as you wish. You can set the value using MathContext class.
BigDecimal is used when you are writing code for developing applications like scientific calculators.
The question maybe has some good sizes to check, for example the financial one is an example, I'll took it from here because I liked it:
Primer on Financial Issues
Currency calculations require precision to a specific degree, such as two digits after the decimal for most currencies. They also require a specific type of rounding behavior, such as always rounding up in the case of taxes.
For example, suppose we have a product which costs 10.00 in a given currency and the local sales tax is 0.0825, or 8.25%. If we work it out on paper, the tax amount is,
10.00 * 0.0825 = 0.825
Because our precision for the currency is two digits after the decimal, we need to round the 0.825 figure. Also, because this is a tax, it is good practice to always round up to the next highest cent. That way when the accounts are balanced at the end of the day, we never find ourselves underpaying taxes.
0.825 -> 0.83
And so the total we charge to the customer is 10.83 in the local currency and pay 0.83 to the tax collector. Note that if we sold 1000 of these, we would have overpaid the collector by this much,
1000 * (0.83 - 0.825) = 5.00
Another important issue is where to do the rounding in a given computation. Suppose we sold Liquid Nitrogen at 0.528361 per liter. A customer comes in and buys 100.00 liters, so we write out the total price,
100.0 * 0.528361 = 52.8361
Because this isn't a tax, we can round this either up or down at our discretion. Suppose we round according to standard rounding rules: If the next significant digit is less than 5, then round down. Otherwise round up. This gives us a figure of 52.84 for the final price.
Now suppose we want to give a promotional discount of 5% off the entire purchase. Do we apply this discount on the 52.8361 figure or the 52.84 figure? What's the difference?
Calculation 1: 52.8361 * 0.95 = 50.194295 = 50.19 Calculation 2:
52.84 * 0.95 = 50.198 = 50.20
Note that we rounded the final figure by using the standard rounding rule.
See how there's a difference of one cent between the two figures? The old code never bothered to consider rounding, so it always did computations as in Calculation 1. But in the new code we always round before applying promotions, taxes, and so on, just like in Calculation 2. This is one of the main reasons for the one cent error.
I have two large long values, e.g. long a = 106951484895 and long b = 47666297253. I want to divide one by the other, while still retaining precision and accuracy. a / b gives just 2, which is neither precise nor accurate. (double)a / b returns 2.243754834308401 which is precise, but I don't know whether it is accurate. Is it accurate, or is there a better way?
If you check the calculation in Wolfram Alpha, you'll see that the exact result is
2.243754834308400900535121747859167616725725368773485418854923... Your figure of
2.243754834308401 is dead on. Unless you need more precision, the calculation with doubles will suffice.
In Java there is a BigInteger class, for when you need unlimited precision using whole numbers. For decimal numbers, use BigDecimal
Wolfram Alpha will give you 2.243754834308400900535121747859167616725725368773485418854923..., and our figure is 2.243754834308401, which is same as
bigDecimal1.divide(bigDecimal2, MathContext.DECIMAL64)
If you will go for
bigDecimal1.divide(bigDecimal2, new MathContext(1000, RoundingMode.HALF_EVEN)))
You will get
2.24375483430840090053512174785916761672572536877348541885492361677904883097801043287594504
1050407263946829396490609763285697017511527160785819555506643456210143732768535294645618694
7909645722613183738163350810420038396599808994188248448801742297144651257940243013656347535
1346061895880989881427322957327423437070470786123178209350642720039431463073874604572487035
0881416301899886949878414127297558394219666911873273296141755170873372894249298571586701215
5058865276866526572281643300564007415077913939597358974242706109866167162174559269200972437
4468185209762552814414640557312348785977139301334520631262090283427956618755742143233766150
5750523038219597199472866720344664485953248792408356275728443143815091921547456137582778817
3173376404446432448382818379181981559569409501831858179303080342832602945081961262782040747
0742627855109348071601512026092932232568603874560325500767085983329631127368742840999544420
8119473080650114494849915293461361824567061678496514955637978679644265088391509259402889160
For more accuracy you can keep increasing the precision given in the MathContext constructor.
This RoundingMode.HALF_EVEN aka Banker's rounding is analogous to the rounding policy used for float and double arithmetic in Java.
I have the following statement:
float diff = tempVal - m_constraint.getMinVal();
tempVal is declared as a float and the getMinVal() returns a float value.
I have the following print out:
diff=0.099999905, tempVal=5.1, m_constraint.getMinVal()=5.0
I expect the diff is 0.1 but not the above number. how to do that?
Floats use the IEEE754 to represent numbers, and that system has some rounding errors.
Floating point guide
What Every Computer Scientist Should Know About Floating-Point Arithmetic
Wikipedia on IEE754
Bottom-line if you are doing arithmetic and it needs to be exact don't use float or double but us BigDecimal
Because of the way they store values internally, floats and doubles can only store completely accurately numbers which can be decomposed into a sum of powers of 2 (and then, within certain constraints relating to their absolute and relative magnitude).
So as soon as you attempt to store, or perform a calculating involving, a number which cannot be stored exactly, you are going to get an error in the final digit.
Usually this isn't a problem provided you use floats and doubles with some precaution:
use a size of floating point primitive which has "spare" digits of precision beyond what you need;
for many applications, this probably means don't use float at all (use double instead): it has very poor precision and, with the exception of division, has no performance benefit on many processors;
when printing FP numbers, only actually print and consider the number of digits of precision that you need, and certainly don't include the final digit (use String.format to help you);
if you need arbitrary number of digits of precision, use BigDecimal instead.
You cannot get exact results with floating point numbers. You might need to use a FixedPoint library for that. See : http://sourceforge.net/projects/jmfp/
Java encodes real numbers using binary floating point representations defined in IEEE 754. Like all finite representations it cannot accurately represent all real numbers because there is far more real numbers than potential representations. Numbers which cannot be represented exactly (like 0.1 in your case) are rounded to the nearest representable number.
I know the problem with double/float, and it's recommended to use BigDecimal instead of double/float to represent monetary fields. But double/float is more effective and space-saving. Then my question is:
It's acceptable to use double/float to represent monetary fields in Java class, but use BigDecimal to take care of the arithmetic (i.e. convert double/float to BigDecimal before any arithmetic) and equal-checking?
The reason is to save some space. And I really see lots of projects are using double/float to represent the monetary fields.
Is there any pitfall for this?
Thanks in advance.
No, you can't.
Suppose double is enough to store two values x and y. Then you convert them to safe BigDecimal and multiple them. The result is accurate, however if you store the multiplication result back in double, chances are you will loose the precision. Proof:
double x = 1234567891234.0;
double y = 1234567891234.0;
System.out.println(x);
System.out.println(y);
BigDecimal bigZ = new BigDecimal(x).multiply(new BigDecimal(y));
double z = bigZ.doubleValue();
System.out.println(bigZ);
System.out.println(z);
Results:
1.234567891234E12 //precise 'x'
1.234567891234E12 //precise 'y'
1524157878065965654042756 //precise 'x * y'
1.5241578780659657E24 //loosing precision
x and y are accurate, as well as the multiplication using BigDecimal. However after casting back to double we loose least significant digits.
I would also recommend that you use nothing but BigDecimal for ALL arithmetic that may involve currency.
Make sure that you always use the String constructor of BigDecimal. Why? Try the following code in a JUnit test:
assertEquals(new BigDecimal("0.01").toString(), new BigDecimal(0.01).toString());
You get the following output:
expected:<0.01[]> but was <0.01[000000000000000020816681711721685132943093776702880859375]>
The truth is, you cannot store EXACTLY 0.01 as a 'double' amount. Only BigDecimal stores the number you require EXACTLY as you want it.
And remember that BigDecimal is immutable. The following will compile:
BigDecimal amount = new BigDecimal("123.45");
BigDecimal more = new BigDecimal("12.34");
amount.add(more);
System.out.println("Amount is now: " + amount);
but the resulting output will be:
Amount is now: 123.45
That's because you need to assign the result to a new (or the same) BigDecimal variable.
In other words:
amount = amount.add(more)
What is acceptable depends on your project. You can use double and long in some projects may be expected to do so. However in other projects, this is considered unacceptable. As a double you can represent values up to 70,000,000,000,000.00 to the cent (larger than the US national debt), with fixed place long you can represent 90,000,000,000,000,000.00 accurately.
If you have to deal with hyper-inflationary currencies (a bad idea in any case) but for some reason still need to account for every cent, use BigDecimal.
If you use double or long or BigDecimal, you must round the result. How you do this varies with each data type and BigDecimal is the least error prone as you are requires to specify what rounding and the precision for different operations. With double or long, you are left to your own devices.
long will be much better choice than double/float.
Are you sure that using BigDecimal type will be a real bottleneck?
Pit fall is that floats/doubles can not store all values without losing precision. Even if you do your use BigDecimal and preserve precision during calculations, you are still storing the end product as a float/double.
The "proper" solution to this, in my experience, is to store monetary values as integers (e.g. Long) representing thousands of a dollar. This gives sufficient resolution for most tasks, e.g. interest accruement, while side stepping the problem of using floats/doubles. As an added "bonus", this requires about the same amount of storage as floats/doubles.
If the only use of double is to store decimal values, then yes, you can under some conditions: if you can guarantee that your values have no more than 15 decimal digits, then converting a value to double (53 bits of precision) and converting the double back to decimal with 15-digit precision (or less) will give you the original value, i.e. without any loss, from an application of David Matula's theorem proved in his article In-and-out conversions. Note that for this result to be applicable, the conversions must be done with correct rounding.
Note however that a double may not be the best choice: monetary values are generally expressed not in floating point, but in fixed point with a few digits (p) after the decimal point, and in this case, converting the value to an integer with a scaling by 10^p and storing this integer (as others suggested) is better.
Should we use double or BigDecimal for calculations in Java?
How much is the overhead in terms of performance for BigDecimal as compared to double?
For a serious financial application BigDecimal is a must.
Depends on how many digits you need you can go with a long and a decimal factor for visualization.
For general floating point calculations, you should use double. If you are absolutely sure that you really do need arbitrary precision arithmetic (most applications don't), then you can consider BigDecimal.
You will find that double will significantly outperform BigDecimal (not to mention being easier to work with) for any application where double is sufficient precision.
Update: You commented on another answer that you want to use this for a finance related application. This is one of the areas where you actually should consider using BigDecimal, otherwise you may get unexpected rounding effects from double calculations. Also, double values have limited precision, and you won't be able to accurately keep track of pennies at the same time as millions of dollars.
How much is the overhead in terms of performance for BigDecimal as compared to double?
A lot. For example, a multiplication of two doubles is a single machine instruction. Multiplying two BigDecimals is probably a minimum of 50 machine instructions, and has complexity of O(N * M) where M and N are the number of bytes used to represent the two numbers.
However, if your application requires the calculation to be "decimally correct", then you need to accept the overhead.
However (#2) ... even BigDecimal can't do this calculation with real number accuracy:
1/3 + 1/3 + 1/3 -> ?
To do that computation precisely you would need to implement a Rational type; i.e. a pair of BigInteger values ... and some thing to reduce the common factors.
However (#3) ... even a hypothetical Rational type won't give you a precise numeric representation for (say) Pi.
As always: it depends.
If you need the precision (even for "small" numbers, when representing amounts for example) go with BigDecimal.
In some scientific applications, double may be a better choice.
Even in finance we can't answer without knowing what area. For instance if you were doing currency conversions of $billions, where the conversion rate could be to 5 d.p. you might have problems with double. Whereas for simply adding and subtracting balances you'd be fine.
If you don't need to work in fractions of a cent/penny, maybe an integral type might be more appropriate, again it depends on the size of numbers involved.