In order to put decimal-separators in the right place, the money module is calculating numbers with *100 and /100. Unfortunatly PHP seems to be somehow imprecise in calculation. Therefore we get small (repeatable) errors - like somebody gives a number with ,92 - and after saving it is ,91.

In order to keep exact numbers i recommend not to calculate, but to work with replacing options. For me, this was the solution:

 $converted_amount = str_replace(array($decimal_separator, $digit_group_separator), array('', ''),  $item['amount']);

            // Now convert the amount to make it storable as an integer.
            // We are always working with a maximum of 2 decimals, this means
            // that one unit in the database corresponds to 1/100th of a unit
            // in reality (i.e. in forms and on display).
            $items[$delta]['amount'] = $converted_amount;

So i just replace ',' with '' - no need to first replace with '.' and than multiply again times 100.

It was enough for me to keep the numbers exact - but in order to bee 100% sure it would be good also to replace the /100 with some replacing function.

regards
Christian

Comments

wim leers’s picture

In my tests, PHP couldn't multiply numbers written with a comma as decimal separator properly. Although what you're doing gives us the same effect, of course.

But I've been unable to reproduce the imprecise calculations you're talking about.

chriszz’s picture

Well - i had those imprecisions - do not know, where they coming from - but money-issues are somehow sensible ;-)

I mean - Not multiply anything - because after just replacing the "," with "" there is no need to multiply again. Therefore no calculation is done - which is more secure as php is not developed for calculating money.

regards

Christian

wim leers’s picture

more secure as php is not developed for calculating money.

Nothing advanced is happening here... And I'd appreciate it if you could give me a reproducible example. Finally, what version of PHP are you using? Maybe it's a bug in that specific version.

wim leers’s picture

Title: calculating number with *100 and /100 gives small imprecisions » Decimal to integer conversion results in imprecisions
Priority: Critical » Normal

Reset to normal, because I can't reproduce this. As soon as I continue development, I'll change it to the way you suggested.

emdalton’s picture

Priority: Normal » Critical

I'm getting the same precision problem. I have PHP 5.1.6 on linux. When one of my users enters a number like USD 16.40, the system saves "16.39" instead and all attempts to fix it fail. However, I can enter "164.00" and all is fine. My users are pretty upset. I can probably arrange for you to access our system if that would help.

wim leers’s picture

Priority: Critical » Minor
Status: Active » Closed (won't fix)

@chriszz: while applying your suggested changes, I found a problem with it. Suppose you've set your decimal separator to the comma. If you then enter 83,0, your change will result in this being stored as 830 instead of 8300 in the database. When you'd display it then, you would see 8,30. So I chose to not apply your suggested change.

@emdalton: all I can say is: upgrade to the next version, or provide a working bugfix. I will not spend time searching for the cause. I'm running PHP 5.2.3 on my local server, and I can't reproduce it there. Also marking this as minor, since it only happens on certain versions of PHP.

emdalton’s picture

When you say "upgrade to the latest version," I guess you mean the latest version of PHP. I don't have control over which version of PHP is being used by my server administrator, nor do I have any guarantee that changing the version of PHP would fix this, since no cause has been found.

I appreciate that this is a free module, and your support is a volunteer effort. But you have two separate users reporting imprecision in a module designed to handle money, which is an area in which most people need precision. I'm a little surprised you're not more concerned. I guess I should either look for a different module to handle this field, or migrate my users off of Drupal to a commercial product. Since this is the second time we've had precision problems in Drupal/CCK, they're starting to lose patience. :(

wim leers’s picture

Title: Decimal to integer conversion results in imprecisions » Float to integer conversion results in imprecisions
Assigned: Unassigned » wim leers
Priority: Minor » Critical
Status: Closed (won't fix) » Fixed

I was able to reproduce this after all, unfortunately: http://drupalbin.com/958. I only mark issues as won't fix if they're either insane or completely unreproducible.

It's fixed now ;) :)

emdalton’s picture

I'm very happy to hear it. My users will be happy, too. :) How do I get the fix? (And the fix doesn't just add 0.01 indiscriminately, does it?)

wim leers’s picture

This is how it works:

1) you enter 16,40
2) the number gets multiplied by 100, this results in 1640
3) we want to save this number to the DB, but this is a float, we have to convert it to an integer first. However, casting it to an integer gives us 1639 (i.e. the problem described by you). To get 1640, I add 0.01, which will be casted away anyway: 1640 == (int) 1640.01

