数值字面量中的前导零表示“这是八进制”。但不要混淆:字符串中的前导零不是。因此
$x = 0123; // 83
$y = "0123" + 0 // 123
一个 int 是集合 ℤ = {..., -2, -1, 0, 1, 2, ...} 中的一个数。
Int 可以用十进制(基数 10)、十六进制(基数 16)、八进制(基数 8)或二进制(基数 2)表示法指定。 否定运算符 可用于表示负 int。
要使用八进制表示法,请在数字前面加上一个 0
(零)。从 PHP 8.1.0 开始,八进制表示法也可以在前面加上 0o
或 0O
。要使用十六进制表示法,请在数字前面加上 0x
。要使用二进制表示法,请在数字前面加上 0b
。
从 PHP 7.4.0 开始,整数字面量可以在数字之间包含下划线 (_
),以提高字面量的可读性。这些下划线将被 PHP 的扫描器移除。
示例 #1 整数字面量
<?php
$a = 1234; // 十进制数
$a = 0123; // 八进制数(等效于十进制的 83)
$a = 0o123; // 八进制数(从 PHP 8.1.0 开始)
$a = 0x1A; // 十六进制数(等效于十进制的 26)
$a = 0b11111111; // 二进制数(等效于十进制的 255)
$a = 1_234_567; // 十进制数(从 PHP 7.4.0 开始)
?>
形式上,int 字面量的结构从 PHP 8.1.0 开始(之前不允许使用 0o
或 0O
八进制前缀,并且在 PHP 7.4.0 之前不允许使用下划线)
decimal : [1-9][0-9]*(_[0-9]+)* | 0 hexadecimal : 0[xX][0-9a-fA-F]+(_[0-9a-fA-F]+)* octal : 0[oO]?[0-7]+(_[0-7]+)* binary : 0[bB][01]+(_[01]+)* integer : decimal | hexadecimal | octal | binary
一个 int 的大小与平台相关,尽管通常最大值为大约二十亿(即 32 位有符号)。64 位平台通常的最大值为大约 9E18。PHP 不支持无符号 int。可以使用常量 PHP_INT_SIZE
确定 int 的大小,可以使用常量 PHP_INT_MAX
确定最大值,可以使用常量 PHP_INT_MIN
确定最小值。
如果 PHP 遇到超出 int 类型范围的数字,它将被解释为 float。同样,导致超出 int 类型范围的数字的操作将返回 float。
示例 #2 在 32 位系统上整数溢出
<?php
$large_number = 2147483647;
var_dump($large_number); // int(2147483647)
$large_number = 2147483648;
var_dump($large_number); // float(2147483648)
$million = 1000000;
$large_number = 50000 * $million;
var_dump($large_number); // float(50000000000)
?>
示例 #3 在 64 位系统上整数溢出
<?php
$large_number = 9223372036854775807;
var_dump($large_number); // int(9223372036854775807)
$large_number = 9223372036854775808;
var_dump($large_number); // float(9.2233720368548E+18)
$million = 1000000;
$large_number = 50000000000000 * $million;
var_dump($large_number); // float(5.0E+19)
?>
PHP 中没有 int 除法运算符,要实现此功能,请使用 intdiv() 函数。1/2
会产生 float 0.5
。该值可以转换为 int 以将其舍入为零,或者 round() 函数可以更精确地控制舍入。
<?php
var_dump(25/7); // float(3.5714285714286)
var_dump((int) (25/7)); // int(3)
var_dump(round(25/7)); // float(4)
?>
要将值显式转换为 int,请使用 (int)
或 (integer)
强制转换。但是,在大多数情况下,不需要强制转换,因为如果运算符、函数或控制结构需要 int 参数,则该值将被自动转换。还可以使用 intval() 函数将值转换为 int。
如果将 resource 转换为 int,则结果将是 PHP 在运行时分配给该 resource 的唯一资源号。
另请参见 类型转换。
从 float 转换为 int 时,该数字将被舍入为零。从 PHP 8.1.0 开始,当将非整数 float 隐式转换为丢失精度的 int 时,会发出弃用通知。
<?php
function foo($value): int {
return $value;
}
var_dump(foo(8.1)); // 从 PHP 8.1.0 开始,"Deprecated: Implicit conversion from float 8.1 to int loses precision"
var_dump(foo(8.1)); // PHP 8.1.0 之前为 8
var_dump(foo(8.0)); // 在两种情况下都为 8
var_dump((int)8.1); // 在两种情况下都为 8
var_dump(intval(8.1)); // 在两种情况下都为 8
?>
如果浮点数超过了 int 的边界(通常在 32 位平台上为 +/- 2.15e+9 = 2^31
,在 64 位平台上为 +/- 9.22e+18 = 2^63
),则结果是未定义的,因为 float 没有足够的精度来提供精确的 int 结果。在这种情况下,不会发出警告,甚至不会发出通知!
注意:
NaN 和 Infinity 在转换为 int 时始终为零。
如果字符串是 数值 或者以数值开头,那么它将解析为相应的整数值,否则它将转换为零 (0
)。
将其他类型转换为 int 的行为是未定义的。不要依赖任何观察到的行为,因为它们可能会在未经通知的情况下更改。
数值字面量中的前导零表示“这是八进制”。但不要混淆:字符串中的前导零不是。因此
$x = 0123; // 83
$y = "0123" + 0 // 123
以下是一些将“点分”IP 地址转换为 LONG int 的技巧,反之亦然。这非常有用,因为在数据库表中访问 IP 地址,如果将它存储为 BIGINT 而不是字符,会快得多。
IP 到 BIGINT
<?php
$ipArr = explode('.',$_SERVER['REMOTE_ADDR']);
$ip = $ipArr[0] * 0x1000000
+ $ipArr[1] * 0x10000
+ $ipArr[2] * 0x100
+ $ipArr[3]
;
?>
从数据库中读取的 IP 作为 BIGINT 转换为点分形式
请记住,PHP 整数运算符是 INTEGER -- 而不是 long。此外,由于 PHP 中没有整数除法,因此通过位移操作,我们节省了几个 S-L-O-W floor(<division>)。我们必须对 $ipArr[0] 使用 floor(/),因为虽然 $ipVal 存储为 long 值,但 $ipVal >> 24 将对 $ipVal 的截断整数进行操作!但是,$ipVint 是一个很好的整数,所以
我们可以享受位移操作。
<?php
$ipVal = $row['client_IP'];
$ipArr = array(0 =>
floor( $ipVal / 0x1000000) );
$ipVint = $ipVal-($ipArr[0]*0x1000000); // for clarity
$ipArr[1] = ($ipVint & 0xFF0000) >> 16;
$ipArr[2] = ($ipVint & 0xFF00 ) >> 8;
$ipArr[3] = $ipVint & 0xFF;
$ipDotted = implode('.', $ipArr);
?>
注意浮点数到整型转换的溢出
<?php
// 您可能期望这些
var_dump(0x7fffffffffffffff); // int(9223372036854775807)
var_dump(0x7fffffffffffffff + 1); // float(9.2233720368548E+18)
var_dump((int)(0x7fffffffffffffff + 1)); // int(9223372036854775807)
var_dump(0x7fffffffffffffff + 1 > 0); // bool(true)
var_dump((int)(0x7fffffffffffffff + 1) > 0); // bool(true)
var_dump((int)'9223372036854775807'); // int(9223372036854775807)
var_dump(9223372036854775808); // float(9.2233720368548E+18)
var_dump((int)'9223372036854775808'); // int(9223372036854775807)
var_dump((int)9223372036854775808); // int(9223372036854775807)
// 但实际上,它喜欢这些
var_dump(0x7fffffffffffffff); // int(9223372036854775807)
var_dump(0x7fffffffffffffff + 1); // float(9.2233720368548E+18)
var_dump((int)(0x7fffffffffffffff + 1)); // int(-9223372036854775808) <-----
var_dump(0x7fffffffffffffff + 1 > 0); // bool(true)
var_dump((int)(0x7fffffffffffffff + 1) > 0); // bool(false) <-----
var_dump((int)'9223372036854775807'); // int(9223372036854775807)
var_dump(9223372036854775808); // float(9.2233720368548E+18)
var_dump((int)'9223372036854775808'); // int(9223372036854775807)
var_dump((int)9223372036854775808); // int(-9223372036854775808) <-----
?>
当您尝试将它与零进行比较或从另一个值(例如金钱)中减去它时,这些溢出会很危险。
关于“PHP 不支持无符号整数”部分,这在使用与 PHP_INT_MIN 匹配的有符号整数的硬编码最小值时,经常会导致很大的混淆。
<?php
// 64 位示例
var_dump(PHP_INT_MIN);
var_dump(-9223372036854775808);
var_dump(PHP_INT_MIN === -9223372036854775808);
// int(-9223372036854775808)
// float(-9.223372036854776E+18)
// bool(false)
?>
尽管在视觉上,我输入了与 PHP_INT_MIN 输出相同的数值 `-9223372036854775808`,但语言解析器只将其理解为两个表达式,其中一个否定运算符后跟 `9223372036854775808`。该值超过了整数的最大值,并被提升为浮点数。尽管过去曾建议建立一个钩子来专门查找此值,但这比看起来更难。标记器无法将否定运算符和整数评估为一个标记。此外,还需要处理二进制、八进制和十六进制字面量。
<?php
var_dump(-9223372036854775808); // 字面量十进制
var_dump(-0x8000000000000000); // 字面量十六进制
var_dump(-0b1000000000000000000000000000000000000000000000000000000000000000); // 字面量二进制
var_dump(-01000000000000000000000); // 字面量八进制
?>
如果需要硬编码最小值,请使用 `PHP_INT_MIN`。它专门为此边缘情况而引入。另一种方法是编写 `-9223372036854775807 - 1`。
-------------------------------------------------------------------------
问题
var_dump((int) 010); // 输出 8
var_dump((int) "010"); // 输出 10
第一个是八进制表示法,所以输出是正确的。但是将 "010" 转换为整数时,它也应该输出 8 吗?
--------------------------------------------------------------------------
回答
使用 (int) 将值强制转换为整数始终会转换为默认基数,即 10。
以这种方式将字符串强制转换为数字不会考虑 PHP 中格式化整数值的多种方式(以 0 开头的表示 8 进制,以 "0x" 开头的表示 16 进制,以 "0b" 开头的表示 2 进制)。它只会查看字符串中的前几个字符并将其转换为 10 进制整数。前导零将被剥离,因为它们在数值中没有意义,因此您最终会得到 (int)"010" 的十进制值 10。
使用 (int)010 在基数之间转换整数时,将考虑格式化整数的各种方式。像 010 中的前导零表示该数字是八进制表示法,使用 (int)010 将将其转换为 10 进制的十进制值 8。
这类似于您如何使用 0x10 以十六进制 (16 进制) 表示法写入。使用 (int)0x10 将将其转换为 10 进制的十进制值 16,而使用 (int)"0x10" 最终将得到十进制值 0:由于 "x" 不是数值,因此后面的任何内容都将被忽略。
如果您想将字符串 "010" 解释为八进制值,则需要指示 PHP 执行此操作。intval("010", 8) 将解释该数字为 8 进制而不是默认的 10 进制,并且您最终将得到十进制值 8。您也可以使用 octdec("010") 将八进制字符串转换为十进制值 8。另一个选择是使用 base_convert("010", 8, 10) 明确地将数字 "010" 从 8 进制转换为 10 进制,但是此函数将返回字符串 "8" 而不是整数 8。
将字符串强制转换为整数遵循 intval 函数使用的相同逻辑
返回 var 的整数值,使用指定的基数进行转换(默认值为 10 进制)。
intval 允许指定不同的基数作为第二个参数,而直接强制转换操作则不,因此使用 (int) 始终会将字符串视为 10 进制。
php > var_export((int) "010");
10
php > var_export(intval("010"));
10
php > var_export(intval("010", 8));
8
为了在某些函数中强制正确使用 32 位无符号整数,只需在处理它们之前添加 "+0"。
例如
echo(dechex("2724838310"));
将打印 '7FFFFFFF'
但它应该打印 'A269BBA6'
添加 "+0" 后,php 将正确处理 32 位无符号整数
正确地
echo(dechex("2724838310"+0));
将打印 'A269BBA6'
使用大数的模运算时要小心,它会将浮点参数强制转换为整数,并可能返回错误结果。例如
<?php
$i = 6887129852;
echo "i=$i\n";
echo "i%36=".($i%36)."\n";
echo "alternative i%36=".($i-floor($i/36)*36)."\n";
?>
将输出
i=6.88713E+009
i%36=-24
alternative i%36=20
仅当输入以数字开头时,转换为整数才有效
(int) "5txt" // 将输出整数 5
(int) "before5txt" // 将输出整数 0
(int) "53txt" // 将输出整数 53
(int) "53txt534text" // 将输出整数 53