ZetCode

PHP 浮点数

最后修改日期:2025 年 4 月 1 日

PHP 使用基于 IEEE 754 的平台相关浮点类型,通常为 64 位双精度浮点数。本指南探讨其属性、精度挑战以及在 PHP 应用程序中有效进行数值运算的最佳实践。

PHP 浮点数概述

本节介绍了 PHP 的浮点数类型,这是一种用于十进制值的关键数值类型。它遵循 IEEE 754 标准,通常是 64 位双精度浮点数,具体取决于系统。了解其范围和精度对于准确计算至关重要。下面的示例演示了 PHP 中浮点数的基本用法及其限制。这些知识有助于开发人员有效地管理浮点数问题。

floatOverview.php
<?php

// Float declarations
$singleFloat = 1.2345678901234567;
$smallFloat = 5.0E-324;
$largeFloat = 1.7976931348623157E308;

echo "Float: $singleFloat\n";
echo "Smallest positive: $smallFloat\n";
echo "Largest: $largeFloat\n";

echo "\nPHP_FLOAT_MIN: " . PHP_FLOAT_MIN . "\n";
echo "PHP_FLOAT_MAX: " . PHP_FLOAT_MAX . "\n";
echo "Size in bytes: " . 8 . "\n"; // Typically 64-bit

// Additional example: Scientific notation
$sciFloat = 1.23E-10;
echo "\nScientific notation: $sciFloat\n";

PHP 的 float 类型是一个浮点数,符合 IEEE 754 标准,在现代系统中通常是 64 位双精度浮点数,尽管其大小可能因平台而异。它提供 15-17 位有效十进制数字,范围从 ±5.0×10⁻³²⁴(PHP_FLOAT_MIN)到 ±1.7976931348623157×10³⁰⁸(PHP_FLOAT_MAX),通常存储在 8 个字节中。

与具有多种数值类型的语言不同,PHP 对所有非整数数字都使用 float,没有明确的十进制类型,从而平衡了简单性和灵活性。这意味着精度问题,例如 0.1 + 0.2,很常见,需要仔细处理。支持科学计数法(例如,1.23E-10),使其易于处理非常小或非常大的值。

精度与舍入

PHP 浮点数的精度可能会由于其二进制表示而导致意外。本节展示了小数分数如何导致错误以及 PHP 如何缓解这些错误。示例使用内置函数有效地控制舍入。掌握这些工具可确保 PHP 脚本中的可靠数值结果。这对于避免细微的计算错误至关重要。

precisionExamples.php
<?php

// Precision demonstration
$a = 0.1;
$b = 0.2;
$sum = $a + $b;

echo "0.1 + 0.2 = $sum\n";
echo "0.1 + 0.2 == 0.3? " . ($sum == 0.3 ? 'true' : 'false') . "\n";
echo "Full precision: " . sprintf("%.17f", $sum) . "\n";

$value = 2.34567;
echo "\nRounding examples:\n";
echo "round($value): " . round($value) . "\n";
echo "floor($value): " . floor($value) . "\n";
echo "ceil($value): " . ceil($value) . "\n";
echo "round to 2 decimals: " . round($value, 2) . "\n";

// Additional example: Pi rounding
$pi = 3.14159;
echo "\nPi to 3 digits: " . round($pi, 3) . "\n";

PHP 浮点数,基于 IEEE 754 的二进制,无法精确表示 0.1 或 0.2 等小数,因此 0.1 + 0.2 等于 0.30000000000000004,而不是 0.3,如使用 sprintf("%.17f") 所示。这些错误会在循环或重复计算中累积,需要在代码中仔细管理精度。

PHP 提供 round 用于最近似值(带有可选精度),floor 用于向下舍入,ceil 用于向上舍入,所有这些都返回浮点数。例如,round(2.34567, 2) 得到 2.35,非常适合显示或控制精度,这与在其他语言中使用基于字符串的替代方法不同。根据需要调整 php.ini 精度设置(默认为 14)或使用格式控制输出。

比较浮点值

由于固有的精度不准确性,在 PHP 中比较浮点数需要谨慎。直接相等性检查通常会失败,因此本节提供了一种更安全的比较方法。它还解决了 PHP 上下文中的特殊值,如 NaNinfinity。该示例通过实际的 PHP 解决方案阐明了这些问题。这确保了您的脚本中可靠的比较逻辑。

floatComparison.php
<?php

function nearlyEqual($a, $b, $epsilon = 1E-10) {
    $absA = abs($a);
    $absB = abs($b);
    $diff = abs($a - $b);

    if ($a == $b) return true;
    if ($a == 0 || $b == 0 || $diff < PHP_FLOAT_EPSILON)
    {
        return $diff < ($epsilon * PHP_FLOAT_EPSILON);
    }

    return $diff / ($absA + $absB) < $epsilon;
}

