我可以依赖PHP php.ini精确解决方案来解决浮点问题

Pet*_*ter 28 php floating-point

我在PHP中找到了浮点问题的一些解决方法:

php.ini设置 precision = 14

342349.23 - 341765.07 = 584.15999999992 // floating point problem
Run Code Online (Sandbox Code Playgroud)

php.ini设置,让我们说吧 precision = 8

342349.23 - 341765.07 = 584.16 // voila!
Run Code Online (Sandbox Code Playgroud)

演示:http://codepad.org/r7o086sS

这有多糟糕?

1.如果我只需要精确的2位数计算(金钱),我可以依赖这个解决方案吗?

如果没有,你可以在这个解决方案失败时给我一个明确的例子吗?

编辑:3.哪个php.ini.precision值最适合两位数,钱计算


  • 请注意我不能使用整数计算(浮点数*100 =美分),这太迟了.
  • 我不打算处理高于10 ^ 6的数字
  • 我不需要比较数字

UPDATE

@Baba答案是好的,但他用precision=20,precision=6在他的测试...所以还是我不知道是不是要去工作与否.

请考虑以下事项:

让我们说precision = 8,我唯一要做的就是加法+和减法-

A + B = C

A - B = C

问题1:对于0到999999.99之间的数字,精确解决方法是否会失败,其中A和B是带小数位的数字?如果是这样,请给我一个例子.

简单的测试可以完成这项工作:

// if it fails what if I use 9,10,11 ???
// **how to find when it fails??? **
ini_set('precision', 8); 
for($a=0;$a<999999.99;$a+=0.01) {
  for($b=0;$b<999999.99;$b+=0.01) {
     // mind I don't need to test comparision (round($a-$b,2) == ($a-$b))
     echo ($a + $b).','.($a - $b)." vs ";
     echo round($a + $b, 2).','.round($a - $b, 2)."\n";
  }
}
Run Code Online (Sandbox Code Playgroud)

但显然99999999 * 2是太大的工作,所以我不能运行这个测试

问题2:如何估算/计算精确解决方法何时失败?没有这样疯狂的测试?有没有数学上的*,直接的答案呢?如何计算是否会失败?

*我不需要知道浮点计算的工作原理,但是如果您知道精度和A和B的范围,则解决方法失败


请介意我真的知道分和bcmath是最好的解决方案.但我仍然不确定是否会因为减法和加法而失败

Bab*_*aba 42

介绍

浮点算术被许多人认为是一个深奥的主题.这是相当令人惊讶的,因为浮点在计算机系统中无处不在.大多数小数都没有精确表示为二进制小数,因此有一些舍入.一个好的开始是每个计算机科学家应该知道的浮点运算

问题

问题1

如果我只需要精确的2位数计算(金钱),我可以依赖这个解决方案吗?

答案1

如果你需要精确的2位数, 那么答案是否定的 你不能使用php精度设置来确定一个2位小数,即使你是not going to work on numbers higher than 10^6.

在计算过程中,如果长度小于8,则可能会增加精度长度

问题2

如果没有这个解决方案失败的话,你能给我一个明确的例子吗?

答案2

ini_set('precision', 8); // your precision
$a =  5.88 ; // cost of 1kg
$q = 2.49 ;// User buys 2.49 kg
$b = $a * 0.01 ; // 10% Discount only on first kg ;
echo ($a * $q) - $b;
Run Code Online (Sandbox Code Playgroud)

产量

14.5824 <---- not precise 2 digits calculations even if precision is 8
Run Code Online (Sandbox Code Playgroud)

问题3

哪个php.ini.precision值最适合两位数,钱计算?

答案3

精确度和资金计算是两个不同的事情......使用PHP精度作为财务计算或浮点长度的基础并不是一个好主意

简单测试

为了避免运行一些例子使用起来bcmath,number_format并简单minus

Base

$a = 342349.23;
$b = 341765.07;
Run Code Online (Sandbox Code Playgroud)

Example A

ini_set('precision', 20); // set to 20 
echo $a - $b, PHP_EOL;
echo floatval(round($a - $b, 2)), PHP_EOL;
echo number_format($a - $b, 2), PHP_EOL;
echo bcsub($a, $b, 2), PHP_EOL;
Run Code Online (Sandbox Code Playgroud)

产量

584.15999999997438863
584.15999999999996817    <----- Round having a party 
584.16
584.15  <-------- here is 15 because precision value is 20
Run Code Online (Sandbox Code Playgroud)

Example B

ini_set('precision', 14); // change to  14 
echo $a - $b, PHP_EOL;
echo floatval(round($a - $b, 2)), PHP_EOL;
echo number_format($a - $b, 2), PHP_EOL;
echo bcsub($a, $b, 2), PHP_EOL;
Run Code Online (Sandbox Code Playgroud)

产量

584.15999999997
584.16
584.16
584.16  <-------- at 14 it changed to 16
Run Code Online (Sandbox Code Playgroud)

Example C

