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?

    » topic index » view message » categorize

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

    » goto parent     » topic index » view message » categorize

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?

    » goto parent     » topic index » view message » categorize

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

    » goto parent     » topic index » view message » categorize

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!

    » goto parent     » topic index » view message » categorize

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

    » goto parent     » topic index » view message » categorize

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

    » goto parent     » topic index » view message » categorize

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

    » goto parent     » topic index » view message » categorize

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

    » goto parent     » topic index » view message » categorize

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!

    » goto parent     » topic index » view message » categorize

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.

    » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu