|
Posted by "Richard Lynch" on 07/22/05 10:59
On Tue, July 19, 2005 10:26 am, John Nichel said:
> There's some freaky math going on there or something. I added a couple
> of other echos in to see and for some reason it seems to be losing
> single digit value (subtracting, rounding down, I don't know).
>
> $calculatedGross = $originalNet + ( $originalNet * $commissionPct * 0.01
> );
>
> echo ( "Gross : " . (int)$calculatedGross ." = ". $originalNet ." + ( ".
> $originalNet ." * ". $commissionPct ." *.01 )<br />\n" );
>
> $calculatedNet = $calculatedGross / ( 1 + ( $commissionPct * 0.01 ));
>
> echo ( "Net : " . (int)$calculatedNet." = " . (int)$calculatedGross . "
> / ( 1 + ( " . $commissionPct . " * .01 ) )<br />\n" );
Floating point mathematics is inherently "unreliable" in computers.
Consider, for example, 1/3 in decimal notation:
1.33333333333333333333333333333333333333333333333333...
PHP can only write so many digits down in a 32-bit floating point
representation, just as you can only write down so many digits before your
hand cramps up or you run out of paper or you get lazy and put "..." on
the end. :-)
Now, the thing to remember is, the problem won't appear where you expect
it to from your experience with DECIMAL numbers all your life because
floating point numbers are being stored in a binary/hexidecimal notation.
If you are feeling particularly masochistic, you can Google for "floating
point internal representation mantissa exponent" and you're gonna find out
EXACTLY how those numbers get stored. Set aside a good chunk of time to
absorb it all... :-)
Even something as simple as:
<?php
$foo = (float) 2;
echo $foo;
?>
could, in theory, print out something like:
1.9999999999999999
and be "correct" as far as Computer Science is concerned.
If you need PRECISE mathematics, you should use integer and count pennies
instead of dollars -- or even 1/10th of pennies or whatever resolution you
need.
Of course, every step you take on the right, you can only store 1/10th as
large a number of dollars as your maximum amount. But you are starting
with 2 billion as your max, so that's a lot of room to play with.
If you *need* float numbers and precision out to decimal point N, look
into BC_MATH and that new-fangled Math package PHP has for this very
reason. You'll still be dealing with this issue, but you'll know your
numbers are accurate to the Nth decimal point, and it will be painfully
obvious in your code that it is so. [It will also be SLOWER than just
plain float numbers.]
If you just don't care about the damn penny, but want your code to work,
something like:
if (1.00 <= $x && $x <= 1.01){
// $x == 1, really
}
may suffice for your everyday work-a-day computing needs...
Though that can also turn into a lot more problems than you think, down
the line...
Remember 2nd Grade when long division was "tricky"? Yeah, well, computers
are a hell of a lot more stupid than 2nd Graders, any day of the week.
Note that there *ARE* computer languages where are prefectly happy to work
in fractions (Lisp is one such language) because somebody has programmed
the algebraic rules of fraction arithmetic into the language:
(let ((x 1/3)
(y 2/3))
(print (+ x y))
will cheefully and with 100% accuracy print out "1" every single time.
[Apologies to Guy Steele if my 10-years-unused Lisp syntax is not spot on.]
Lisp stores fractions as 2 integers, not as floating point numbers.
[Though of course you can ask it to convert and round off if you like.]
You could, if you so desired, whip up a PHP "class fraction {" code-base,
or even just store fractions as 2-element arrays (numerator, denominator)
and write some procedural functions to work with them.
It's not exactly Rocket Science to do that.
--
Like Music?
http://l-i-e.com/artists.htm
Navigation:
[Reply to this message]
|