7

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

Community
  • 1
  • 1
Xtrinity
  • 125
  • 1
  • 5
  • The question is how much precision you actually need. Even with `BigDecimal` you don't get unlimited precision. – Axel Aug 08 '15 at 06:14
  • 1
    You need to be careful when talking about significant digits. It's about 15.9 significant digits *of an integral value.* if the number has a fraction you can in certain cases get what appears to be many more than that. For example 2^-20 can be represented as a double with perfect accuracy, because it's really only one set bit and an exponent within range. – user207421 Aug 08 '15 at 06:18
  • double is precise to 53 bits or log(2^53) ~ 15.95 digits. It's not a decimal type so you can't simply count in decimal digits. – phuclv Aug 08 '15 at 06:18
  • 1
    http://floating-point-gui.de/ –  Aug 08 '15 at 06:20
  • So in short, a double can hold up to 15 digits [not significant digits] precisely. Anything larger will result in rounding up with some special cases that are accurate? – Xtrinity Aug 08 '15 at 06:37
  • @Xtrinity Not in the least. Just removing the word 'significant' makes that statement less accurate, not more so. You need to read the references cited. – user207421 Aug 08 '15 at 07:38
  • 2
    http://stackoverflow.com/questions/588004/is-floating-point-math-broken might also be worth a read –  Aug 08 '15 at 13:06
  • [What Every Computer Scientist Should Know About Floating-Point Arithmetic](https://docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html) – phuclv Aug 11 '15 at 09:54

2 Answers2

1

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.

1

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.

Oly
  • 2,329
  • 1
  • 18
  • 23