PHP浮点数使用的最佳实践

6
我阅读了有关在PHP中使用浮点类型的浮点数指南。答案是使用BC Math扩展。使用字符串可以将浮点数表示为精确类型,并防止浮点数和整数问题。
同时,我没有在Github上找到任何使用BC Math扩展的好例子,也没有在这个网站上找到。有什么干净的方法来强制PHP使用字符串,如何评估它们?
特别是,使用BC Math扩展与MySQL DECIMAL数据类型的最佳方式是什么?
我的测试示例使用PHP 5.4.10,正确答案为:0.2999999999996
<?php
bcscale(13);

$a = '0.3';
$b = '0.0000000000004';

echo $a-$b; // 0.3
echo '<br />';
echo bcsub($a, $b); // 0.2999999999996
echo '<hr />';

$a = "0.3";
$b = "0.0000000000004";

echo $a-$b; // 0.3
echo '<br />';
echo bcsub($a, $b); // 0.2999999999996
echo '<hr />';

$a = 0.3;
$b = 0.0000000000004;

echo $a-$b; // 0.3
echo '<br />';
echo bcsub($a, $b); // 0.3000000000000
echo '<hr />';

$a = '0.3';
$b = '0.0000000000004' + 0;

echo $a-$b; // 0.3
echo '<br />';
echo bcsub($a, $b); // 0.3000000000000
echo '<hr />';

$a = (string) 0.3;
$b = (string) 0.0000000000004;

echo $a-$b; // 0.3
echo '<br />';
echo bcsub($a, $b); // 0.3000000000000
echo '<hr />';

$a = strval(0.3);
$b = strval(0.0000000000004);

echo $a-$b; // 0.3
echo '<br />';
echo bcsub($a, $b); // 0.3000000000000
?>

1
嗯,第一和第二种情况有什么问题吗?如果你想知道“最佳”选项,单引号''比双引号""更好,因为如果你使用双引号,PHP将花费额外的时间填充变量名和转义序列。 - Dave
谢谢您的快速回答,Dave。使用这些单引号就可以解决问题了。在数据可能丢失之前,我如何确保我拥有正确类型的值。例如,从MySQL中获取行时。 - Mike
@Dave 这在任何最近的 PHP 版本中都不是真的。请阅读此文章:http://nikic.github.com/2012/01/09/Disproving-the-Single-Quotes-Performance-Myth.html。 - Maerlyn
1
@Maerlyn 我不知道这些统计数据,但没关系。看起来我所说的关于那篇文章的话是完全正确的。PHP会做额外的工作,尽管时间差异微不足道且在编译时完成。单引号是最好的选择,就目前而言,“最好”的选项就是这个。回到正题:Mike:根据文档(http://php.net/manual/en/function.mysql-fetch-row.php),你将从mysql函数中获得字符串(而不是数字)。所以这不应该是一个问题。 - Dave
谢谢 Dave,这让我有信心 :) - Mike
1个回答

4

最好的方法不是使用BC Math,而是开始使用GMP。只需要看一下基准测试就知道了。GMP比BC Math快多达30倍。

我使用DECIMAL(12,0)和DECIMAL(27,0),当数字大于10^19时,php表现很差。演示:

//PHP Version 5.3.10,  FreeBSD 8.2-RELEASE amd64

$a = pow(10, 18);
var_dump($a, $a > ($a - 1), ($a - 1) > $a, ($a - 1) == $a, ($a - 1) === $a);
// int(1000000000000000000)
// bool(true)
// bool(false)
// bool(false)
// bool(false)

$a = pow(10, 19);
var_dump($a, $a > ($a - 1), ($a - 1) > $a, ($a - 1) == $a, ($a - 1) === $a);

// double(1.0E+19)
// bool(false)
// bool(false)
// bool(true)
// bool(true)

我制作了一个Decimal类,这里是一个简单的实现代码。之后你可以使用本机php数学操作或BC数学等方式实现相同的接口。是的,函数调用和对象创建将增加开销,但我们编写的代码是供其他开发人员阅读的,不是吗?
附注:
来自PHP Secure Communications团队的人有他们自己的实现-Math_BigInteger,它涵盖了所有潜在的需求,但对我的任务来说过于庞大。
更新:
自从PHP 5.6开始,GMP实现了内部运算符重载,因此gmp资源可以像常规数字一样使用(大部分情况下)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接