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)
Related
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).
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");
This question already has answers here:
Why not use Double or Float to represent currency?
(16 answers)
Closed 8 years ago.
it's pretty clear why double & co. are not a good choice when it comes to handling currency.
I'm wondering though, since the issue only arises when calculations are performed on the value, am I correct by assuming that there is no problem at all to just store a currency value in a double?
For example:
1. Value gets loaded from any given source into a double
2. Value gets modified, directly typed in by the user.
3. Value gets stored to disk in a suitable format.
In the above example the double is just a way to store the value in memory, and thus shouldn't present any of the problems that arise if calculates are performed on the value.
Is this correct?
And, if correct, wouldn't it be better to use currency specific types, only when performing calculations?
Instead of loading 1000 BigDecimals from a database one could load 1000 doubles. Then, when necessary, define a BigDecimal, do the calculations and just keep the resulting double in memory.
No, it still causes problems, even if you don't perform any calculations. The problem is that the value you store might not be exactly representable as a double, so even if you just store it and read it back, you might not get back what you stored.
If you store 0.1 in a double, for instance, you'll find it's not actually 0.1 stored at all, because you can't represent 0.1 exactly in binary.
No. A double is never the correct type to store a currency value. Doubles are floating point values, that is, they are basically numbers of the form x * 2^y, where x and y are integers. Thus, some values, such as 0.10 (10 cents) have no exact representation as a double.
The problem here is that when you save say 10.12 in a double variable it may not have a exact double representation, so the java runtime will save it as a closest possible double representation, say 10.1199999999999 or 10.1200000000001 (just an example, I am not sure, have to test). So you get the point, as soon as you put the value in a double variable the currency value is approximated. However, that being said, you can still use a double for calculations and then use appropriate formatting while printing out the values or writing to file, such that the non-significant digits are hidden, depending on your application.
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 working on a program which needs to cube a number. Long story short, I need to compare the number to a string, so I need to get rid of the decimal place a double gives when I convert to string. To do this, I used Math.round and saved it as a long. This works fine for relatively normal numbers, but the numbers can go up to 999,999.
I used 275393 (a given test number, so I'm assuming it must be correct for the problem I'm working on) and neither a calculator nor the computer seemed to get the correct answer. The correct answer is supposed to contain 123457 somewhere in the results, but the calculator has 12346 (which I think is just rounding, as it stops listing numbers after this) and the computer has 123456 (the computer stops listing numbers after this point). Is rounding it giving it the problem (it shouldn't because I'm pretty sure it only rounds to the tenths place, but who knows)? Or is it something else?
Math.pow() takes two doubles and returns a double. There is not enough precision in a double to represent 2753933 = 20886164356123457 (exact).
The solution is to use BigInteger.pow().
A double has limited precision. Instead, use BigInteger or BigDecimal for your calculation.
I need to compare the number to a string, so I need to get rid of the decimal place a double gives when I convert to string.
Do it the other way around. Convert the String to a number, then compare it to the double (with a small tolerance to account for inaccurate binary representations of float point numbers).