You can get the fix by waiting for the tarball to regenerate (once every couple of hours), or get it via CVS, or wait until the next official release.

emdalton’s picture

Sorry, is the tarball you're referring to the -dev release? Thanks!

wim leers’s picture

Yep :)

emdalton’s picture

Status: Fixed » Active

Bad news. I updated to the -dev version this morning and accessed a test record to see if I could set the value to $16.40 instead of $16.39. The value was showing as $16. Attempts to change the value still displayed $16 (both in viewing the record, when editing the record, and in Views.)

I reinstalled money-5.x-1.3 and things returned to normal... and the value now displays $16.40 (in that test record only).

So I think the fix for precision is working, but something else seems messed up with display of values.

wim leers’s picture

Version: 5.x-1.3 » 5.x-1.4

Ugh! You're right. I added many new features and fixed several more bugs after fixing this one, so I guess I broke it somewhere.

Should be fixed soon.

wim leers’s picture

Status: Active » Fixed

Ok, it's not a bug, it's a new feature. Actually it's a borked upgrade path.

Go to the settings of the money field. There's a new setting there: "Displayed decimals:". I guess this is defaulting to 0, while it should be defaulting to 2. Will fix that.

wim leers’s picture

Please DON'T set it to 2 just yet. Please download the tarball, install it and see if it's now defaulting to 2 automatically.

emdalton’s picture

Version: 5.x-1.4 » 5.x-1.3
Status: Fixed » Active

I don't think the tarball has been refreshed yet. I'll test this tomorrow AM (US Eastern time). Thanks for all your efforts on this!

Also, just want to check -- did the patch for adding filtering by amount to Views get into this version?

chriszz’s picture

Wow - thank you for fixing this.

wim leers’s picture

emdalton: check the changelog of version 1.4.

emdalton’s picture

Seems to be working correctly now! I've installed it on production, with crossed fingers. I'll let you know if anything unfortunate crops up. Thanks again!

wim leers’s picture

Excellent. I'll tag a new release then.

wim leers’s picture

Status: Active » Fixed
emdalton’s picture

Status: Fixed » Active

More bad news. My users report: "The log is still changing the amounts. [The value in one record] should be $20.00 but it keeps changing it. It changed it to $19.98 first, then $19.99, then $19.97."

It turns out that for this application, my users don't need to manipulate the value numerically, so I'm going to convert this field to a text field. However, I have a project coming up in the near term that will require accurate handling of money values, so I still need a fix for this bug.

wim leers’s picture

Status: Active » Postponed (maintainer needs more info)

I'm afraid I can't reproduce this… You'll have to provide me with *exact* steps to reproduce this problem.

emdalton’s picture

Sorry, I've been out of the office several days. In fact, I can't reproduce (or even see) the problem my users reported last week, but since they usually don't complain unless they actually see something, I'm keeping my eyes open for another example of whatever they were seeing.

Meanwhile, my users just gave me another example, which I was able to reproduce. I've created a test entry in which I add a value of -25.00. After save, the amount displays as -24.99. Most likely you just need to check to see if the value is negative, and if so, subtract the .01 rather than add.

wim leers’s picture

Status: Postponed (maintainer needs more info) » Active

I'm subtracting 0.1 from the 2500, not from the 25.00… so it should be rounded away. Will try to reproduce when I find the time.

emdalton’s picture

Status: Active » Postponed (maintainer needs more info)

Right, well it should have rounded correctly in the first place (not your fault Python doesn't). Anyway, look at how you're treating values less than 0 to see if you reversed the correction-- I think you may need to do that.

drenton’s picture

We were having the same problem with negative numbers.

After changing

$converted_amount = (int) ($converted_amount + 0.01);

to

$converted_amount = ($converted_amount < 0) ? ((int) ($converted_amount - 0.01)) : ((int) ($converted_amount + 0.01));

it seems to be working fine now.

wim leers’s picture

Status: Postponed (maintainer needs more info) » Needs review

emdalton: could you please confirm that this change fixes it?

wim leers’s picture

Title: Float to integer conversion results in imprecisions » Float to integer conversion results in imprecisions with negative numbers
Status: Needs review » Fixed

Oops, didn't read your follow-up properly, drenton. I committed your change: http://drupal.org/cvs?commit=153608. Thanks! :)

emdalton: I'm going to assume it was your PHP that was at fault, since it works for everybody else.

markus_petrux’s picture

Status: Fixed » Closed (fixed)