Kevin Tysen

Ranch Hand

Posts: 255

posted 8 years ago

I used the class BigDecimal and used the field ROUNDHALFUP in order to round up a number, but it didn't round up.

More specifically, the number was the double obtained by dividing 1942 by 40. If you divide 1942 by 40 on paper, you get 48.55 exactly, so I figured that using ROUNDHALFUP would give me 48.6 but instead I got 48.5.

I thought about it a bit, and I assume that dividing 1942.0 by 40.0 would give a double very close to 48.55, but not exactly because the numbers are all binary instead of base 10. Also, although 48.5 would be exact, 48.6 would not be exact.

What I used to do to round to the nearest tenth was like this:

double a;

double b;

double d = 10.0 * a/b + 0.5;

int result = (int) d;

// then change result into a string, stick a period in just before the last digit on the right, and add a zero on the left if necessary

You know what I mean, I think. But that takes up a lot of space in a program. Using BigDecimal saved space, but I got a result I did not want. There must be some simple way to round accurately to the nearest tenth. Anyone know what it is?

More specifically, the number was the double obtained by dividing 1942 by 40. If you divide 1942 by 40 on paper, you get 48.55 exactly, so I figured that using ROUNDHALFUP would give me 48.6 but instead I got 48.5.

I thought about it a bit, and I assume that dividing 1942.0 by 40.0 would give a double very close to 48.55, but not exactly because the numbers are all binary instead of base 10. Also, although 48.5 would be exact, 48.6 would not be exact.

What I used to do to round to the nearest tenth was like this:

double a;

double b;

double d = 10.0 * a/b + 0.5;

int result = (int) d;

// then change result into a string, stick a period in just before the last digit on the right, and add a zero on the left if necessary

You know what I mean, I think. But that takes up a lot of space in a program. Using BigDecimal saved space, but I got a result I did not want. There must be some simple way to round accurately to the nearest tenth. Anyone know what it is?

Campbell Ritchie

Marshal

Posts: 56536

172

posted 8 years ago

Are you using floating-point arithmetic? If you pass a double to the constructor of a BigDecimal you can permanently immortalise any arithmetical errors.

So you would actually round to 48.5, not 48.6.campbell@linux-pgix:~/java> java RoundDemo 1942 40

48.5499999999999971578290569595992565155029296875

campbell@linux-pgix:~/java> java RoundDemo 1942.0 40.0

48.5499999999999971578290569595992565155029296875

campbell@linux-pgix:~/java>

Kevin Tysen

Ranch Hand

Posts: 255

posted 8 years ago

This is the actual line in my program:

writer.write((new BigDecimal(tts / nw)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());

And tts and nw are both doubles.

What do you mean by floating point arithmetic? Is that a float instead of a double?

What do you mean by "immortalize"?

writer.write((new BigDecimal(tts / nw)).setScale(2, BigDecimal.ROUND_HALF_UP).toString());

And tts and nw are both doubles.

What do you mean by floating point arithmetic? Is that a float instead of a double?

What do you mean by "immortalize"?

Campbell Ritchie

Marshal

Posts: 56536

172

posted 8 years ago

Both float and double use floating-point arithmetic, which is inherently imprecise (look at these FAQs (no 20) and links). So when you divide 1942.0 by 40.0 you don't get 48.55, but something slightly less, in fact 48.5499999999999971578290569595992565155029296875.

If you pass that double to the BigDecimal constructor, you make the imprecision immortal, make it permanent, preserve it for ever, maintain it until the application terminates, can't get rid of it. So the BigDecimal will have the value of 48.5499999999999971578290569595992565155029296875 not 48.55.

TryThat should come out as 48.55, and should round to 48.6.

And instead of new BigDecimal(123.456) which can be imprecise, write new BigDecimal("123.456"). The String representation of the number will be interpreted precisely as 123.456.

It says in the The API documentation that a BigDecimal stores its values as whole numbers, so its arithmetic can be precise, just as integer arithmetic is precise. Note what it says in the divide method about Exceptions and precision.

If you pass that double to the BigDecimal constructor, you make the imprecision immortal, make it permanent, preserve it for ever, maintain it until the application terminates, can't get rid of it. So the BigDecimal will have the value of 48.5499999999999971578290569595992565155029296875 not 48.55.

TryThat should come out as 48.55, and should round to 48.6.

And instead of new BigDecimal(123.456) which can be imprecise, write new BigDecimal("123.456"). The String representation of the number will be interpreted precisely as 123.456.

It says in the The API documentation that a BigDecimal stores its values as whole numbers, so its arithmetic can be precise, just as integer arithmetic is precise. Note what it says in the divide method about Exceptions and precision.

Kevin Tysen

Ranch Hand

Posts: 255