I need to round some numbers and get modifying value.
I've tried:
$Num=12.456;
$RoundDiff=$Num-round($Num,2);
$Num=round($Num,2);
echo $Num.'<br/>'.$RoundDiff;
but I've got:
12.46
-0.0040000000000013
while my expected result was:
12.46
-0.004
What's wrong?
How can I get what I need?
Try below code. It will give you the expected result. I have just rounded off the difference.
$Num = 12.456;
$RoundDiff = $Num-round($Num,2);
$Num = round($Num,2);
echo $Num.'<br/>'.round($RoundDiff,3);
CodePad
There is issue in precision of floating point values. Refer this article for reference - The PHP floating point precision is wrong by default
If you want exact precision you can use bcmath or gmp.
Because internally, computers use a format (binary floating-point)
that cannot accurately represent a number like 0.1, 0.2 or 0.3 at all.
When the code is compiled or interpreted, your “0.1” is already
rounded to the nearest number in that format, which results in a small
rounding error even before the calculation happens. — floating point
guide
Another Reference :
Given that the implicit precision of a (normal) IEEE 754 double
precision number is slightly less than 16 digits 3, this is a
serious overkill. Put another way, while the mantissa is composed of
52 bits plus 1 implicit bit, 100 decimal digits can carry up to
100*log2(10) =~ 332 bits of information, around 6 times more.
Given this, I propose changing the default precision to 17 (while the
precision is slightly less than 16, a 17th digit is necessary because
the first decimal digit carries little information when it is low). —
source
BCMATH : As requested in comments
$a = 12.456;
$b = round($a,2);
echo 'a ='.$a.'<br>';
echo 'b ='.$b.'<br>';
echo bcsub($a, $b, 3);
Related
When I set PHP's precision setting to values 18 or higher (whether in php.ini or at runtime), the round() function yields unexpected results. Is this a bug; or what am I missing?
Results for e.g. rounding the float 12.4886724321 to 4 decimal precision are as follows:
14: 12.4887
...
17: 12.4887
18: 12.4886999999999997
19: 12.48869999999999969
20: 12.48869999999999969
21: 12.4886999999999996902
22: 12.4886999999999996902
23: 12.488699999999999690203
24: 12.4886999999999996902034
Test case as follows:
$floaty = 12.4886724321;
for ($i = 14; $i <= 24; $i++) {
ini_set('precision', $i);
echo ini_get('precision') . ': ';
echo round($floaty, 4) . "\n";
}
Even if I set my $floaty to just 12.4, I'll get 18: 12.4800000000000004 etc. for "extrapolated" decimals. What exactly is this twilight zone we're entering at precisions 18 or greater? I do know how to clean things up for output, but I'd like to know why this happens, and whether it's intended behavior.
Tested on PHP 7.0.2 # W7x64 and PHP 5.6.8 # CentOS 6.5 with identical results. number_format() doesn't do this, suppose it's more blunt (or non-mathematical) in how it truncates the decimals.
EDIT: Seems to happen with all math ops? 1234.56 - 1233.03 iterated at different precisions:
12: 1.53
13: 1.53
14: 1.53
15: 1.52999999999997
16: 1.529999999999973
17: 1.5299999999999727
18: 1.52999999999997272
Floats in base 10, like 0.1 or 0.7, do not have an exact representation as float numbers in base 2
see http://floating-point-gui.de
PHP Docs to float
see Floating point precision
Floating point numbers have limited precision. Although it depends on the system, PHP typically uses the IEEE 754 double precision format, which will give a maximum relative error due to rounding in the order of 1.11e-16. Non elementary arithmetic operations may give larger errors, and, of course, error propagation must be considered when several operations are compounded.
Additionally, rational numbers that are exactly representable as floating point numbers in base 10, like 0.1 or 0.7, do not have an exact representation as floating point numbers in base 2, which is used internally, no matter the size of the mantissa. Hence, they cannot be converted into their internal binary counterparts without a small loss of precision. This can lead to confusing results: for example, floor((0.1+0.7)*10) will usually return 7 instead of the expected 8, since the internal representation will be something like 7.9999999999999991118....
So never trust floating number results to the last digit, and do not compare floating point numbers directly for equality. If higher precision is necessary, the arbitrary precision math functions and gmp functions are available.
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
I'm currently trying to do math with large decimals. This works fine:
<?php
$math = 99 + 0.0001;
echo $math;
?>
Output: 99.0001
However, when I try to add with decimals with more than 12 decimal places, I do not receive the expected output:
<?php
$math = 99 + 0.0000000000001;
echo $math;
?>
Output: 99
How can I make it so that if I add with a decimal that has more than 12 decimal places, that the result will still have the exact answer without rounding? For example:
<?php
$math = 99 + 0.0000000000001;
echo $math;
?>
Output: 99.0000000000001
Quick google search yielded this.
Floating point numbers have limited precision. Although it depends on
the system, PHP typically uses the IEEE 754 double precision format,
which will give a maximum relative error due to rounding in the order
of 1.11e-16.
The page I linked has lots of helpful info regarding floating point numbers in PHP as well as links to libraries which can work with arbitrary precision floating point numbers. Could be useful depending on your needs.
Edit: Also, as Mark Baker said in a comment, you may also need to specify the precision of the number you want to print in the printf() format string. Check this page out and look at number 5.
This question already has answers here:
Is floating point math broken?
(31 answers)
Closed 5 years ago.
$a = '35';
$b = '-34.99';
echo ($a + $b);
Results in 0.009999999999998
What is up with that? I wondered why my program kept reporting odd results.
Why doesn't PHP return the expected 0.01?
Because floating point arithmetic != real number arithmetic. An illustration of the difference due to imprecision is, for some floats a and b, (a+b)-b != a. This applies to any language using floats.
Since floating point are binary numbers with finite precision, there's a finite amount of representable numbers, which leads accuracy problems and surprises like this. Here's another interesting read: What Every Computer Scientist Should Know About Floating-Point Arithmetic.
Back to your problem, basically there is no way to accurately represent 34.99 or 0.01 in binary (just like in decimal, 1/3 = 0.3333...), so approximations are used instead. To get around the problem, you can:
Use round($result, 2) on the result to round it to 2 decimal places.
Use integers. If that's currency, say US dollars, then store $35.00 as 3500 and $34.99 as 3499, then divide the result by 100.
It's a pity that PHP doesn't have a decimal datatype like other languages do.
Floating point numbers, like all numbers, must be stored in memory as a string of 0's and 1's. It's all bits to the computer. How floating point differs from integer is in how we interpret the 0's and 1's when we want to look at them.
One bit is the "sign" (0 = positive, 1 = negative), 8 bits are the exponent (ranging from -128 to +127), 23 bits are the number known as the "mantissa" (fraction). So the binary representation of (S1)(P8)(M23) has the value (-1^S)M*2^P
The "mantissa" takes on a special form. In normal scientific notation we display the "one's place" along with the fraction. For instance:
4.39 x 10^2 = 439
In binary the "one's place" is a single bit. Since we ignore all the left-most 0's in scientific notation (we ignore any insignificant figures) the first bit is guaranteed to be a 1
1.101 x 2^3 = 1101 = 13
Since we are guaranteed that the first bit will be a 1, we remove this bit when storing the number to save space. So the above number is stored as just 101 (for the mantissa). The leading 1 is assumed
As an example, let's take the binary string
00000010010110000000000000000000
Breaking it into it's components:
Sign Power Mantissa
0 00000100 10110000000000000000000
+ +4 1.1011
+ +4 1 + .5 + .125 + .0625
+ +4 1.6875
Applying our simple formula:
(-1^S)M*2^P
(-1^0)(1.6875)*2^(+4)
(1)(1.6875)*(16)
27
In other words, 00000010010110000000000000000000 is 27 in floating point (according to IEEE-754 standards).
For many numbers there is no exact binary representation, however. Much like how 1/3 = 0.333.... repeating forever, 1/100 is 0.00000010100011110101110000..... with a repeating "10100011110101110000". A 32-bit computer can't store the entire number in floating point, however. So it makes its best guess.
0.0000001010001111010111000010100011110101110000
Sign Power Mantissa
+ -7 1.01000111101011100001010
0 -00000111 01000111101011100001010
0 11111001 01000111101011100001010
01111100101000111101011100001010
(note that negative 7 is produced using 2's complement)
It should be immediately clear that 01111100101000111101011100001010 looks nothing like 0.01
More importantly, however, this contains a truncated version of a repeating decimal. The original decimal contained a repeating "10100011110101110000". We've simplified this to 01000111101011100001010
Translating this floating point number back into decimal via our formula we get 0.0099999979 (note that this is for a 32-bit computer. A 64-bit computer would have much more accuracy)
A Decimal Equivalent
If it helps to understand the problem better, let's look decimal scientific notation when dealing with repeating decimals.
Let's assume that we have 10 "boxes" to store digits. Therefore if we wanted to store a number like 1/16 we would write:
+---+---+---+---+---+---+---+---+---+---+
| + | 6 | . | 2 | 5 | 0 | 0 | e | - | 2 |
+---+---+---+---+---+---+---+---+---+---+
Which is clearly just 6.25 e -2, where e is shorthand for *10^(. We've allocated 4 boxes for the decimal even though we only needed 2 (padding with zeroes), and we've allocated 2 boxes for signs (one for the sign of the number, one of the sign of the exponent)
Using 10 boxes like this we can display numbers ranging from -9.9999 e -9 to +9.9999 e +9
This works fine for anything with 4 or fewer decimal places, but what happens when we try to store a number like 2/3?
+---+---+---+---+---+---+---+---+---+---+
| + | 6 | . | 6 | 6 | 6 | 7 | e | - | 1 |
+---+---+---+---+---+---+---+---+---+---+
This new number 0.66667 does not exactly equal 2/3. In fact, it's off by 0.000003333.... If we were to try and write 0.66667 in base 3, we would get 0.2000000000012... instead of 0.2
This problem may become more apparent if we take something with a larger repeating decimal, like 1/7. This has 6 repeating digits: 0.142857142857...
Storing this into our decimal computer we can only show 5 of these digits:
+---+---+---+---+---+---+---+---+---+---+
| + | 1 | . | 4 | 2 | 8 | 6 | e | - | 1 |
+---+---+---+---+---+---+---+---+---+---+
This number, 0.14286, is off by .000002857...
It's "close to correct", but it's not exactly correct, and so if we tried to write this number in base 7 we would get some hideous number instead of 0.1. In fact, plugging this into Wolfram Alpha we get: .10000022320335...
These minor fractional differences should look familiar to your 0.0099999979 (as opposed to 0.01)
There's plenty of answers here about why floating point numbers work the way they do...
But there's little talk of arbitrary precision (Pickle mentioned it). If you want (or need) exact precision, the only way to do it (for rational numbers at least) is to use the BC Math extension (which is really just a BigNum, Arbitrary Precision implementation...
To add two numbers:
$number = '12345678901234.1234567890';
$number2 = '1';
echo bcadd($number, $number2);
will result in 12345678901235.1234567890...
This is called arbitrary precision math. Basically all numbers are strings which are parsed for every operation and operations are performed on a digit by digit basis (think long division, but done by the library). So that means it's quite slow (in comparison to regular math constructs). But it's very powerful. You can multiply, add, subtract, divide, find modulo and exponentiate any number that has an exact string representation.
So you can't do 1/3 with 100% accuracy, since it has a repeating decimal (and hence isn't rational).
But, if you want to know what 1500.0015 squared is:
Using 32 bit floats (double precision) gives the estimated result of:
2250004.5000023
But bcmath gives the exact answer of:
2250004.50000225
It all depends on the precision you need.
Also, something else to note here. PHP can only represent either 32 bit or 64 bit integers (depending on your install). So if an integer exceeds the size of the native int type (2.1 billion for 32bit, 9.2 x10^18, or 9.2 billion billion for signed ints), PHP will convert the int into a float. While that's not immediately a problem (Since all ints smaller than the precision of the system's float are by definition directly representable as floats), if you try multiplying two together, it'll lose significant precision.
For example, given $n = '40000000002':
As a number, $n will be float(40000000002), which is fine since it's exactly represented. But if we square it, we get: float(1.60000000016E+21)
As a string (using BC math), $n will be exactly '40000000002'. And if we square it, we get: string(22) "1600000000160000000004"...
So if you need the precision with large numbers, or rational decimal points, you might want to look into bcmath...
bcadd() might be useful here.
<?PHP
$a = '35';
$b = '-34.99';
echo $a + $b;
echo '<br />';
echo bcadd($a,$b,2);
?>
(inefficient output for clarity)
First line gives me 0.009999999999998.
Second gives me 0.01
Because 0.01 can't be represented exactly as sum of series of binary fractions. And that is how floats are stored in memory.
I guess it is not what you want to hear, but it is answer to question. For how to fix see other answers.
Every number will be save in computer by binary value such as 0, 1. In Single-precision numbers occupy 32 bits.
The floating point number can be presented by: 1 bit for sign, 8 bit for exponent and 23 bit called mantissa (fraction).
Look the example below:
0.15625 = 0.00101 = 1.01*2^(-3)
sign: 0 mean positive number, 1 mean negative number, in this case it is 0.
exponent: 01111100 = 127 - 3 = 124.
Note: the bias = 127 so biased exponent = −3 + the "bias". In single precision, the bias is ,127, so in this example the biased exponent is 124;
At fraction part, we have: 1.01 mean: 0*2^-1 + 1*2^-2
Number 1 (first position of 1.01) do not need to save because when present the floating number in this way the first number always be 1.
For example convert: 0.11 => 1.1*2^(-1), 0.01 => 1*2^(-2).
Another example show always remove the first zero: 0.1 will be presented 1*2^(-1). So the first alwasy be 1.
The present number of 1*2^(-1) will be:
0: positive number
127-1 = 126 = 01111110
fraction: 00000000000000000000000 (23 number)
Finally: The raw binary is:
0 01111110 00000000000000000000000
Check it here: http://www.binaryconvert.com/result_float.html?decimal=048046053
Now if you already understand how a floating point number are saved. What happen if the number cannot save in 32 bit (simple precision).
For example: in decimal. 1/3 = 0.3333333333333333333333 and because it is infinite I suppose we have 5 bit to save data. Repeat again this is not real. just suppose. So the data saved in computer will be:
0.33333.
Now when the number loaded the computer calculate again:
0.33333 = 3*10^-1 + 3*10^-2 + 3*10^-3 + 3*10^-4 + 3*10^-5.
About this:
$a = '35';
$b = '-34.99';
echo ($a + $b);
The result is 0.01 ( decimal). Now let show this number in binary.
0.01 (decimal) = 0 10001111 01011100001010001111 (01011100001010001111)*(binary)
Check here: http://www.binaryconvert.com/result_double.html?decimal=048046048049
Because (01011100001010001111) is repeat just like 1/3. So computer cannot save this number in their memory. It must sacrifice. This lead not accuracy in computer.
Advanced
( You must have knowledge about mathematics )
So why we can easily show 0.01 in decimal but not in binary.
Suppose the fraction in binary of 0.01 (decimal) is finite.
So 0.01 = 2^x + 2^y... 2^-z
0.01 * (2^(x+y+...z)) = (2^x + 2^y... 2^z)*(2^(x+y+...z)). This expression is true when (2^(x+y+...z)) = 100*x1. There are not integer n = x+y+...+z exists.
=> So 0.01 (decimal) must be infine in binary.
Use PHP's round() function: http://php.net/manual/en/function.round.php
This answer solves problem, but not explains why. I thought that it is obvious [I am also programming in C++, so it IS obvious for me ;]], but if not, let's say that PHP has it's own calculating precision and in that particular situation it returned most complying information regarding that calculation.
wouldn't it be easier to use number_format(0.009999999999998, 2) or $res = $a+$b; -> number_format($res, 2);?
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);