$x = 0.1 + 0.2;
$y = 0.3;

echo "Direct equality: " . ($x == $y ? 'true' : 'false') . "\n";
echo "NearlyEqual: " . (nearlyEqual($x, $y) ? 'true' : 'false') . "\n";
echo "Difference: " . ($x - $y) . "\n";

$nan = NAN;
$inf = INF;

echo "\nNAN == NAN: " . ($nan == $nan ? 'true' : 'false') . "\n";
echo "is_nan($nan): " . (is_nan($nan) ? 'true' : 'false') . "\n";
echo "INF == INF: " . ($inf == $inf ? 'true' : 'false') . "\n";

// Additional example: Small number comparison
$small = 1E-15;
echo "\nNearlyEqual($small, 0): " . (nearlyEqual($small, 0) ? 'true' : 'false') . "\n";

在 PHP 中,使用 == 进行浮点数比较是不可靠的,因为由于微小的二进制精度误差,0.1 + 0.2 != 0.3,通常会偏差一小部分,例如 5.5E-17。 nearlyEqual 函数在 epsilon (例如,1E-10) 内比较浮点数,使用 PHP_FLOAT_EPSILON 用于接近零的情况,确保了稳健的检查。

特殊值,如 NANINF 需要小心 — NAN != NAN,所以需要 is_nan,而 INF == INF 为真,但需要上下文处理。对于像 1E-15 和 0 这样的小数字,nearlyEqual== 失败的地方成功,使其成为一个实用的解决方案。此方法适应 PHP 的浮点数行为,增强了应用程序之间的比较精度。

用于财务计算的浮点数

由于其二进制性质,PHP 的浮点类型在财务精度方面存在问题。本节将浮点数问题与基于整数的精确解决方法进行对比。示例展示了 PHP 中利息计算和货币舍入的有效性。这些技术可确保 PHP 项目中财务任务的准确性。对于复杂的需求,请考虑使用 BCMath 等扩展。

financialExamples.php
<?php

$principal = 1000.00;
$interestRate = 0.05; // 5%
$years = 10;

// Using floating-point
$futureValueFloat = $principal * pow(1 + $interestRate, $years);
echo "Future value (floating-point): $" . round($futureValueFloat, 2) . "\n";

// Workaround with integer cents
$principalCents = 100000; // $1000.00 in cents
$futureValueCents = round($principalCents * pow(1 + $interestRate, $years));
$futureValue = $futureValueCents / 100;
echo "Accurate future value: $" . number_format($futureValue, 2) . "\n";

$payment = 123.456789;
echo "\nPayment to cents: " . round($payment, 2) . "\n";
echo "Payment rounded up: " . ceil($payment * 100) / 100 . "\n";
echo "Payment rounded down: " . floor($payment * 100) / 100 . "\n";

// Additional example: Tax calculation
$price = 19.99;
$taxRate = 0.08;
$tax = round($price * $taxRate, 2);
echo "\nTax on $$price: $$tax\n";

PHP 的浮点数类型在财务计算中引入了错误,例如在 1000 美元的 5% 的 10 年复合利息中,结果中可能会出现微小的误差。使用美分作为整数(例如,100000 代表 1000 美元)并在计算后转换回结果为精确的 1628.89 美元,避免了浮点数漂移。

round($payment, 2)ceilfloor 这样的函数与美分(例如,12345 美分转换为 123.45 美元)一起使用可确保精确的货币值,这对于会计完整性至关重要。对 19.99 美元的 8% 税额,使用 round 后正好为 1.60 美元,符合现实世界的期望。对于高精度需求,PHP 的 BCMath 扩展提供了完全替代浮点数的方案。

最佳实践

将 PHP 浮点数用于一般数学运算,但切换到整数美分或 BCMath 以获得财务精度。避免使用 == 进行浮点数比较,而是使用带有 epsilon 的 nearlyEqual 以确保可靠性。显式应用 roundceilfloor,并使用 number_format 进行清晰的输出格式。使用 is_nan 检查 NAN,使用 is_infinite 检查 INF 以正确处理边缘情况。这些实践使您的 PHP 数值代码保持稳健且无错误。

资料来源

从以下资源中了解更多信息:PHP 浮点数文档PHP 数学函数,以及 BCMath 文档

作者

我叫 Jan Bodnar,是一位充满激情的程序员,拥有丰富的编程经验。自 2007 年以来,我一直在撰写编程文章。迄今为止,我撰写了 1400 多篇文章和 8 本电子书。我拥有超过十年的编程教学经验。

列出 所有 PHP 教程