Matthew,
You are absolutely right, we have to do something about handling money better before anyone else
notices that 0.7 plus 0.1 is not 0.8 with php! (I've already had an e-commerce user notice that
their account balance is misquoted because 82 - 2 became 79 because of this).
Looking through this thread it seems :
1. Floating points are not accurate, floating point arithmetic is not accurate enough for money
calculations.
2. Zeev is worried that a string based type would create performance problems.
3. The standard approach when programming in low-level languages such as C or Java, is to convert to
"pennies" and rely on integer arithmetic.
4. bc calculations/comparisons are safe.
5. Something funny is already happening in php with decimals i.e. print 1 * 0.1 is always 0.1 and
never 0.10000001 or whatever else has the same binary representation.
My questions are:
1. Is it useful to cut down the "money" problem to the bare esentials? e.g.
- only handle addition/subtraction/multiplication
- never worry about more than 2 decimal places
- maximum values could be practically limited to a billion or so
2. Could we use integer arithmetic for money, given the above restrictions? We only have to spot a
"money" field and carry out integer arithmetic rather than floating point arithmetic. Of course we
need to multiply the operands by 100 before the addition and divide by 100 at the end. Would that
bring back the floating point problem in a new guise?
3. How does mysql get away with it? I've never had a smidgeon of trouble with mysql adding
decimals. In fact, my "safe" fall back for my bug will be to get mysql to do all the arithmetic
rather than php.
4. How bad could performance get? How much non-integer, non-money, floating point calculation is
really done in php code? Apart from some gd stuff, I can't think when I ever actually do a
non-integer, non-money calculation!
5. If actual overall impact on php applications is not too bad, (even if we slow floating point by a
factor of ten), could we just use bc routines for all floating point? Perhaps it could be a
configuration option, with the default as precise calculation i.e. use bc, and an option for "fast"
i.e. non-bc calculation?
Let me say again, if we want to be treated seriously as a credible HIGH LEVEL language, let alone a
reliable e-commerce platform it is NOT ACCEPTABLE that (int) 10 * 0.7 + 0.1 is 7. It doesn't matter
how much documentation we create, it's just not on as default behaviour. So we have to do something!
The alternative is that sooner or later we'll be reading a press release from Seattle that runs
something like this...
PHP dangerous for e-commerce
Reports are coming in of serious failings at e-commerce sites using php. These result from
fundamental weaknesses in php's basic arithmetic on monetary figures. Accounting integrity is
acknowledged to be completely compromised if developers rely on default php arithmetic. Even
calculations such as 8.20 - 0.20 are not dependable. The problems are particularly dangerous
because they are intermittent. Php's development team have been aware of this for over a year and
despite their much vaunted "open source rapid response", there is no fix, nor even any intention of
a fix to this problem. Instead they choose to highlight weaknesses in their underlying C platform.
Bigsoft CEO, Robert Doorways comments : This totally vindicates our view that Open Source
technologies must not be relied upon for professional corporate applications. Only the most
experienced and highly resourced software suppliers have the understanding and capability to deliver
to the corporate market. We fully sympathise with customers who have been taken in by the
exaggerated claims of the Open Source movement. We have set up a special php translation facilities
for everyone who wants to convert their php applications to a more stable language. At BigSoft we
have been counting our own and especially other people's money from our earliest days, and are
specialists in big money calculations...
George
Matthew Hagerty wrote:
At 12:09 PM 12/5/2001 +0200, Zeev Suraski wrote:
At 00:38 05/12/2001, Matthew Hagerty wrote:
Okay, then why when I ask PHP to show me the value of $fFloat1, do I get
this:
printf("%f", $fFloat1); -> 63.670000
Not some long draw out number like 63.67000000001 or even
63.66999999999... If PHP uses precision past the 6 digit then it needs
to show me that it does.
I can't agree with your reason because computers are not that imprecise.
They are. Generally, the equality operator on floats is inaccurate by
definition, you learn that in Numeric Analysis 101 :) Comparing floating
point numbers should only be done by comparing ranges. E.g.,
something like
abs($num1-$num2)<0.00001
Yes, I'm finding this out... It was my own ignorance that let me shoot my
mouth off. I should know better, but frustration set in early.
<snip>
However, even if the number is not stored as indicated, ie. 3.55000000001
instead of a clean 3.550000000, then why does PHP take the liberty to
chop off that precision when converting to a string? And why is that
precision not put back on when going back to a double? It is not put
back on because PHP can represent 3.55 as a clean 3.5500000000, so the
assignment of floats, ie. $fFloat = 3.55;, is coded in error in PHP's
internals!?
There's no clean 3.55, there simply isn't. It's just how computers
work. The only way to do what you're asking for is to switch to a whole
different string-based representation, which is going to slow things down
very considerably.
My tests in C revealed that with floats (4 byte floats) the math works, I
guess the precision can be kept under control, but when using doubles I get
the same results as I got with the PHP code.
Why was the decision made to have all "float" types in PHP set to
doubles? Doubles are good for really big numbers, but floats come in handy
at times too (like this) where the added precision becomes more of a
hindrance. Any chance in getting both 4-byte floats and 8-byte doubles
into PHP?
The issue that kicked this whole thing off came about because I have to
deal with money values. PHP only has integers and doubles for dealing with
numbers, not counting boolean (unless I missed one), so you can't really
reliably represent, say $3.55. Money math is a tricky beast and values
need to be kept under control. It would be nice to have a fixed precision
type similar to SQL's numeric or number types. I can get by with bcmath
for now, but a built-in type that takes care of this kind of thing
automatically would be really nice!
mbers%22
Zeev
Thanks for the education, sorry about all the fussing!
Matthew
--
PHP Development Mailing List <
http://www.php.net/>
To unsubscribe, e-mail: php-dev-unsubscribe@lists.php.net
For additional commands, e-mail: php-dev-help@lists.php.net
To contact the list administrators, e-mail: php-list-admin@lists.php.net