I need to simulate a ∞ in PHP.
So that min(∞,$number) is always $number.
I suppose that, for integers, you could use PHP_INT_MAX , the following code :
var_dump(PHP_INT_MAX);
Gives this output, on my machine :
int 2147483647
But you have to be careful ; see Integer overflow (quoting) :
If PHP encounters a number beyond the
bounds of the integer type, it will
be interpreted as a float instead.
Also, an operation which results in a
number beyond the bounds of the
integer type will return a float
instead.
And, from the Floating point numbers documentation page :
The size of a float is
platform-dependent, although a maximum
of ~1.8e308 with a precision of
roughly 14 decimal digits is a common
value (the 64 bit IEEE format).
Considering the integer overflow, and depending on your case, using this kind of value might be a better (?) solution...
Use the constant PHP_INT_MAX.
http://php.net/manual/en/language.types.integer.php
You could potentially use the PHP_INT_MAX constant (click for PHP manual docs).
However, you may want to think about whether you really need to use it - it seems like a bit of an odd request.
PHP actually has a predefined constant for "infinity": INF. This isn't true infinity, but is essentially the largest float value possible. On 64-bit systems, the largest float is roughly equal to 1.8e308, so this is considered to be equal to infinity.
$inf = INF;
var_dump(min($inf,PHP_INT_MAX)); // outputs int(9223372036854775807)
var_dump(min($inf,1.79e308)); // outputs float(1.79E+308)
var_dump(min($inf,1.799e308)); // outputs float(INF)
var_dump(min($inf,1.8e308)); // outputs float(INF)
var_dump($inf === 1.8e308); // outputs bool(true)
Note, any number with a value larger than the maximum float value will be cast to INF. So therefore if we do, var_dump($inf === 1e50000);, this will also output true even though the maximum float is less than this.
I suppose, assuming this is an integer, you could use PHP_INT_MAX constant.
min($number, $number + 1) ??
In Perl you can use
$INF = 9**9E9;
which is larger than any value you can store in IEEE floating point numbers. And that really works as intended: any non-infinite number will be smaller than $INF:
$N < $INF
is true for any "normal" number $N.
Maybe you use it in PHP too?
min($number,$number) is always $number (also true for max() of course).
If your only concern is comparison function then yes, you can use array(), it will be always larger then any number
like
echo min(array(), 9999999999999999);
or
if (array() > 9999999999999999) {
echo 'array won';
} else {
echo 'number won';
}
Related
I made this function. It seemed it's working but when it comes to 20 digits number, the return value was 19. I'm wondering why this problem happen..
My function
function sumDigits($n) {
return strlen($n);
}
echo sumDigits(100); //3
echo sumDigits(1000); //4
echo sumDigits(12345); //5
echo sumDigits(1000000000); //10
echo sumDigits(145874589632); //12
echo sumDigits(0); //1
echo sumDigits(12345698745254856320); //19 <-- Why not 20?
Can you please somebody explain for me?
Thank you so much.
First, I would point out that the name of your function is misleading, as you are not really summing the values of the digits, but are counting the digits. So I would call your function countDigits instead of sumDigits.
The reason why it doesn't work for large numbers, is that the string representation will switch to scientific notation, so you're actually getting the length of "1.2345698745255E+19" not of "12345698745254856320"
If you are only interested in integers, you will get better results with the logarithm:
function countDigits($n) {
return ceil(log10($n));
}
For numbers that have decimals, there is no good solution, since the precision of 64-bit floating pointing point numbers is limited to about 16 significant digits, so even if you provide more digits, the trailing decimals will be dropped -- this has nothing to do with your function, but with the precision of the number itself. For instance, you'll find that these two literals are equal:
if (1.123456789123456789123456789 == 1.12345678912345678) echo "equal";
Because you function parameter is an integer, exceeding the limit.
If you dump it, it actually shows the following:
1.2345698745255E+19 - which is 19 letters.
If you would do the following, it will return 20 - mind the quotes, which declares the input as string.
echo sumDigits("12345698745254856320"); //19 <-- Why not 20? -> now will be 20
As per documentation, strlen() expects a string so a cast happens. With default settings you get 1.2345698745255E+19:
var_dump((string)12345698745254856320);
string(19) "1.2345698745255E+19"
The root issue is that PHP converts your integer literal to float because it exceeds PHP_INT_MAX so it cannot be represented as integer:
var_dump(12345698745254856320, PHP_INT_MAX);
In 64-bit PHP:
float(1.2345698745254857E+19)
int(9223372036854775807)
You could change display settings to avoid E notation but you've already lost precision at this point.
Computer languages that store integers as a fixed amount of bytes do not allow arbitrary precision. Your best chance is to switch to strings:
var_dump('12345698745254856320', strlen('12345698745254856320'));
string(20) "12345698745254856320"
int(20)
... and optionally use an arbitrary precision library such as BCMath or GMP if you need actual maths.
It's also important to consider that this kind of issues is sometimes a symptom that your input data is not really meant to be an integer but just a very long digit-only string.
I've got this problem on Windows 10 with both php 7 and 7.1 and also on raspbian with PHP 7.0.33
When I try to cast a large double (a miliseconds timestamp) to int I get a totally wrong result. Example:
$a = 1512298800000.0;
echo intval($a);
The output is: 470311808
Any suggestion on how to troubleshoot this?
Based on intval() Manual, it cleary states:
Return Values
The integer value of var on success, or 0 on failure. Empty arrays return 0, non-empty arrays return 1.
The maximum value depends on the system. 32 bit systems have a maximum signed integer range of -2147483648 to 2147483647. So for example on such a system, intval('1000000000000') will return 2147483647. The maximum signed integer value for 64 bit systems is 9223372036854775807.
Strings will most likely return 0 although this depends on the leftmost characters of the string. The common rules of integer casting apply.
And
Notes
Note:The base parameter has no effect unless the var parameter is a string.
So basically it seems you are using 32-bit system and value got overflowed from the range of integer.
PHP documentation here states that:
If PHP encounters a number beyond the bounds of the integer type, it will be interpreted as a float instead. Also, an operation which results in a number beyond the bounds of the integer type will return a float instead.
But what about an operation which results in a number less than PHP_INT_MAX ?
See this code snippet as an example:
$max_int = 2**31-1 ; // 2147483647
var_dump(PHP_INT_MAX === $max_int); // false
As you can see, even when an operation results in a valid int value PHP seems to cast the result into float
var_dump(PHP_INT_MAX === (int) $max_int) // true
My questions:
Does PHP interpreter cast the result into float before making any calculations?
Shouldn't PHP calculate the result and then sets the type accordingly? (Makes sense right?)
Edit:
PHP version: 7.2.1 32-bit
OS: Windows: 10 x64
I'm using XAMPP
When calculating $max_int = 2**31-1 the engine does this in steps:
$tmp = 2**31;
$max_int = $tmp-1
Here $tmp is bigger than maximum integer value and converted to a floatng point number. In consequence there is an float subtraction, resulting in a float. Since it had been float it has to stay float.
Can someone please explain what is happening in the last 2 cases?
$x=PHP_INT_MAX;
var_dump($x); // int(9223372036854775807) no problem
var_dump($x+1); // float(9.2233720368548E+18) value is cast to float still no problem
var_dump($x+1-1); // float(9.2233720368548E+18) still okay
var_dump((int)($x+1-1)); // int(-9223372036854775808) negative value?!!
var_dump($x+1-$x); // float(0) zero?!!!!!!!!!!!
According to the PHP manual:
PHP_INT_MAX is the maximum integer size.
If PHP encounters a number beyond the bounds of the integer type, it will be interpreted as a float instead. Also, an operation which
results in a number beyond the bounds of the integer type will return
a float instead.
So the first 2 dumps are okay. The third dump gives the expected result, but why on earth does the last two dumps give a negative value and zero?
Two different things are combining: floating point rounding and integer overflow.
var_dump($x+1-$x); // float(0) zero?!!!!!!!!!!!
This is because floating point arithmetic is not exact (it can't be, as real numbers can be infinite). $x and $x+1 are so close together that they are rounded to the same floating point value, so float($x+1)==float($x). Now you know why $x+1-$x==0.
Combined with integer overflow you get this result:
var_dump((int)($x+1-1)); // int(-9223372036854775808) negative value?!!
Because of the reason above $x+1-1==float(9223372036854775808). When casting this to an int it overflows and becomes a negative value.
9223372036854775808==2^63, which in 64-bit signed integers becomes -2^63
About the 4th test:
Integers are signed. Internally they are stored in such a way, that when you add one, the value wraps and turns into the lowest negative integer. In case of a signed nibble of 4 bits, this would look like:
0111 (=7) + 0001 (=1) = 1000 (= -7)
This explains partially how a number can suddenly become a very large negative number.
But you would expect x+1-1 to be x, but it is probably an issue with significant digits. PHP stores only 14 significant digits, which is not enough to store a number like 9223372036854775807 exactly without losing information.
I'll explain using the 5th test:
A floating point can contain much bigger numbers than an integer, but only with a limited number of significant digits. That's why it looks like 9.2233720368548E+18 instead of 9223372036854800000 or 9223372036854775808.
Since low numbers like 1 are outside of the significant digits of the value, I think 9.2233720368548E+18 + 1 is still 9.2233720368548E+18. That also means that 9.2233720368548E+18 + 1 - 9.2233720368548E+18 is 0, because the +1 just didn't have any effect.
I think this is actually the explanation for both fenomena.
The weird thing is that you would normally not have to know about any of these implementation details, especially in a scripting language like PHP, but in tests like this, they may surface anyway and sometimes cause weird behavior.
Also, you might want to read The PHP floating point precision is wrong by default. It contains a good explanation of other rounding errors that might occur using floats, and it will give you a deeper understanding of how floats work in general and in PHP specifically.
This has to do with how the computer stores numbers. In this case, signed numbers, meaning they can both be negative and positive.
For the sake of it I will use an 8-bit example. The computer can store numbers from -128 to 127.
-128 is represented as 1000 0000 while 127 is presented as 0111 1111. The first bit represents if the number is negative or positive. Every bit is worth 2^n, were n is the position from right to left. For 127 it is 2^0 + 2^1 + 2^2 + 2^3 + 2^4 + 2^5 + 2^6 + 2^7 = 127. The opposite for a negative value.
var_dump($x); // int(9223372036854775807) no problem
No problems because we are just setting it to the maximum value.
var_dump($x+1); // float(9.2233720368548E+18) value is cast to float still no problem
var_dump($x+1-1); // float(9.2233720368548E+18) still okay
Here there are problems, +1 and +1-1 equals to the same thing. Which they should not, problem does occur here, a bit more subtle.
var_dump((int)($x+1-1)); // int(-9223372036854775808) negative value?!!
In this case 0111 (...) +1 equals to 1000 (...) which is the absolute value, notice the 8 in the end of the number, compared to the 7 for the positive value. Overflow flag in CPU is set.
var_dump($x+1-$x); // float(0) zero?!!!!!!!!!!!
$x+1 forces a cast to float, then subtracts the same value (because of rounding errors with float numbers), $x+1 is the same as $x, so $x-$x = 0.
Hope this clarifies some.
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.