1

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.

Nivas
  • 18,126
  • 4
  • 62
  • 76
Jayanga Kaushalya
  • 2,674
  • 5
  • 38
  • 58

5 Answers5

4

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

  1. Let prices etc be represented by BigDecimal which allows you to store numbers as 0.1 accurately, or
  2. 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.

aioobe
  • 413,195
  • 112
  • 811
  • 826
3

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.

Michael Borgwardt
  • 342,105
  • 78
  • 482
  • 720
  • Can't see how using `BigDecimal` will help, if `f_num` is a double to start with. – aioobe Aug 18 '11 at 11:07
  • I just want the first two decimal places. Yes you right this is for calculating money values. I just used the Math.round() method and it works. Thanks....... – Jayanga Kaushalya Aug 18 '11 at 11:08
  • If you're working with prices etc, I'd suggest you store the number of cents in an `int` instead. Otherwise you'll run into inaccuracies such as this everywhere... – aioobe Aug 18 '11 at 11:09
1
int f_part = (int) Math.round(((f_num - num) * 100));
0

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.

JB Nizet
  • 678,734
  • 91
  • 1,224
  • 1,255
0

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.

Community
  • 1
  • 1
Nivas
  • 18,126
  • 4
  • 62
  • 76