Missing 0.0000001 - driving me crazy - help!

Do you have a question? Post it now! No Registration Necessary.  Now with pictures!

•  Subject
• Author
• Posted on
Hi all,

This is weird and it is driving me nuts, please can someone explain how
I can get it to return the correct answer:

print '\$_SESSION["a"]: '.\$_SESSION["a"].'<br />';
print '\$_SESSION["c"]: '.\$_SESSION["c"].'<br />';
print \$_SESSION["c"]." - ".\$_SESSION["a"].' =
'.(\$_SESSION["c"]-\$_SESSION["a"]);

----------------- RETURNS -----------------------

\$_SESSION["allowance"]: 200
\$_SESSION["cart_value"]: 200.15
200.15 - 200 = 0.14999999999998

When I test on my windows box it is correct, but not when I test on my
Unix server.

Your help would be *greatly* appreciated.

mat

Re: Missing 0.0000001 - driving me crazy - help!

Variables are held to s certain degree of precision. 200.15 cannot be
held accurately as a normal binary number, for example, so errors can
occur.

Use bcsub(200.15,200,n) where n is the number of decimal places you
want.

Ian

Re: Missing 0.0000001 - driving me crazy - help!

Any attempt to round to "a number of decimal places" will virtually
guarantee roundoff error.  There are very few non-integer decimal
numbers which can be represented exactly in binary floating
point.  All of them end in 5 (plus trailing zeroes).

Gordon L. Burditt

Re: Missing 0.0000001 - driving me crazy - help!

Gordon Burditt wrote:

bcsub doesn't round

Re: Missing 0.0000001 - driving me crazy - help!

"mat" wrote:

Your Windows box is just rounding the number up for you.

0.15 is an infinitely long number in binary [1], just like 3/10 is equal to
0.3333333333... in decimal notation

Try using sprintf [2] to tidy up your results.

[1] 0.00100110011001100110011001100...
[2] http://php.net/sprintf

--
phil [dot] ronan @ virgin [dot] net
http://vzone.virgin.net/phil.ronan/

Re: Missing 0.0000001 - driving me crazy - help!

"Philip Ronan" wrote:

Oops, "1/3" not "3/10"

--
phil [dot] ronan @ virgin [dot] net
http://vzone.virgin.net/phil.ronan/

Re: Missing 0.0000001 - driving me crazy - help!

Thanks Philip and Ian, a real help.

Working on what Philip said about Windows just rounding, I did the same
and it works for me:

round(\$_SESSION["c"]-\$_SESSION["a"],2)

Is this wrong / ill advised for any reason? Or just another way...

Thank you,

Mat

Re: Missing 0.0000001 - driving me crazy - help!

"mat" wrote:

No, that's fine. Just bear in mind that the round() function returns a
floating point value, so you still shouldn't expect it to be exactly equal
to the rounded number.

This is a problem common to all programming languages, not just PHP. You
should never expect floating point values to be exactly equal to anything.
Code like this just won't work properly:

\$x = 1/10;
if (\$x == 0.1) { ... do something ...}

--
phil [dot] ronan @ virgin [dot] net
http://vzone.virgin.net/phil.ronan/

Re: Missing 0.0000001 - driving me crazy - help!

round() converts the result of whatever is between the parentheses to a
float and rounds that up to whatever number of decimals you want, so
you start getting problems around the 15th decimal.

The bcmath functions work on strings to whatever degree of accuracy you
ask for and will never produce errors.

So basically if you round up to fewer than, say ten decimals, ordinary
maths functions are fine, but more than that, use bcmath.

Of course, with ints you just get ints back.

Ian

Re: Missing 0.0000001 - driving me crazy - help!

wrote:

You appear to be working with monetary values from the names in your original
post. You shouldn't use floating point numbers for money; use integers,
multiplied up to the minor currency. So £1.50 = 150.

