I'm rewriting some scientific code that someone else wrote a while back, and throughout the code constants are always declared as:
final double value = 2.0000000000D;
with an apparently arbitrary length of supposedly significant digits attached to it. I'm 95% sure that declaring variables in this way actually does nothing, and that setting the value to:
double value = 2.0;
would be exactly the same. But just to be sure, I'm asking SO, does declaring a constant in this fashion make any (meaningful) difference, or is this likely just a relic from another language where it might have made a difference?
EDIT: In response to an answer below: Yes, I verified that the answer is the same in this particular instance before asking this question. Maybe I should have been more specific. Is there ever an instance, where we would expect that adding additional "significant" digits would in fact return a different number? I suppose it's possible if we get to really large or really small values where floating point numbers start to have resolution issues?
In your example it makes no difference. You can verify this like,
final double value1 = 2.0000000000D;
double value2 = 2.0;
System.out.println(value1 == value2);
Which outputs
true
It's also legal to use double value2 = 2.;
Uh, there is definitely some misunderstanding in that legacy code.
double value=2
works well.
It is pointless to set decimals to zero, and it is likely errorneous to set a double with decimals. For example,
double preciseNumber=3.1415926535897932384626433832795028;
is bogous:
the developer assumes that double does have an arbitrary decimal precision - but that is not the case
the developer assumes that the double will store the identical decimal number as intended - but that's not the case since it is stored in binary fractions instead of decimal fractions.
For precise calculations, consider using BigDecimal (and pass the initial value in String to ensure that no decimals get lost).
Related
I have written the following simple function that calculates the arctan of the inverse of an integer. I was wondering how to use BigDecimal instead of double to increase the accuracy of the results. I was also thinking of using a BigInteger to store the growing multiples of xSquare that the "term" value is divided by.
I have limited experience with the syntax for how to perform calculations on BigDecimals. How would I revise this function to use them?
/* Thanks to https://www.cygnus-software.com/misc/pidigits.htm for explaining the general calculation method
credited to John Machin.
*/
public static double atanInvInt(int x) {
// Returns the arc tangent of an inverse integer
/* Terminates once the remaining amount reaches zero or the denominator reaches 2101.
If the former happens, the accuracy should be determined by the number format used, such as double.
If the latter happens, the result should be off by at most one from the correct nearest value
in the seventh decimal place, if allowed by the accuracy of the number format used.
This likely only happens if the integer is 1.
*/
int xSquare = x*x;
double result = ((double)1)/x;
double term = ((double)1)/x;
int divisor = 1;
double midResult;
while ((term > 0)) {
term = term / xSquare;
divisor += 2;
midResult = result - term/divisor;
term = term /xSquare;
divisor += 2;
result = midResult + term/divisor;
if (divisor >= 2101) {
return ((result + midResult) / 2);
}
}
return result;
}
The BigDecimal provides very intuitive wrapper methods to provide all the different operations. you can have something like this to have an arbitrary precision of, for example, 99:
public static void main(String[] args) {
System.out.println(atanInvInt(5, 99));
// 0.197395559849880758370049765194790293447585103787852101517688940241033969978243785732697828037288045
}
public static BigDecimal atanInvInt(int x, int scale) {
BigDecimal one = new BigDecimal("1");
BigDecimal two = new BigDecimal("2");
BigDecimal xVal = new BigDecimal(x);
BigDecimal xSquare = xVal.multiply(xVal);
BigDecimal divisor = new BigDecimal(1);
BigDecimal result = one.divide(xVal, scale, RoundingMode.FLOOR);
BigDecimal term = one.divide(xVal, scale, RoundingMode.FLOOR);
BigDecimal midResult;
while (term.compareTo(new BigDecimal(0)) > 0) {
term = term.divide(xSquare, scale, RoundingMode.FLOOR);
divisor = divisor.add(two);
midResult = result.subtract(term.divide(divisor, scale, RoundingMode.FLOOR));
term = term.divide(xSquare, scale, RoundingMode.FLOOR);
divisor = divisor.add(two);
result = midResult.add(term.divide(divisor, scale, RoundingMode.FLOOR));
if (divisor.compareTo(new BigDecimal(2101)) >= 0) {
return result.add(midResult).divide(two, scale, RoundingMode.FLOOR);
}
}
return result;
}
For anyone who wanted to know why it was beneficial to pose this question to begin with: That is a fair question. I have written a rather long answer to it. I believe that writing this answer helped me to articulate to myself things about the BigDecimal class that are more intuitive now that I have Armando Carballo’s answer than they were before, so writing it was hopefully educational. I can only hope that reading it will be as well, though likely in a different way if at all.
The official documentation lists methods, but it doesn’t explain how they are used in the same way that Armando Carballo’s code demonstrates. For example, while the way the BigDecimal.divide method works is pretty intuitive, there is nothing in the official documentation that says “to take the mean of two numbers, not only should you have BigDecimals for those two numbers, but you should also create a BigDecimal equal to 2 and apply the BigDecimal.divide method to the result of a BigDecimal.add operation with the 2 BigDecimal as the input for the divisor.” This is something that is simple enough to be perfectly intuitive once you see it, but if you’ve never used object-oriented methods for the specific purpose of performing arithmetic before, it may be less intuitive the first time you are trying to figure out how to take the mean.
As another example, consider the idea that to figure out whether a number is greater than or equal to another number, instead of using a Boolean operator on the two numbers, you use a compareTo method that can give three possible outputs on one number with the other number as an input, then apply a Boolean operator to the output of that method. This makes perfect sense once you see it in action and have a quick sense of how the compareTo method works, but may be less obvious when you’re staring at a quick description of the compareTo method in the official documentation, even if the description is clear and you are able to figure out what the compareTo method will output with a given BigDecimal value calling the method and a given BigDecimal input as the comparison value. For anyone who has used compareTo methods with other classes besides BigDecimal extensively, this is probably obvious even if they’re new to the specific class, but if you haven’t used Booleans on the result of ANY compareTo method recently, it’s faster to see it.
When working with ints, you might very well write code a bit like this:
int x = 5;
x = x + 2;
System.out.println(x) // should be 7
Here, the “2” value was never declared to be an int. The result of the addition was the same as if we had declared y=2 and said that x = x+y instead of x = x+2, but with the above lines of code no named variable, or Integer object if we used those instead of primitive ints, was created for the “2”. With BigDecimal, on the other hand, since the BigDecimal.add method requires BigDecimals as inputs, it would be mandatory to create a BigDecimal equal to 2 in order to add 2. I don’t see anything in the official documentation that says “use this as a more accurate substitute for doubles, or for longs if you want something more versatile than BigInteger, but in addition to using it as a substitute for declared variables, also create BigDecimal objects equal to small integers that by themselves wouldn’t call for the use of the BigDecimal class so that you can use them in operations. Both your variables and the small values you are adding to them need to be BigDecimals if you want to use BigDecimals.”
Finally, let me explain something that has the potential to make the BigDecimal class more intimidating than it needs to be. Anyone who has ever worked with primitive arrays and tried to predict in advance at the time the array is created exactly how large it needs to be, or is familiar with how lower-level languages involve certain situations in which a programmer needs to know exactly how many bytes something takes up, may feel the need for caution when dealing with something that seems to demand a specified level of precision upfront. The documentation says this: “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.” A newbie reading that sentence for the first time may be thinking that they are going to have to think extensively about rounding when writing their code for the first time or else face exceptions as soon as a value cannot be represented exactly, or that they are going to have to read the documentation on the MathContext object as well before using BigDecimal, which in turn might lead to reading IEEE standards that help grant an understanding of floating point numbers but are far removed from what the person actually wanted to code. Seeing that some of the constructors for BigDecimal take arrays as inputs and that others take a MathContext as an input, along with noticing that one of the constructors for the related BigInteger class takes a byte array as the input, may strengthen the feeling that using this object class requires a very fine understanding of the exact number of digits that will be used for the specific calculations the class is used for and that understanding MathContext is more or less essential to even the most basic use of the class. While I’m sure understanding MathContext is helpful, baby’s first BigDecimal project can actually work perfectly well without the need to learn this added functionality at the same time as the first use of the BigDecimal. Reading up on the scale parameter might also lead to the belief by a coder looking up info on the class for the first time that it is necessary to predict the order of magnitude of the answer in advance in order to use the class at all.
Armando Caballo’s commendable answer shows that these concerns of a hypothetical newbie are overblown, as while rounding mode does need to be specified fairly often and a consistent scale is often called as a parameter when using the divide method, the scale parameter is actually a fairly arbitrary specification of the desired accuracy in terms of number of decimal places and not something that requires pinpoint predictions about exactly what numbers the class will handle (unless the ultimate purpose for which the BigDecimal is being used requires a finely controlled level of accuracy, in which case it is fairly easy to specify). An “infinite” series of added and subtracted terms to compute an arc tangent was processed without ever declaring a MathContext object.
I understand that due to the nature of a float/double one should not use them for precision important calculations. However, i'm a little confused on their limitations due to mixed answers on similar questions, whether or not floats and doubles will always be inaccurate regardless of significant digits or are only inaccurate up to the 16th digit.
I've ran a few examples in Java,
System.out.println(Double.parseDouble("999999.9999999999");
// this outputs correctly w/ 16 digits
System.out.println(Double.parseDouble("9.99999999999999");
// This also outputs correctly w/ 15 digits
System.out.println(Double.parseDouble("9.999999999999999");
// But this doesn't output correctly w/ 16 digits. Outputs 9.999999999999998
I can't find the link to another answer that stated that values like 1.98 and 2.02 would round down to 2.0 and therefore create inaccuracies but testing shows that the values are printed correctly. So my first question is whether or not floating/double values will always be inaccurate or is there a lower limit where you can be assured of precision.
My second question is in regards to using BigDecimal. I know that I should be using BigDecimal for precision important calculations. Therefore I should be using BigDecimal's methods for arithmetic and comparing. However, BigDecimal also includes a doubleValue() method which will convert the BigDecimal to a double. Would it be safe for me to do a comparison between double values that I know for sure have less than 16 digits? There will be no arithmetic done on them at all so the inherent values should not have changed.
For example, is it safe for me to do the following?
BigDecimal myDecimal = new BigDecimal("123.456");
BigDecimal myDecimal2 = new BigDecimal("234.567");
if (myDecimal.doubleValue() < myDecimal2.doubleValue()) System.out.println("myDecimal is smaller than myDecimal2");
Edit: After reading some of the responses to my own answer i've realized my understanding was incorrect and have deleted it. Here are some snippets from it that might help in the future.
"A double cannot hold 0.1 precisely. The closest representable value to 0.1 is 0.1000000000000000055511151231257827021181583404541015625. Java Double.toString only prints enough digits to uniquely identify the double, not the exact value." - Patricia Shanahan
Sources:
https://stackoverflow.com/a/5749978 - States that a double can hold up to 15 digits
I suggest you read this page:
https://en.wikipedia.org/wiki/Double-precision_floating-point_format
Once you've read and understood it, and perhaps converted several examples to their binary representations in the 64 bit floating point format, then you'll have a much better idea of what significant digits a Double can hold.
As a side note, (perhaps trivial) a nice and reliable way to store a known precision of value is to simply multiply it by the relevant factor and store as some integral type, which are completely precise.
For example:
double costInPounds = <something>; //e.g. 3.587
int costInPence = (int)(costInPounds * 100 + 0.5); //359
Plainly some precision can be lost, but if a required/desired precision is known, this can save a lot of bother with floating point values, and once this has been done, no precision can be lost by further manipulations.
The + 0.5 is to ensure that rounding works as expected. (int) takes the 'floor' of the provided double value, so adding 0.5 makes it round up and down as expected.
It is well documented that using a double can lead to inaccuracies and that BigDecimal guarantees accuracy so long as there are no doubles in the mix.
However, is accuracy guaranteed if the double in question is a small whole number?
For example, although the following will be inaccurate/unsafe:
BigDecimal bdDouble = new BigDecimal(0.1d); // 0.1000000000000000055511151231257827021181583404541015625
will the following always be accurate/safe?
BigDecimal bdDouble = new BigDecimal(1.0d); // 1
Is it safe to assume that small whole number doubles are safe to use with BigDecimals - if so, what is the smallest whole number that would introduce an inaccuracy?
>> Additional info in response to initial answers:
Thanks for the answers. Very helpful.
Just to add a little more detail, I have a legacy interface which supplies doubles, but I can be certain that these doubles will represent whole numbers having being themselves converted from Strings to doubles via Double.parseDouble(String) where the String is a guaranteed whole number representation.
I do not want to create a new interface which passes me Strings or BigDecimals if I can avoid it.
I can immediately convert the double to a BigDecimal on my side of the interface and make all internal calculations using BigDecimal calls, but I want to be sure that is as safe as creating a new BigDecimal/String interface.
Given that in my original example using 0.1d does not accurately result in 0.1, as shown by the fact that the actual BigDecimal is 0.1000000000000000055511151231257827021181583404541015625, it appears that some fractions will introduce an inaccuracy.
On the other hand, given that in my original example using 1.0d does accurately results in 1, it appears that whole numbers retain accuarcy. It appears that this is guaranteed up to a value of 2^53, if I understand your answers correctly.
Is that a correct assumption?
The BigDecimal aspect isn't as relevant to this question as "what is the range of integers that can be exactly represented in double?" - in that every finite double value can be represented exactly by BigDecimal, and that's the value you'll get if you call the BigDecimal(double) constructor. So you can be confident that if the value you wish to represent is an integer which is exactly representable by a double, if you pass that double to the BigDecimal constructor, you'll get a BigDecimal which exactly represents the same integer.
The significand of a double is 52 bits. Due to normalization, that means you should expect to be able to store integer values in the range [-253, 253] exactly. Those are pretty large numbers.
Of course, if you're only in the business of representing integers, it's questionable as to why you're using double at all... and you need to make sure that any conversions you're using from original source data to double aren't losing any information loss - but purely on the matter of "what range of integers are exactly representable as double values" I believe the above is correct...
A short answer is no. Because of the way a floating point variable is stored in memory there is no "small" value 0.000001 uses the same number of bits as 100000, every value is represented in the same way 0.xxx..eyy
A better way to initialize a BigDecimal is to initialize it with a string.
BigDecimal bdDouble = new BigDecimal("0.1");
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.
I am having a double variable:
private double b=0.0;
I am taking value of a certain field in b (the value is between 0.0 to 9.99999999). Mostly, user enters value between 0.0 to 1.0.
When I am saving the value from user in 'b', and if the value is 0.000001, then it is getting saved as 1E-6 and next time displayed as same on the screen (this is because b is Double).
Is there any way (just by calling some method) in Java, that I can avoid the conversion of value to 1E-6 format? I want to save the value as entered by user only (0.000001).
The double isn't actually "saved" as a string at all. That's just a matter of formatting - for which you should see DecimalFormat. However, it definitely won't be exactly 0.000001 because that number isn't exactly representable as a double. I would strongly recommend that you use BigDecimal instead when precise decimal values matter (as it sounds like they do here).
It's true that the number isn't stored in memory using the scientific (E) notation.
You can demonstrate this to yourself by formatting the number differently when you output it:
#Test
public void testLittle() {
double myNum = 0.000001;
System.out.printf("%f \n",myNum);
}
You should definitely heed other advice posted in answers here and use BigDecimal if you really want the output to match the input, since some floating-point values aren't what
you think they are when representing them in a computer. (ref: IEEE-754)
The "Java Puzzlers" presentations sometimes discuss these odd behaviors and are always a fun way to learn. (See this talk from Google I/O 2011, a bit after 3 minutes in)