PHP Math
Rounding, base conversion, random numbers and float pitfalls.
Key PointsNever compute money with floats — `0.1 + 0.2 != 0.3`.
Rounding
PHP
<?php
echo floor(3.7), " ", ceil(3.2), " ", round(3.5), " ", round(3.4), "\n";
echo round(3.14159, 2), " ", round(1234.5678, -2), "\n";
echo abs(-5), " ", max(1, 5, 3), " ", min(1, 5, 3), "\n";
echo intdiv(10, 3), " ", 10 % 3, "\n";
echo fmod(10.5, 3), "\n";
echo (int)3.9, "\n";Output
3 4 4 3
3.14 1200
5 5 1
3 1
1.5
3round rounds half away from zero, not banker's rounding. Financial calculations needing half-to-even must implement it.Float pitfalls
PHP
<?php
var_dump(0.1 + 0.2);
var_dump(0.1 + 0.2 == 0.3);
// 正确比较:用误差范围
var_dump(abs((0.1 + 0.2) - 0.3) < PHP_FLOAT_EPSILON);
// 正确做法:整数分
$price = 1999; // 19.99 元
$qty = 3;
echo $price * $qty, "\n";
echo number_format($price * $qty / 100, 2), "\n";Output
float(0.30000000000000004)
bool(false)
bool(true)
5997
59.970.1 + 0.2 is 0.30000000000000004 — inherent to IEEE 754, not a PHP bug. **Store money as integer cents or use bcmath.**Arbitrary precision
PHP
<?php
echo bcadd("0.1", "0.2", 2), "\n";
echo bcsub("1.00", "0.99", 2), "\n";
echo bcmul("19.99", "3", 2), "\n";
echo bcdiv("1", "3", 10), "\n";
echo bcpow("2", "10"), "\n";
var_dump(bccomp("1.00", "1.0", 2));Output
0.30
0.01
59.97
0.3333333333
1024
int(0)bcmath takes and returns strings, with a scale argument. Never pass floats — precision is already lost before the call.
Base conversion
PHP
<?php
echo decbin(10), " ", decoct(10), " ", dechex(255), "\n";
echo bindec("1010"), " ", octdec("12"), " ", hexdec("ff"), "\n";
echo base_convert("ff", 16, 2), "\n";
echo base_convert("zz", 36, 10), "\n";Output
1010 12 ff
10 10 255
11111111
1295Randomness
| Function | Use | Secure |
|---|---|---|
| random_int | 整数随机 | 是 |
| random_bytes | 随机字节 | 是 |
| mt_rand | 一般随机(抽奖展示、排序) | 否 |
| shuffle | 打乱数组 | 否 |
| array_rand | 随机取键 | 否 |
PHP
<?php
echo random_int(1, 6), "\n";
echo strlen(bin2hex(random_bytes(16))), "\n";
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$token = "";
for ($i = 0; $i < 8; $i++) {
$token .= $chars[random_int(0, strlen($chars) - 1)];
}
echo strlen($token), "\n";Output
6
32
8**Tokens, captchas and reset links must use
random_int / random_bytes.** mt_rand is predictable — using it for tokens means no security at all.取整与舍入
PHP
<?php
echo floor(3.7), " ", ceil(3.2), " ", round(3.5), " ", round(3.4), "\n";
echo round(3.14159, 2), " ", round(1234.5678, -2), "\n";
echo abs(-5), " ", max(1, 5, 3), " ", min(1, 5, 3), "\n";
echo intdiv(10, 3), " ", 10 % 3, "\n";
echo fmod(10.5, 3), "\n";
echo (int)3.9, "\n";Output
3 4 4 3
3.14 1200
5 5 1
3 1
1.5
3round(3.5) 得 4,round(2.5) 得 3——PHP 用「四舍五入远离零」,不是银行家的「四舍六入五成双」。财务计算要成双舍入得自己实现。浮点陷阱
PHP
<?php
var_dump(0.1 + 0.2);
var_dump(0.1 + 0.2 == 0.3);
// 正确比较:用误差范围
var_dump(abs((0.1 + 0.2) - 0.3) < PHP_FLOAT_EPSILON);
// 正确做法:整数分
$price = 1999; // 19.99 元
$qty = 3;
echo $price * $qty, "\n";
echo number_format($price * $qty / 100, 2), "\n";Output
float(0.30000000000000004)
bool(false)
bool(true)
5997
59.970.1 + 0.2 是 0.30000000000000004,这是 IEEE 754 双精度的固有问题,不是 PHP 的 bug。**金额一律用整数分或 bcmath**。高精度计算
PHP
<?php
echo bcadd("0.1", "0.2", 2), "\n";
echo bcsub("1.00", "0.99", 2), "\n";
echo bcmul("19.99", "3", 2), "\n";
echo bcdiv("1", "3", 10), "\n";
echo bcpow("2", "10"), "\n";
var_dump(bccomp("1.00", "1.0", 2));Output
0.30
0.01
59.97
0.3333333333
1024
int(0)bcmath 的所有函数都收字符串、返回字符串,第三个参数是小数位数。别传浮点进去——传的时候精度已经丢了。
进制转换
PHP
<?php
echo decbin(10), " ", decoct(10), " ", dechex(255), "\n";
echo bindec("1010"), " ", octdec("12"), " ", hexdec("ff"), "\n";
echo base_convert("ff", 16, 2), "\n";
echo base_convert("zz", 36, 10), "\n";Output
1010 12 ff
10 10 255
11111111
1295随机数
| 函数 | 用途 | 密码学安全 |
|---|---|---|
| random_int | 整数随机 | 是 |
| random_bytes | 随机字节 | 是 |
| mt_rand | 一般随机(抽奖展示、排序) | 否 |
| shuffle | 打乱数组 | 否 |
| array_rand | 随机取键 | 否 |
PHP
<?php
echo random_int(1, 6), "\n";
echo strlen(bin2hex(random_bytes(16))), "\n";
$chars = "abcdefghijklmnopqrstuvwxyz0123456789";
$token = "";
for ($i = 0; $i < 8; $i++) {
$token .= $chars[random_int(0, strlen($chars) - 1)];
}
echo strlen($token), "\n";Output
6
32
8**token、验证码、密码重置链接必须用
random_int / random_bytes**。mt_rand 的输出可预测,用它生成 token 等于没有安全措施。