This way the floating point inaccuracies (which are not specific to Windows or
PHP, they are fundamental to the way x86 and many other processors handle
floating point numbers) don't become an issue, because you're always dealing
with exact integer calculations.
--
Andy Hassall :: andy@andyh.co.uk :: http://www.andyh.co.uk
http://www.andyhsoftware.co.uk/space :: disk and FTP usage analysis tool

Re: Missing 0.0000001 - driving me crazy - help!

"Andy Hassall" wrote:

There might be a good reason for using floats. For example, all your sales
tax calculations will be rounded down if you use integers.

--
phil [dot] ronan @ virgin [dot] net
http://vzone.virgin.net/phil.ronan/

Re: Missing 0.0000001 - driving me crazy - help!

wrote:

Then you can multiply up to the number of decimal places required by your tax
authority and apply their rounding rules (up or down) - more deterministic than
having small but unpredictable amounts of money being lost or gained due to
non-exact float representations. Depending on how many decimals you pick, the
32-bit limits on integers (31 bits for signed) could then become a problem for
large transactions or monthly/yearly totals.
--
Andy Hassall :: andy@andyh.co.uk :: http://www.andyh.co.uk
http://www.andyhsoftware.co.uk/space :: disk and FTP usage analysis tool

Re: Missing 0.0000001 - driving me crazy - help!

Andy Hassall (andy@andyh.co.uk) wrote:
: wrote:

: >"Andy Hassall" wrote:
: >
: >> You appear to be working with monetary values from the names in your original
: >> post. You shouldn't use floating point numbers for money; use integers,
: >> multiplied up to the minor currency. So 1.50 = 150.
: >
: >There might be a good reason for using floats. For example, all your sales
: >tax calculations will be rounded down if you use integers.

:  Then you can multiply up to the number of decimal places required by your tax
: authority and apply their rounding rules (up or down) - more deterministic than
: having small but unpredictable amounts of money being lost or gained due to
: non-exact float representations. Depending on how many decimals you pick, the
: 32-bit limits on integers (31 bits for signed) could then become a problem for
: large transactions or monthly/yearly totals.

(My understanding, feel free to argue.)

Computer double size floating point numbers can perform calculations on
integer _values_ with out integer value errors (exactly like integer
numbers do).

But the computer double can represent a much larger range of exact integer
values.

So to avoid 32-bit limits on integer numbers, scale the value to create an
integer value (just like you suggest), but store and manipulate that value
using doubles.

I guess the only issue would be to determine if any part of a caculation's
integer result went beyond the range of what could be represented exactly,
but I think that question also arises if you use plain integers anyway, so
I'm not sure this is an additional problem.  E.g. if you multiply two
large integers using integer arithmentic then the resulting value may not
be correct and you don't get an error to warn you - at least that is what
I confirmed in one quick test using one C compiler to multiple two large
integers.

--

This programmer available for rent.

Re: Missing 0.0000001 - driving me crazy - help!

On 6 Dec 2005 11:04:51 -0700, yf110@vtn1.victoria.tc.ca (Malcolm Dew-Jones)
wrote:

Makes sense - you can go up to 2^53 in an exact form in a double using just
the significand without using the exponent bits.
--
Andy Hassall :: andy@andyh.co.uk :: http://www.andyh.co.uk
http://www.andyhsoftware.co.uk/space :: disk and FTP usage analysis tool

Re: Missing 0.0000001 - driving me crazy - help!

There is no exact representation of 0.15 in binary floating point.
I suggest not printing so many digits after the decimal point.
printf() should round it.

0.15 as double:
Before:        0.149999999999999966693309261245303787291049957275390625000000
Value:         0.149999999999999994448884876874217297881841659545898437500000
After:         0.150000000000000022204460492503130808472633361816406250000000

0.15 as float:
Before:        0.149999991059303283691406250000000000000000000000000000000000
Value:         0.150000005960464477539062500000000000000000000000000000000000
After:         0.150000020861625671386718750000000000000000000000000000000000

Gordon L. Burditt