1. Stange problem with sprintf or trace display

Hello gurus...
My app was adding up dollars and cents but did not balance.
A simplified example of why not is given below:

```-- euphoria v4.0.3 windows 2011-06-24 13:43:18
with type_check
with trace
with warning

procedure test()
atom a1, a2, a3, a4, a5, a6
sequence s1
trace(1)
a1 = 0.77
a2 = a1 * 100
printf(1,"\na2 is " & sprintf("%2d",{a2}) & "\n")
-- now the strange part
a3 = 66.77
a4 = floor(a3)
a5 = a3 - a4 -- 0.77, same as a1
a6 = a5 * 100 -- trace says this is 77
printf(1,"\na6 is " & sprintf("%2d",{a6}) & "!\n") -- 76!!
end procedure
test()

-- the problem exists whenever a6 > 01
```

This is whacky! any ideas?

2. Re: Stange problem with sprintf or trace display

AJ_Oxley said...

Hello gurus...
My app was adding up dollars and cents but did not balance.
A simplified example of why not is given below:

...

This is whacky! any ideas?

In general, you should not use floating point arithmetic for financial purposes. You're seeing rounding errors due to the fact that floating point numbers are stored in binary, and there are many decimal fractions that cannot be represented exactly.

Matt

3. Re: Stange problem with sprintf or trace display

A floating point/rounding problem with a 2 digit atom? Its nowhere near the 31 bit limit!
Strange indeed. Anyway -
If the data input from my user is characters in the format nnnn.cc ,multiple instances.
I have to seperate the cents from the dollars and convert to numeric to add this up.
Can you suggest how to achieve this and avoid the rounding problem?

4. Re: Stange problem with sprintf or trace display

AJ_Oxley said...

A floating point/rounding problem with a 2 digit atom? Its nowhere near the 31 bit limit!

It can probably get pretty close. Try, for instance, to precisely express 1/3 as a decimal. The radices are different, but the problem is the same.

AJ_Oxley said...

Strange indeed. Anyway -
If the data input from my user is characters in the format nnnn.cc ,multiple instances.
I have to seperate the cents from the dollars and convert to numeric to add this up.
Can you suggest how to achieve this and avoid the rounding problem?

The simplest is probably to convert to your smallest unit. In this case, track everything in terms of cents. Then you can convert / format that however you need. Alternatively, you could track dollars and cents separately, and manage the borrowing / carrying manually. Some systems use binary coded decimal (BCD). That's probably not a practical approach for euphoria code.

Matt

5. Re: Stange problem with sprintf or trace display

Thanks Matt,
The problem is that I am trying to save the data as characters, so will need to use value() and sprintf() quite a bit when fetching / displaying / accepting input.
In essence you are suggesting that a user may not enter numeric data as chars that contain a period, because of the rounding errors?
Possible bypass is to have seperate list controls designated for dollars and cents; the user would have to tab accross to the cents.
That won't make me popular!

6. Re: Stange problem with sprintf or trace display

AJ_Oxley said...

Thanks Matt,
The problem is that I am trying to save the data as characters, so will need to use value() and sprintf() quite a bit when fetching / displaying / accepting input.
In essence you are suggesting that a user may not enter numeric data as chars that contain a period, because of the rounding errors?
Possible bypass is to have seperate list controls designated for dollars and cents; the user would have to tab accross to the cents.
That won't make me popular!

Just split the number on the decimal and combine the two values:

```-- this is straight out of my head,
-- it may not work if actually run
function decimal_from_string( sequence str )

if find( '.', str ) then

sequence parts = split( str, '.' )
sequence val1 = value( str[1] )
sequence val2 = value( str[2] )

return val1[2] + (val2[2] / 100)

else

-- no decimal here
sequence val = value( str )

return val[2]

end if

end function
```

-Greg

7. Re: Stange problem with sprintf or trace display

I will experiment with mutiplying the floating point atom by 100 to get integer and then use divide and floor() .. it should work I hope !
Thanks for the help

8. Re: Stange problem with sprintf or trace display

AJ_Oxley said...

Thanks Matt,
The problem is that I am trying to save the data as characters, so will need to use value() and sprintf() quite a bit when fetching / displaying / accepting input.
In essence you are suggesting that a user may not enter numeric data as chars that contain a period, because of the rounding errors?
Possible bypass is to have seperate list controls designated for dollars and cents; the user would have to tab accross to the cents.
That won't make me popular!

If rounding errors are a problem, then you can't simply convert a fractional number into an atom. In general, monetary rounding errors are considered problematic.

I think the correct approach is to allow your users to do whatever they're doing, but to handle the data carefully inside your program so that you don't get any rounding errors. This should be completely transparent to a user. It makes things more difficult on you, of course, but there's really no way around this.

Databases tend to store things in different formats for this purpose. I know that in Java, a common way to implement money is to use the BigDecimal class, which makes arithmetic more difficult than "normal" numeric types, but has the bonus of avoiding floating point rounding errors.

I was somewhat surprised that a quick search in the archive didn't turn up any libraries that deal with money. I'm sure someone around here must have some code lying around.

Matt

9. Re: Stange problem with sprintf or trace display

AJ_Oxley said...

I will experiment with mutiplying the floating point atom by 100 to get integer and then use divide and floor() .. it should work I hope !
Thanks for the help

Ack! No no no! Don't divide and floor(). That's just a different way to recreate the same headaches you already have.

Matt

10. Re: Stange problem with sprintf or trace display

Thanks Greg,
I didn't think to handle the input as chars first before attempting arithmetic on the floating point.
That should work a treat!

11. Re: Stange problem with sprintf or trace display

mattlewis said...

I was somewhat surprised that a quick search in the archive didn't turn up any libraries that deal with money. I'm sure someone around here must have some code lying around.

Matt

There's a bignum or bigmath library that Lucius Hilley III wrote that can do arbiturary precision arthimetic.

The lack of a library specifically for money may be due to the ease of dealing with this issue simply by doing everything using integers and cents.