473,881 Members | 1,517 Online

# Re: Rounding error when converting from double to int

Victor said:
Also, consider that *all calculations* should be performed in *cents* and not in
dollars. You should never have to "round" anything like '1.115'.
The double value that I'm trying to convert to GCSMoney (which is
implemented as cents) was produced by multiplying a dollar amount by
an interest rate to get interest.

double amount = 126.60;
double interestRate = .075;
double interest = amount * interestRate;
int interestAsCents = interest * 100.0 + 0.5;

The debugger says interest = 9.495.
But when I convert it to cents, we see that interest really wasn't
9.495 since the cents rounds to 949 instead of 950.

Below is code where I do my calculations as cents. I can convert
amount to cents by multiplying by 100 since I'm dealing with a dollar
amount. But interest rate is not a dollar amount. I can't convert it
to cents. In this particular case, I can convert it to an integer by
multiplying by 1000 since it has three decimal places.

double amount = 126.60;
double interestRate = .075;
int amountAsCents = amount * 100; // = 12660
int interestRateAsI nt = interestRate * 1000; // = 75
int interestTemp = amountAsCents * interestRateAsI nt; // = 949500
interestTemp += 500; // round it. = 950000
int interestAsCents = interestTemp / 1000; // undo interestRate
* 10000. = 950

So this works. But what if I have an interest rate that has more
decimal places? For example, 0.1234? Then I would have to convert
it to an int by multiplying by 10000 and later divide by 10000.

In general, if I had a money class that stored money amounts as cents
and I needed a multiplication operator that could multiply a money
type by a double, how would I implement that? Would I just
arbitrarily choose the amount of double precision like this?:

Clint
int precision = 3;
double amount = 126.60;
double interestRate = .075;
int amountAsCents = amount * 100; // = 12660
int interestRateAsI nt = interestRate * pow( 10.0, precision ); //
= 75
int interestTemp = amountAsCents * interestRateAsI nt; // = 949500
interestTemp += 5 * pow(10.0, precision - 1 ); // round it. = 950000
int interestAsCents = interestTemp / pow(10.0, precision); //
undo interestRate * 10000. = 950

Or instead of hardcoding the precision in the code above, could I
detect how many decimal places there are and use that as my precision?

Jun 27 '08 #1
2 5139
On May 27, 11:57 pm, clintonb <cba...@century tel.netwrote:
Victor said:
Also, consider that *all calculations* should be performed
in *cents* and not in dollars. You should never have to
"round" anything like '1.115'.
The double value that I'm trying to convert to GCSMoney (which is
implemented as cents) was produced by multiplying a dollar amount by
an interest rate to get interest.
double amount = 126.60;
double interestRate = .075;
double interest = amount * interestRate;
int interestAsCents = interest * 100.0 + 0.5;
The debugger says interest = 9.495.
But when I convert it to cents, we see that interest really
wasn't 9.495 since the cents rounds to 949 instead of 950.
Well, the first error in the above: interestRate is NOT .075,
because .075 can't be represented as a double. Since you start
with an incorrect value, obviously, all of the remaining values
will be incorrect as well.
Below is code where I do my calculations as cents. I can
convert amount to cents by multiplying by 100 since I'm
dealing with a dollar amount. But interest rate is not a
dollar amount. I can't convert it to cents.
No, but it has been specified as an exact decimal fraction, so
you must use some representation which preserves its exact
value, and double isn't it.

(The actually depends on what you're using the monetary values
for. But if it's any bookkeeping, the law generally says
exactly how you have to round, and it it almost always specifies
the rules in terms of decimal values.)
In this particular case, I can convert it to an integer by
multiplying by 1000 since it has three decimal places.
For example. More generally, regardless of the number of
decimal places, you can convert by multiplying by a power of
ten.

This is usually handled with some sort of class, which stores
the integral value and the number of digits after the decimal.
The values are determined directly when converting the input.
double amount = 126.60;
double interestRate = .075;
int amountAsCents = amount * 100; // = 12660
int interestRateAsI nt = interestRate * 1000; // = 75
int interestTemp = amountAsCents * interestRateAsI nt; // = 949500
interestTemp += 500; // round it. = 950000
int interestAsCents = interestTemp / 1000; // undo interestRate
* 10000. = 950
So this works. But what if I have an interest rate that has
more decimal places? For example, 0.1234? Then I would have
to convert it to an int by multiplying by 10000 and later
divide by 10000.
Given something like:

class Decimal
{
// ...
long long value ;
int scalingFactor ;
} ;

you multiply by multiplying the value, and adding teh
scalingFactor.

In addition to the usual arithmetic operations, you probably
want some functions to round to a specific position, etc.
In general, if I had a money class that stored money amounts
as cents and I needed a multiplication operator that could
multiply a money type by a double, how would I implement that?
Would I just arbitrarily choose the amount of double precision
like this?:
No. Not arbitrarily, but from the input. If the input is 1.23,
then value is 123, and scalingFactor 2. If it is 0.12345, then
value is 12345, and scalingFactor 5.
Clint
int precision = 3;
double amount = 126.60;
double interestRate = .075;
int amountAsCents = amount * 100; // = 12660
int interestRateAsI nt = interestRate * pow( 10.0, precision ); //
= 75
int interestTemp = amountAsCents * interestRateAsI nt; // = 949500
interestTemp += 5 * pow(10.0, precision - 1 ); // round it. = 950000
int interestAsCents = interestTemp / pow(10.0, precision); //
undo interestRate * 10000. = 950
Or instead of hardcoding the precision in the code above,
could I detect how many decimal places there are and use that
as my precision?
Exactly. And since the only powers you need are those of 10
(and you can probably restrict those, e.g. by restricting the
scalingFactor to +/- 13, like Cobol does), you can easily put
those in a table. (Don't forget that pow(10.0, precision) will
return a possibly inexact double.)

Better yet, I'm sure that there are packages on the market which
do this already. Use one of them.

--
James Kanze (GABI Software) email:ja******* **@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientier ter Datenverarbeitu ng
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Jun 27 '08 #2
On 2008-05-27 23:57, clintonb wrote:
Victor said:
>Also, consider that *all calculations* should be performed in *cents* and not in
dollars. You should never have to "round" anything like '1.115'.

The double value that I'm trying to convert to GCSMoney (which is
implemented as cents) was produced by multiplying a dollar amount by
an interest rate to get interest.

You should perform the calculations with integers too, as soon as you
use floating point values you run the risk of getting problems. You
should consider creating (or getting from somewhere) a Decimal-class
which can represent decimal numbers and perform calculations with them.

--
Erik WikstrÃ¶m
Jun 27 '08 #3

This thread has been closed and replies have been disabled. Please start a new discussion.