Is there a generally accepted way to get Euler's number in BigDecimal?
I basically want to do Math.exp(double a) and I basically want the same function but calculated using BigDecimal to prevent precision loss with other calculations.
I thought of just doing,
BigDecimal.valueOf(Math.E).pow(BigDecimal.SOMEOTHERNUMBER)
But not sure if that's the right way to approach the problem.
The problem is that Euler's number is irrational; in BigDecimal, you'd have to have infinite memory to hold it. The reason there's no previously-stored value is that however many decimal places you needed, you wouldn't have the right number for your current application.
You're going to have to determine how many decimals you want, then construct the BigDecimal representation using the String constructor (new BigDecimal("2.71828...")).
Well, Math.E is a double. If you want a higher precision representation of Euler's number, you can create it with some arbitrary precision of decimal places, or there are BigDecimal math libraries with a really high precision version of E.
You could approximate one yourself with something like this: Better approximation of e with Java
While the double Math.E should be accurate enough for most calculations, there is a way to calculate e to greater accuracy.
If you want to get e that has more accuracy that double can contain, you calculate it by adding up the sum of 1/0!, 1/1!, 1/2!, 1/3!...
Where ! means factorial. Just add up the reciprocal of all the factorials starting from zero. The more terms you add up, the more precise you will get. Of course since e is irrational, you can never get the real value, but you can get pretty darn close.
Related
I'm using an implementation of this answer to calculate the distance between two points for my GPS tracking software.
However I've read on numerous StackOverFlow that using double is bad because of the inaccurate float representation of values. Therefore, I'm asking :
Would using BigDecimal to calculate distances be more accurate ?
Would converting the result from BigDecimal back to double produce additional inaccuracies ? (to store or retrieve the value)
No, using BigDecimal instead of double will not significantly improve the accuracy of this algorithm.
The main source of error is a modeling error: you're using a formula that assumes the Earth is a perfect sphere. This means the result can be wrong by as much as 0.5%. The rounding error you get from using double is on the order of 0.000000000001%.
To get more accurate results, you could use for example Vincenty's formulae, which assumes the Earth is an ellipsoid, but it is harder to implement than the great circle distance. To get even more accurate results, you should take into account the local topography, which is even harder.
For further reading: https://en.m.wikipedia.org/wiki/Geographical_distance
Yes, in theory doing any calculations in BigDecimal could lead to better precisions, but that is very unlikely to actually help for several reasons:
as you found out there are non-naive algorithms to calculate the distance using only double without an excessive numerical error.
the precision of double values for this use case is many orders of magnitude higher than the precision and accuracy of any positional information you might start out with. That means that the measurement error will definitely be bigger than the numerical error caused by using double.
And if you did your calculations in BigDecimal anyway and then converted the result to double, then you'd lose precision, of course, since BigDecimal can be effectively arbitrarily precise, but double can't.
But again: that loss of precision is irrelevant for the use case you describe.
I am working with BigDecimal and I know that if I divide I have to use MathContext and tell which Scale and RoundingMode to avoid ArithmeticException as described in the documentation:
In the case of divide, the exact quotient could have an infinitely
long decimal expansion; for example, 1 divided by 3. If the quotient
has a nonterminating decimal expansion and the operation is specified
to return an exact result, an ArithmeticException is thrown.
In the method I'm working on I have to sum amounts coming from our database (that are rounded at 2 decimals) with amounts coming from an external service, and I don't know the exact scaling of these amounts (probabily 3 decimals).
My question is, can I trust BigDecimal's add method and use it without rounding and scaling or it's a good practice to always specify the desired scale?
Is there any particular case in which addition and subtraction can raise ArithmeticException?
BigDecimal.add() will throw ArithmeticException if the scale of the result does not fit into an int.
A simple example is adding two numbers with the maximum and the minimum scales:
BigDecimal a = new BigDecimal(BigInteger.ONE, Integer.MIN_VALUE);
BigDecimal b = new BigDecimal(BigInteger.ONE, Integer.MAX_VALUE);
a.add(b);
If your application needs to operate at such scale then you probably have some bigger problems than worrying about arithmetic exception.
Adding numbers without using MathContext will maintain proper scale and give you the precice result. Depending on the actual values this approach can use arbitrary amount of memory to represent increasingly long numbers, and longer numbers take more time to add.
Adding numbers without using MathContext and rouding once after the summation will give you the precice result rounded to the requested MathContext. The memory and computation costs are the same as in the first case.
Using MathContext for each addition will produce a result which can differ from the presize result by an arbitrary value, but the memory and speed will be more predicatable.
Choosing which one of these approaches to use really depends on the nature of the task, so it is up to you to assess and chose the proper approach for each particular case.
I'm working with money so I need my results to be accurate but I only need a precision of 2 decimal points (cents). Is BigDecimal needed to guarantee results of multiplication/division are accurate?
BigDecimal is a very appropriate type for decimal fraction arithmetic with a known number of digits after the decimal point. You can use an integer type and keep track of the multiplier yourself, but that involves doing in your code work that could be automated.
As well as managing the digits after the decimal point, BigDecimal will also expand the number of stored digits as needed - many business and government financial calculations involve sums too large to store in cents in an int.
I would consider avoiding it only if you need to store a very large array of amounts of money, and are short of memory.
One common option is to do all your calculation with integer or long(the cents value) and then simply add two decimal places when you need to display it.
Similarly, there is a JODA Money library that will give you a more full-featured API for money calculations.
It depends on your application. One reason to use that level of accuracy is to prevent errors accumulated over many operations from percolating up and causing loss of valuable information. If you're creating a casual application and/or are only using it for, say, data entry, BigDecimal is very likely overkill.
+1 for Patricias answer, but I very strongly discourage anyone to implement own classes with an integer datatype with fixed bitlength as long as someone really do not know what you are doing. BigDecimal supports all rounding and precision issues while a long/int has severe problems:
Unknown number of fraction digits: Trade exchanges/Law/Commerce are varying in their amount
of fractional digits, so you do not know if your chosen number of digits must be changed and
adjusted in the future. Worse: There are some things like stock evaluation which need a ridiculous amount of fractional digits. A ship with 1000 metric tons of coal causes e.g.
4,12 € costs of ice, leading to 0,000412 €/ton.
Unimplemented operations: It means that people are likely to use floating-point for
rounding/division or other arithmetic operations, hiding the inexactness and leading to
all the known problems of floating-point arithmetic.
Overflow/Underflow: After reaching the maximum amount, adding an amount results in changing the sign. Long.MAX_VALUE switches to Long.MIN_VALUE. This can easily happen if you are doing fractions like (a*b*c*d)/(e*f) which may perfectly valid results in range of a long, but the intermediate nominator or denominator does not.
You could write your own Currency class, using a long to hold the amount. The class methods would set and get the amount using a String.
Division will be a concern no matter whether you use a long or a BigDecimal. You have to determine on a case by case basis what you do with fractional cents. Discard them, round them, or save them (somewhere besides your own account).
In my JAVA program there is code like this:
int f_part = (int) ((f_num - num) * 100);
f_num is double and num is long. I just want to take the fractional part out and assign it to f_part. But some times f_part value is one less than it's value. Which means if f_num = 123.55 and num = 123, But f_part equals to 54. And it happens only f_num and num is greater than 100. I don't know why this happening. Please can someone explain why this happens and way to correct it.
This is due to the limited precision in doubles.
The root of your problem is that the literal 123.55 actually represents the value 123.54999....
It may seem like it holds the value 123.55 if you print it:
System.out.println(123.55); // prints 123.55
but in fact, the printed value is an approximation. This can be revealed by creating a BigDecimal out of it, (which provides arbitrary precision) and print the BigDecimal:
System.out.println(new BigDecimal(123.55)); // prints 123.54999999999999715...
You can solve it by going via Math.round but you would have to know how many decimals the source double actually entails, or you could choose to go through the string representation of the double in fact goes through a fairly intricate algorithm.
If you're working with currencies, I strongly suggest you either
Let prices etc be represented by BigDecimal which allows you to store numbers as 0.1 accurately, or
Let an int store the number of cents (as opposed to having a double store the number of dollars).
Both ways are perfectly acceptable and used in practice.
From The Floating-Point Guide:
internally, computers use a format (binary floating-point) that cannot
accurately represent a number like 0.1, 0.2 or 0.3 at all.
When the code is compiled or interpreted, your “0.1” is already
rounded to the nearest number in that format, which results in a small
rounding error even before the calculation happens.
It looks like you're calculating money values. double is a completely inappropriate format for this. Use BigDecimal instead.
int f_part = (int) Math.round(((f_num - num) * 100));
This is one of the most often asked (and answered) questions. Floating point arithmetics can not produce exact results, because it's impossible to have an inifinity of real numbers inside 64 bits. Use BigDecimal if you need arbitrary precision.
Floating point arithmetic is not as simple as it may seem and there can be precision issues.
See Why can't decimal numbers be represented exactly in binary?, What Every Computer Scientist Should Know About Floating-Point Arithmetic for details.
If you need absolutely sure precision, you might want to use BigDecimal.
I thought java.math.BigDecimal is supposed to be The Answer™ to the need of performing infinite precision arithmetic with decimal numbers.
Consider the following snippet:
import java.math.BigDecimal;
//...
final BigDecimal one = BigDecimal.ONE;
final BigDecimal three = BigDecimal.valueOf(3);
final BigDecimal third = one.divide(three);
assert third.multiply(three).equals(one); // this should pass, right?
I expect the assert to pass, but in fact the execution doesn't even get there: one.divide(three) causes ArithmeticException to be thrown!
Exception in thread "main" java.lang.ArithmeticException:
Non-terminating decimal expansion; no exact representable decimal result.
at java.math.BigDecimal.divide
It turns out that this behavior is explicitly documented in the API:
In the case of divide, the exact quotient could have an infinitely long decimal expansion; for example, 1 divided by 3. If the quotient has a non-terminating decimal expansion and the operation is specified to return an exact result, an ArithmeticException is thrown. Otherwise, the exact result of the division is returned, as done for other operations.
Browsing around the API further, one finds that in fact there are various overloads of divide that performs inexact division, i.e.:
final BigDecimal third = one.divide(three, 33, RoundingMode.DOWN);
System.out.println(three.multiply(third));
// prints "0.999999999999999999999999999999999"
Of course, the obvious question now is "What's the point???". I thought BigDecimal is the solution when we need exact arithmetic, e.g. for financial calculations. If we can't even divide exactly, then how useful can this be? Does it actually serve a general purpose, or is it only useful in a very niche application where you fortunately just don't need to divide at all?
If this is not the right answer, what CAN we use for exact division in financial calculation? (I mean, I don't have a finance major, but they still use division, right???).
If this is not the right answer, what CAN we use for exact division in financial calculation? (I mean, I don't have a finance major, but they still use division, right???).
Then I was in primary school1, they taught me that when you divide by 1 by 3 you get a 0.33333... i.e. a recurring decimal. Division of numbers represented in decimal form is NOT exact. In fact for any fixed base there will be fractions (the result of dividing one integer by another) that cannot be represented exactly as a finite precision floating point number in that base. (The number will have a recurring part ...)
When you do financial calculations involving division, you have to consider the what to do with a recurring fraction. You can round it up, or down, or to the nearest whole number, or something else, but basically you cannot just forget about the issue.
The BigDecimal javadoc says this:
The BigDecimal class gives its user complete control over rounding behavior. If no rounding mode is specified and the exact result cannot be represented, an exception is thrown; otherwise, calculations can be carried out to a chosen precision and rounding mode by supplying an appropriate MathContext object to the operation.
In other words, it is your responsibility to tell BigDecimal what to do about rounding.
EDIT - in response to these followups from the OP.
How does BigDecimal detect infinite recurring decimal?
It does not explicitly detect the recurring decimal. It simply detects that the result of some operation cannot be represented exactly using the specified precision; e.g. too many digits are required after the decimal point for an exact representation.
It must keep track of and detect a cycle in the dividend. It COULD HAVE chosen to handle this another way, by marking where the recurring portion is, etc.
I suppose that BigDecimal could have been specified to represent a recurring decimal exactly; i.e. as a BigRational class. However, this would make the implementation more complicated and more expensive to use2. And since most people expect numbers to be displayed in decimal, and the problem of recurring decimal recurs at that point.
The bottom line is that this extra complexity and runtime cost would be inappropriate for typical use-cases for BigDecimal. This includes financial calculations, where accounting conventions do not allow you to use recurring decimals.
1 - It was an excellent primary school. You may have been taught this in high school.
2 - Either you try to remove common factors of the divisor and dividend (computationally expensive), or allow them to grow without bounds (expensive in space usage and computationally expensive for subsequent operations).
The class is BigDecimal not BigFractional. From some of your comments it sounds like you just want to complain that someone didn't build in all possible number handling algorithms into this class. Financial apps do not need infinite decimal precision; just perfectly accurate values to the precision required (typically 0, 2, 4, or 5 decimal digits).
Actually I have dealt with many financial applications that use double. I don't like it but that was the way they are written (not in Java either). When there are exchange rates and unit conversions then there are both the potential of rounding and bruising problems. BigDecimal eliminates the later but there is still the former for division.
If you want to work with decimals, not rational numbers, and you need exact arithmetics before the final rounding (rounding to cents or something), here's a little trick.
You can always manipulate your formulas so that there's only one final division. That way you won't lose precision during calculations and you'll always get the correctly rounded result. For instance
a/b + c
equals
(a + bc) / b.
By the way, I'd really appreciate
insight from people who've worked with
financial software. I often heard
BigDecimal being advocated over double
In financial reports we use alwasy BigDecimal with scale = 2 and ROUND_HALF_UP, since all printed values in a report must be lead to a reproducable result. If someone checks this using a simple calculator.
In switzerland they round to 0.05 since they no longer have 1 or 2 Rappen coins.
You should prefer BigDecimal for finance calculations. Rounding should be specified by the business. E.g. an amount (100,00$) has to be split equally across three accounts. There has to be a business rule which account takes the extra cent.
Double, floats are not approriate for use in financial applications because they can not represent fractions of 1 precisely that are not exponentials of 2. E.g. consider 0.6 = 6/10 = 1*1/2 + 0*1/4 + 0*1/8 + 1*1/16 + ... = 0.1001...b
For mathematic calculations you can use a symbolic number, e.g. storing denominator and numerator or even a whole expression (e.g. this number is sqrt(5)+3/4). As this is not the main use case of the java api you won' find it there.
Is there a need for
a=1/3;
b=a*3;
resulting in
b==1;
in financial systems? I guess not. In financial systems it is defined, which roundmode and scale has to be used, when doing calculations. In some situations, the roundmode and scale is defined in the law. All components can rely on such a defined behaviour. Returning b==1 would be a failure, because it would not fulfill the specified behaviour. This is very important when calculating prices etc.
It is like the IEEE 754 specifications for representing floats in binary digits. A component must not optimize a "better" representation without loss of information, because this will break the contract.
To divide save, you have to set the MATHcontext,
BigDecimal bd = new BigDecimal(12.12, MathContext.DECIMAL32).divide(new BigDecimal(2)).setScale(2, RoundingMode.HALF_UP);
I accept that Java doesn't have great support for representing fractions, but you have to realise that it is impossible to keep things entirely precise when working with computers. At least in this case, the exception is telling you that precision is being lost.
As far as I know, "infinite precision arithmetic with decimal numbers" just isn't going to happen. If you have to work with decimals, what you're doing is probably fine, just catch the exceptions. Otherwise, a quick google search finds some interesting resources for working with fractions in Java:
http://commons.apache.org/math/userguide/fraction.html
http://www.merriampark.com/fractions.htm
Best way to represent a fraction in Java?
Notice we are using a computer... A computer has a lot of ram and precision takes ram. So when you want an infinite precision you need
(infinite * infinite) ^ (infinite * Integer.MAX_VALUE) terrabyte ram...
I know 1 / 3 is 0.333333... and it should be possible to store it in ram like "one divided by three" and then you can multiply it back and you should have 1. But I don't think Java has something like that...
Maybe you have to win the Nobel Price for writing something doing that. ;-)