When I Try get remainder, It gives invalid value. I am trying to get remained of two decimal values and I get
3.4694469519536E-18
My Values are
$x=0.1; $y=0.005;
I tried the following:
echo $ed = fmod(0.1,0.005); OutPut:3.4694469519536E-18
echo $ed = ((floatval(0.1)) % (floatval(0.005)));
But getting
Warning: Division by zero
But Reality is 0.1 will be divide by 0.005 in 20 times. i.e. 0.1/0.005 = 20
The same function returns correct value when I use 10%1 = 0.
How can I get the exact remainder. ( I don't face the same issue in JQuery :) )
Check an Work Around: PHP Division Float Value WorkAround
The % operator just works on integers, so it truncates your floats to integers and tries to do 0 % 0, which results in a division by zero.
The only thing you can use is fmod(). The issue you face here is the typical floating point imprecision (the value is "nearly" zero). I'd consider to just ignore values smaller then 10^-15. There's no real alternative to this in PHP.
What you could try is doing the real division, then, with a bit of luck traces of the imprecision disappear. (like round(0.1 / 0.005) === 0.1 / 0.005)
Related
I stumbled on the difference between the result of Machine epsilon calculation.
When compared to 0 PHP yields 4.9406564584125E-324.
While for 1 it pops up with 1.1102230246252E-16.
Quite a difference. Guess it's something with the type of data initially set by default in PHP.
The code is:
<?php
//Machine epsilon calculation
$e = 1;
$eTmp = null;
for ($i = 0; 0 != 0 + $e; $i++){ //Changing 0 by 1 produces absolutely different result
$e = $e/2;
if ($e != 0) {$eTmp = $e;}
}
echo $eTmp;
//var_dump($eTmp);
?>
Any clarification on the difference between the two?
And how can a variable or value by assigned manually to float in PHP?
Many thanks for your ideas!
Your PHP implementation appear to be using the common IEEE 754 64-bit binary format. In this format, finite values are represented, effectively, as a sign, an integer M less than 253, and an integer exponent e between −1074 and 971, inclusive. The value represented is +M•2e or −M•2e, according to the sign. (This format will often be described with M being a fraction between 1 and 2 with a certain number of bits after the radix point. The descriptions are mathematically equivalent, just useful for different purposes.)
Given this, we can easily see that the next representable number after 0 is +1•2−1074, which is approximately 4.94065645841246544•10−324.
In this format as stated, 1 can be represented as +1•20. However, to see what the smallest change that can be made to 1 is, we must normalize it by making M as large as possible. 1 can also be represented with M = 252 and e = −52, resulting in +252•2−52. In this form, we can see the next representable value greater than 1 is achieved by adding 1 to M, mkaing the number +(252+1)•2−52.
The difference between 1 and +(252+1)•2−52 is of course 1•2−52, which is exactly 2.220446049250313080847263336181640625•10−16.
Note that your code records $eTmp (if $e is not zero) after reducing $e with $e = $e/2, which means it reports the first value of $e that does not cause a change. Thus it reports 1.11e-16 rather than 2.22e-16, which is the last value that does cause a change to 1.
floor function in PHP behave weirdly.
For 16 decimal values it gives floor value but by increasing 1 decimal it round.
$int = 0.99999999999999999;
echo floor($int); // returns 1
$int = 0.9999999999999999;
echo floor($int); // returns 0
$int = 0.99999999999999994;
echo floor($int); // returns 0
Is it defined/explained somewhere, at which point it gives "round" value?
Is there any function which gives 0 anyhow how many 9 in decimals?
It's not floor that rounds, it's floating point math that does.
This line:
echo 0.99999999999999999;
Prints 1 (demo) because 0.99999999999999999 is too precise to be represented by a (64-bit?) float, so the closest possible value is taken, which happens to be 1.
0.99999999999999994 is also too precise to be represented exactly, but here the closest representable value happens to be 0.9999999999999999.
Is it defined/explained somewhere, at which point it gives "round" value?
It's complicated, but the numbers are rounded almost always.
I believe there is no definition of "from when values will be approximated", but that is a mathematical property that follows from the definitions in the IEEE 754 floating point standard.
To be safe, just assume everything is approximated.
Is there any function which gives 0 anyhow how many 9 in decimals?
No. The problem is that, for PHP, 0.99999999999999999 is literally the same as 1.
They're represented by exactly the same sequence of bits, so it can't distinguish them.
There are some solutions to work with bigger precision decimals, but that requires some major code changes.
Probably of interest to you:
Working with large numbers in PHP
Note that while you may get arbitrary precision, you will never get infinite precision, as that would require infinite amounts of storage.
Also note that if you actually were dealing with infinite precision, 0.999... (going on forever) would be truly (as in, mathematically provable) equal to 1, as explained in depth in this Wikipedia article.
$float_14_digits = 0.99999999999999;
echo $float_14_digits; // prints 0.99999999999999
echo floor($float_14_digits); // prints 0
$float_15_digits = 0.999999999999999;
echo $float_15_digits; // prints 1
echo floor($float_15_digits); // prints 1
exit;
on my development machine that behavior happens on digit '15' not '17' like yours. PHP rounds the last digit in the floating numbers. your floor() function has nothing to do with this behavior
Yesterday I was helping some one and got a weird error which I could not explain to him how it worked.
The code (tested on 3 machines (PHP 5.3 and PHP 5.4))
// (float)65.35
$percentage = round(65.351, 2);
// (float) 6535
$total = $percentage * 100;
// (int) 6534
$int = (int) $total;
What is suspected was that the int value would be 6535 but it ended up being 6534.
Could some one explain what is going on?
You don't actually have 65.35 after the first operation.
>>> '%.20f' % (65.351 - 0.001,)
'65.34999999999999431566'
Either start with an integral value scaled appropriately in the first place, don't attempt to convert the value to an integer, or add a small value before taking the integer value.
This has to do with how floating point (read the warning in this link!) values are stored in memory. Indeed after the first operation you don't have an exact decimal value, but a rounded value. Probably 65.34999999 or so. (The value is stored as a list of bits (0/1))
This is why when talking about money, developers don't store dollars/euros but rather the amount of cents. This way they avoid working with floats that are less precise for decimals, but rather work with integers, that are precise.
Use round instead of int
round($total)
$r=$explode('.',$total);
debug($r);
Modulus operator is supposed to show the remainder. Like for echo(34%100) outputs 34. But why do i get a "Division by zero" error for this code echo(34%4294967296)
4294967296 is 2^32 and cannot be represented as 32 bit number - it wraps back to 0. If you use 64-bit version of PHP, it may work.
You might be able to use floating point modulus fmod to get what you want without overflowing.
https://bugs.php.net/bug.php?id=51731
2^31 is the largest integer you can get on Windows.
If you still want to mod large numbers, use bcmod.
I found this question searching for "division by zero error when using modulus", but the reason why was different.
Modulus (the % operator) will not work when the denomenator is is less than 1. using fmod() solves the problem.
Example:
$num = 5.1;
$den = .25;
echo ($num % $den);
// Outputs Warning: Division by zero
echo fmod($num, $den);
// Outputs 0.1
$num = 5.1;
$den = 1;
echo ($num % $den);
// Outputs 0, which is incorrect
echo fmod($num, $den);
// Outputs 0.1, which is correct
There's a lot of reports of mod getting wonky with large integers in php. Might be an overflow in the calculation or even in that number itself which is going to give you bugs. Best to use a large number library for that. Check out gmp or bcmath.
Because the float data type in PHP is inaccurate, and a FLOAT in MySQL takes up more space than an INT (and is inaccurate), I always store prices as INTs, multipling by 100 before storing to ensure we have exactly 2 decimal places of precision. However I believe PHP is misbehaving. Example code:
echo "<pre>";
$price = "1.15";
echo "Price = ";
var_dump($price);
$price_corrected = $price*100;
echo "Corrected price = ";
var_dump($price_corrected);
$price_int = intval(floor($price_corrected));
echo "Integer price = ";
var_dump($price_int);
echo "</pre>";
Produced output:
Price = string(4) "1.15"
Corrected price = float(115)
Integer price = int(114)
I was surprised. When the final result was lower than expected by 1, I was expecting the output of my test to look more like:
Price = string(4) "1.15"
Corrected price = float(114.999999999)
Integer price = int(114)
which would demonstrate the inaccuracy of the float type. But why is floor(115) returning 114??
Try this as a quick fix:
$price_int = intval(floor($price_corrected + 0.5));
The problem you are experiencing is not PHP's fault, all programming languages using real numbers with floating point arithmetics have similar issues.
The general rule of thumb for monetary calculations is to never use floats (neither in the database nor in your script). You can avoid all kinds of problems by always storing the cents instead of dollars. The cents are integers, and you can freely add them together, and multiply by other integers. Whenever you display the number, make sure you insert a dot in front of the last two digits.
The reason why you are getting 114 instead of 115 is that floor rounds down, towards the nearest integer, thus floor(114.999999999) becomes 114. The more interesting question is why 1.15 * 100 is 114.999999999 instead of 115. The reason for that is that 1.15 is not exactly 115/100, but it is a very little less, so if you multiply by 100, you get a number a tiny bit smaller than 115.
Here is a more detailed explanation what echo 1.15 * 100; does:
It parses 1.15 to a binary floating point number. This involves rounding, it happens to round down a little bit to get the binary floating point number nearest to 1.15. The reason why you cannot get an exact number (without rounding error) is that 1.15 has infinite number of numerals in base 2.
It parses 100 to a binary floating point number. This involves rounding, but since 100 is a small integer, the rounding error is zero.
It computes the product of the previous two numbers. This also involves a little rounding, to find the nearest binary floating point number. The rounding error happens to be zero in this operation.
It converts the binary floating point number to a base 10 decimal number with a dot, and prints this representation. This also involves a little rounding.
The reason why PHP prints the surprising Corrected price = float(115) (instead of 114.999...) is that var_dump doesn't print the exact number (!), but it prints the number rounded to n - 2 (or n - 1) digits, where n digits is the precision of the calculation. You can easily verify this:
echo 1.15 * 100; # this prints 115
printf("%.30f", 1.15 * 100); # you 114.999....
echo 1.15 * 100 == 115.0 ? "same" : "different"; # this prints `different'
echo 1.15 * 100 < 115.0 ? "less" : "not-less"; # this prints `less'
If you are printing floats, remember: you don't always see all digits when you print the float.
See also the big warning near the beginning of the PHP float docs.
The other answers have covered the cause and a good workaround to the problem, I believe.
To aim at fixing the problem from a different angle:
For storing price values in MySQL, you should probably look at the DECIMAL type, which lets you store exact values with decimal places.
Maybe it's another possible solution for this "problem":
intval(number_format($problematic_float, 0, '', ''));
PHP is doing rounding based on significant digits. It's hiding the inaccuracy (on line 2). Of course, when floor comes along, it doesn't know any better and lops it all the way down.
As stated this is not a problem with PHP per se, It is more of an issue of handling fractions that can't be expressed as finite floating point values hence leading to loss of character when rounding up.
The solution is to ensure that when you are working on floating point values and you need to maintain accuracy - use the gmp functions or the BC maths functions - bcpow, bcmul et al. and the problem will be resolved easily.
E.g instead of
$price_corrected = $price*100;
use $price_corrected = bcmul($price,100);