Possible Duplicate:
Why can't decimal numbers be represented exactly in binary?
problem with floating values
$var1 = 1;
for ( $i=0; $i<30; $i++ ) {
$var1 += 0.1;
$var2 = floor($var1);
$var3 = $var1-$var2;
if ( $var3 == 0.5 ) {
$var1 = $var2+1;
}
}
The intention of this loop is to count 1.0, 1.1, 1.2, 1.3, 1.4, and then jump to 2.0, 2.1, 2.2 etc
The problem I'm getting is that the if
statement is never true. Also every tenth calculation resolves to some insane scientific answer.
How do I fix this? please help!
Edit: I wrote the question in a bit of a frustrated rush and it was more than one, I see that now.
The first part of the question really was "how can I make this work by-passing this floating point querk" and "why is this querk even happening!"
Thank you for all the great replies and I'm voting the answer as correct that easily answered the core question of "how to make this work".
Using 0.49 instead of 0.5 and > instead of == does it. Crude and not the best code in the world but it does solve the original question. Thank you to everyone for the other responses that I am going to read and follow up on to improve my coding.
Once again, many thanks.
This one gives desired results:
$var1 = 1;
for ( $i=0; $i<30; $i++ ) {
$var1 += 0.1;
$var2 = (int)($var1);
$var3 = $var1-$var2;
if ( $var3 >= 0.45 ) {
$var1 = $var2+1;
}
echo $var1.' '.$var2.' '.$var3.'<br/>';
}
Computers cannot accurately represent floats, as they're stored in binary (base 2). All floats will have an ever so slight bit of adjustment +/-.
See here: http://www.cygnus-software.com/papers/comparingfloats/comparingfloats.htm
Binary float point will choose approximate value to represent base-10 float point which it cannot represents. So, it is not accurate to compare float to float (because you cannot determine its behavior).
You need to invent your own base-10 float point. It is not hard to invent if you know which precise you want. In your case, you will need only one-precise digit. Formula for our base-10 integer would be: value / 10 to power of our precise-digit which is 1, hence your formula is = value / 10.
Performing arithmetic operation is as easy as performing normal arithmetic. Just convert normal representation (refers to representation that computer use) to our invented representation. In your case, $var1 += 0.1
would turn to $var1 += 1
.
Outputting would be as easy as converting your representation to normal representation. In your case, echo $var1 . '<br>';
would turn to echo $var1 / 10 . '<br>';
$var1 = 10;
for ( $i=0; $i<30; $i++ ) {
echo $var1 / 10 . '<br>';
$var1 += 1;
if ($var1 % 10 == 5) {
$var1 = $var1+5;
}
}
Lots of good information on why this happens and a number of solutions. One of the fundamental design goals of PHP is its loose-typed design and implicit casting. In that spirit I think one of the simplest solutions to this problem is to use string comparison. In your case it's a great solution:
<?php
$var1 = 1;
for ( $i=0; $i<30; $i++ ) {
$var1 += 0.1;
$var2 = floor($var1);
$var3 = $var1 - $var2;
echo "$var1 | $var2 | $var3 \n";
if (number_format($var3, 1) == '0.5') {
echo "It's true\n";
$var1 = $var2 + 1;
}
}
A proper solution is to use the bcmath package:
$var1 = '1.0'; bcscale(1);
for ( $i = 0; $i < 30; $i++) {
$var2 = $var1;
for( $j = 0; $j < 5; $j++) {
echo $var2 . "\n";
$var2 = bcadd( $var2, '0.1');
}
$var1 = bcadd( $var1, '1');
}
This outputs the correct result.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With