Same number different formats in PHP - php

I have two numbers in PHP. 81.0000 and 81. While they are equal in reality I cannot get them to be equal in PHP.
I have tried casting both numbers to float and they still won't come thought as equal.
Anyone have any idea how I can have these two numbers be the same?

Check out the awesome WARNING on php.net:
never trust floating number results to the last digit, and never compare floating point numbers for equality.
The very best you can to is type cast to (int), or use PHP rounding functions like round(), floor(), or ceil().
UPDATE
Check out the Arbitrary Precision Math Functions such as the one #Jose Vega pointed out in his answer. They should get you where you need to go.

bccomp — Compare two arbitrary precision numbers
bccomp ( string $left_operand , string $right_operand [, int $scale ] )
<?php
echo bccomp('1', '2') . "\n"; // -1
echo bccomp('1.00001', '1', 3); // 0
echo bccomp('1.00001', '1', 5); // 1
?>

Related

PHP cast float to string is different from cast to int

Something you would not expect but need to be aware of when you're dealing with floating point numbers in php
<?php
$i = (32.87*100);
echo $i; // outputs 3287
echo (int) $i; // outputs 3286 !!
echo (int) (string) $i // outputs 3287
Internal representation of $i is something like 3286.9999999.
Why is the string representation of $i 3287 ?
Let's go through your code:
$i = (32.87*100);
Now $i is slightly less than 3287 as float as shown below:
echo sprintf('%.30f', $i) . PHP_EOL; //3286.999999999999545252649113535881
But when you print (echo) it, you'll get rounded value.
echo $i; // outputs 3287
And here we come to the trick - casting float to int means to simply cut off the part after dot, despite its .99999999(...) which is almost 1 (but it's not!). So the output is 3286.
echo (int) $i; // outputs 3286 !!
Now, in the last example, you first cast float to string, which means exactly what you already did by doing echo $i; because whatever you print, internally PHP need to cast to string. So it's 3286.999999999999545252649113535881 casted to "3287" and then casted to 3287, and then printed.
echo (int) (string) $i // outputs 3287;
To sum up, it's difference between the way float is casted to string and int.
EDIT Further explanation about "rounding"
Well it's not really rounding. I've made a mistake by saying that.
PHP uses 64 bit float (do called double), which in decimal representation has 14 digit precision.
As mentioned in PHP manual:
The size of a float is platform-dependent, although a maximum of approximately 1.8e308 with a precision of roughly 14 decimal digits is a common value (the 64 bit IEEE format).
That means, that a float can contain (for most of the time) a 14-digit number (in decimal) and it doesn't matter where the dot is placed.
Now, the most important thing:
Casting to string doesn't round the float number
Examples:
$a = 1.23456789012349 - the last 9 is 15th digit, so you'll get "rounded" float to 1.2345678901235
$a = 12345678901234.9 - same as above
$a = 1.99999999999999 - last 9 is 15th digit, so you'll get 2
And as a string it will be printed exactly as the float is, which means 14 digits precision. The "rounding" is at the moment when we create float variable's structure in memory.
The last example is what we're talking about in this topic.
Now, why I did that mistake and said about "rounding"?
I misunderstood the result of echo sprintf('%.30f', $i). A saw many more digits and thought it's the real value of the float number.
But it's not.
As we know, 64-bit float has only 14 digits precision.
So where the result of sprintf comes from?
The answer is actually pretty easy.
We already know that it's not always possible to express a decimal number in binary system. So for example a simple 0.1 in float (binary representation) is just an approximation because the real binary representation would be infinitely long.
Now it works exactly the same when converting binary system to decimal. What can be expressed in binary (which means every float value), not always is possible to express in decimal.
So what sprintf('%.30f', $i) is to give the 30-digit precision approximation of converting the float number from binary to decimal system.
Thanks to #Quasimodo'sclone for asking in comment for being more precise about this. That made me go a little deeper in this topic.
You're casting $i (3287) to a string and then to an int, so the result stays 3287.
If you cast $i to an int you'll get 3286, and then if you cast it to a string you'll have what you want.
Try echo (string) (int) $i

PHP 7 intdiv() vx floor or custom type cast

I am looking PHP 7 new Additions in order to improve my skills, i want to know what exactly the difference between the intdiv() vs floor() or custom type cast
for example
echo (int) (8/3); // 2
echo floor((8/3)); // 2
echo intdiv((8/3)); // 2
what exactly the reason for this function to add in newer version of PHP.
(int) casts the value to int whereas floor() keeps a float as a float. You used positive numbers in your example, but the difference is in negative numbers.
intdivwas designed to keep int after division of 2 ints.
echo (int) (-8/3); // -2
echo floor(-8/3); // -3
echo intdiv(-8,3); //-2
There is a different in how they behave in terms of rounding.
divint() is more intelligent, it always knows if it supposes to round up or down.
floor()
With floor(), you will always round the value lover so:
3.33333 becomes 3
-3.33333 becomes -4
Which is often not what you intend.
divint()
divint will round to the closest number:
3.33333 becomes 3
-3.33333 becomes -3
Casting to (int)
Casting float/double to a int will perform similary like divint. So you can use:
(int)(10/3) returns 3
(int)(-10/3) returns -3
In general all these three you mentioned works similar as of your example but intdiv() will give you more accurate result while working extremely large set of numbers
here is example you can see.
echo PHP_INT_MAX; // 9223372036854775807
echo (int)(PHP_INT_MAX/2); // 4611686018427387904
// here you can look the ending number
echo floor(PHP_INT_MAX/2); // 4.6116860184274E+18
// here you can see floor will return scientific notation for larger numbers
echo intdiv(PHP_INT_MAX,2); // 4611686018427387903
// you can compare the result return by (int) cast
intdiv() always give you positive number or in other word intdiv() let you know how many times you can divide evenly
Another example developer always use Modulus Operator in order to get remainder but intdiv() will always return you positive numbers and let you know how many times you can divide evenly.
echo (5 % 2) // 1
echo intdiv(5, 2) // 2
Hope this good enough to understand the difference among all 3 of them..

PHP function for float or double comarision (bccomp and gmp_cmp)

PHP Suggested to use these function for floating point number comparison [here]
bccomp — Compare two arbitrary precision numbers
int bccomp ( string $left_operand , string $right_operand [, int $scale ] )
gmp_cmp — Compare numbers
int gmp_cmp ( resource $a , resource $b )
I used bccomp but I cant still get correct result:
<?php
$n1 = bcdiv(1, 2.36, 4);
$n2 = bcdiv(4237288, 10000000, 4);
echo bccomp( $n1, $n2, 4); // 0! must be 1
echo "<br>\n";
var_dump(bcdiv(1, 2.36, 4)); // string(6) "0.4237"
echo "<br>\n";
var_dump(bcdiv(4237288, 10000000, 4)); // string(6) "0.4237"
?>
So far, I knew that the result of 1/2.36 is equal to (0.4237288135593220338983050847457627118644067796610169491525423728813559322033898305084745762711864406......)
Then, how to I know that the number of decimals is great like this?
The possible solution:
$n1 = bcdiv(1, 2.36, 400);
$n2 = bcdiv(4237288, 10000000, 400);
echo bccomp( $n1, $n2, 400); //1
I think this solution is till not more usable.
Any suggestion?
When you comparing float numbers you MUST choose comparing scale. If 400 is nessary, so it's your scale.
Or... you can try to define your own divide function and compare result on every step of scaling of division. It might be a recursive function.
First, if you want to compare floats, you can use the built-in comparison operators such as <, as mentioned under the heading “Comparing float” on the page you linked to.
Second, if you do not want a direct comparison of the floating-point values (but want some sort of tolerance or partial comparison), you have not specified what comparison you actually want. For the needs of your application, under precisely what circumstances should a comparison return less-than, equal-to, and greater-than?
Third, bccomp is an atrocious way to compare floating-point values:
By default, it attempts to use the “number of digits” in the values to determine the tolerance to compare. However, binary floating-point values do not have a number of decimal digits in the way this code tries to measure them.
Even if they did, the amount of error that could be present in floating-point values after various operations is not a function of the number of decimal digits in the number, so the number of digits is not a good measure of how much tolerance should be accepted.
Once you determine how much error tolerance is acceptable, a digit-by-digit comparison fails to allow for that tolerance. E.g,, if you want to accept as equal two values if they differ by less than .01 but you compare the values 3.4951 and 3.4949 by comparing their two-digit representations “3.50” and “3.49”, they will be reported as unequal even though they differ by only .0002.

Is there a function like "bcadd" except it performs multiplication?

I'm hoping to do some math operations on numbers being represented as strings. So far, I haven't found anything that could do multiplication.
Thanks!
You need of the bcmul() method. You too can take a look on BC Math functions.
Example:
echo bcmul('2.123456', '4.7891011', 6); // 6 is the precision
You mean bcmul?
http://www.php.net/manual/en/function.bcmul.php
bcmul — Multiply two arbitrary precision number
string bcmul ( string $left_operand , string $right_operand [, int $scale ] )
Multiply the left_operand by the right_operand.

Strange number conversion error in PHP

How come the result for
intval("19.90"*100)
is
1989
and not 1990 as one would expect (PHP 5.2.14)?
That's because 19.90 is not exactly representable in base 2 and the closest approximation is slightly lower than 19.90.
Namely, this closest approximation is exactly 2^-48 × 0x13E66666666666. You can see its exact value in decimal form here, if you're interested.
This rounding error is propagated when you multiply by 100. intval will force a cast of the float to an integer, and such casts always rounds towards 0, which is why you see 1989. Use round instead.
You can also use bc* function for working with float :
$var = bcmul("19.90", "100");
echo intval($var);
intval converts doubles to integers by truncating the fractional component of the number. When dealing with some values, this can give odd results. Consider the following:
print intval ((0.1 + 0.7) * 10);
This will most likely print out 7, instead of the expected value of 8.
For more information, see the section on floating point numbers in the PHP manual
Why are you using intval on a floating point number? I agree with you that the output is a little off but it has to do with the relative inprecision of floating point numbers.
Why not just use floatval("19.90"*100) which outputs 1990
I believe the php doc at http://de2.php.net/manual/en/function.intval.php is omitting the fact that intval will not deliver "the integer value" but the integer (that is non-fractional) part of the number. It does not round.

Categories