ini_set('precision', 6); // change to  6 
echo $a - $b, PHP_EOL;
echo floatval(round($a - $b, 2)), PHP_EOL;
echo number_format($a - $b, 2), PHP_EOL;
echo bcsub($a, $b, 2), PHP_EOL;
Run Code Online (Sandbox Code Playgroud)

产量

584.16
584.16
584.16
584.00  <--- at 6 it changed to 00 
Run Code Online (Sandbox Code Playgroud)

Example D

ini_set('precision', 3); // change to 3
echo $a - $b, PHP_EOL;
echo floatval(round($a - $b, 2)), PHP_EOL;
echo number_format($a - $b, 2), PHP_EOL;
echo bcsub($a, $b, 2), PHP_EOL;
Run Code Online (Sandbox Code Playgroud)

产量 

584
584
584.16   <-------------------------------- They only consistent value 
0.00  <--- at 3 .. everything is gone 
Run Code Online (Sandbox Code Playgroud)

结论

忘记浮点cents然后计算然后再划分为100如果为时已晚只是简单地使用number_format它看起来与我一致.

更新

问题1:对于0到999999.99之间的数字,精确解决方法是否会失败,其中A和B是带小数位的数字?如果是这样,请给我一个例子

形式0999999.99增量的0.01关于99,999,999你的循环的组合可能性9,999,999,800,000,000 我真的不认为有人会想要为你运行这样的测试.

由于浮点是具有有限精度的二进制数,因此尝试设置precision对于确保准确性的影响有限这是一个简单的测试:

ini_set('precision', 8);

$a = 0.19;
$b = 0.16;
$c = 0.01;
$d = 0.01;
$e = 0.01;
$f = 0.01;
$g = 0.01;

$h = $a + $b + $c + $d + $e + $f + $g;

echo "Total: " , $h , PHP_EOL;


$i = $h-$a;
$i = $i-$b;
$i = $i-$c;
$i = $i-$d;
$i = $i-$e;
$i = $i-$f;
$i = $i-$g;

echo $i , PHP_EOL;
Run Code Online (Sandbox Code Playgroud)

产量

Total: 0.4
1.0408341E-17     <--- am sure you would expect 0.00 here ;
Run Code Online (Sandbox Code Playgroud)

尝试

echo round($i,2) , PHP_EOL;
echo number_format($i,2) , PHP_EOL;
Run Code Online (Sandbox Code Playgroud)

产量

0
0.00    <------ still confirms number_format is most accurate to maintain 2 digit 
Run Code Online (Sandbox Code Playgroud)

问题2:如何估算/计算精确解决方法何时失败?没有这样疯狂的测试?有没有数学*,直接的答案呢?如何计算是否会失败?

事实上,基石仍然存在浮点数准确性问题,但对于数学解决方案,你可以看一下

我不需要知道浮点计算的工作原理,但是如果您知道精度和A和B的范围,则解决方法失败

在此输入图像描述

不确定那句话是什么意思:)

  • 这个答案显示了很多努力(我为此向你致敬)。然而,在阅读它之后,您会觉得 PHP 的 `precision` 指令以某种方式影响数学计算,并且在最好的情况下会产生误导,因为它只是字符串转换的参数。如果你有一个数字并且你将它转换为字符串 *before* 你用它做数学运算(`precision` 可以起作用的唯一情况)你可能应该从调整代码的逻辑开始。 (2认同)
  • 非常感谢。真的很棒的答案。我很高兴点击 +500 按钮,因为这正是我想要的。多亏了你,我毫不怀疑在任何可能的情况下都不能选择精确的解决方法。再次感谢。 (2认同)

Álv*_*lez 5

根据文档,precision 指令仅更改将数字转换为字符串时显示的数字:

精度 integer
以浮点数显示的有效位数。

所以它基本上是number_format()money_format()的一个非常复杂的替代方案,除了它具有较少的格式选项并且它可能会遭受一些您可能不知道的其他副作用:

<?php

$_POST['amount'] = '1234567.89';

$amount = floatval($_POST['amount']);
var_dump($amount);

ini_set('precision', 5);
$amount = floatval($_POST['amount']);
var_dump($amount);
Run Code Online (Sandbox Code Playgroud)

...

float(1234567.89)
float(1.2346E+6)
Run Code Online (Sandbox Code Playgroud)

编辑:

我坚持:这个设置不会改变 PHP 用数字进行数学计算的方式。这只是何时更改格式选项的神奇的方式转换从浮点数(即使不是整数!)为字符串。例子:

<?php

ini_set('precision', 2);

$amount = 1000;
$price = 98.76;
$total = $amount*$price;

var_dump($amount, $total);

ini_set('precision', 15);
var_dump($amount, $total);
Run Code Online (Sandbox Code Playgroud)

... 印刷:

int(1000)
float(9.9E+4)
int(1000)
float(98760)
Run Code Online (Sandbox Code Playgroud)

这说明:

  1. 浮点计算不受影响,只是显示改变
  2. 整数在所有情况下都不受影响