【问题标题】:Determine if $x is divisible evenly by $y in PHP确定 $x 是否可以在 PHP 中被 $y 整除
【发布时间】:2014-03-21 19:47:30
【问题描述】:

我只是想知道 $x 是否可以被 $y 整除。例如,假设:

$x = 70;
$y = .1;

我尝试的第一件事是:

$x % $y

这似乎在两个数字都是整数时有效,但如果不是整数则失败,并且如果 $y 是小于 1 的小数,则返回“除以零”错误,所以我尝试了:

fmod($x,$y)

返回同样令人困惑的结果,“0.099999999999996”。

php.net 声明fmod():

返回被除数 (x) 除以除数 (y) 的浮点余数

根据我的计算器70 / .1 = 700。这意味着余数为 0。有人可以解释我做错了什么吗?

【问题讨论】:

  • 并非所有值都有精确的浮点表示:因此它不是“[完美]均匀”(整数除外),而是“足够接近”。

标签: php floating-point modulus


【解决方案1】:

一种解决方案是进行正常的除法,然后将值与下一个整数进行比较。如果结果是该整数或非常接近该整数,则结果是可整除的:

$x = 70;
$y = .1;

$evenlyDivisable = abs(($x / $y) - round($x / $y, 0)) < 0.0001;

这会减去两个数字并检查绝对差是否小于某个舍入误差。这是比较浮点数的常用方法,因为根据您获得浮点数的方式,表示可能会有所不同:

php> 0.1 + 0.1 + 0.1 == 0.3
bool(false)
php> serialize(.3)
'd:0.29999999999999999;'
php> serialize(0.1 + 0.1 + 0.1)
'd:0.30000000000000004;'

看这个演示:

php> $x = 10;
int(10)
php> $y = .1;
double(0.1)
php> abs(($x / $y) - round($x / $y, 0)) < 0.0001;
bool(true)
php> $y = .15;
double(0.15)
php> abs(($x / $y) - round($x / $y, 0)) < 0.0001;
bool(false)

【讨论】:

  • 我对这个解决方案很感兴趣。一个问题,为什么要使用减法和&lt; 0.0001 而不是简单的($x / $y) == round($x / $y)
  • @billynoah 我修改了我的答案,解释了为什么要像这样比较浮点数。
  • 谢谢蒂姆,我想我明白了,但是在什么情况下我在上面评论中建议的方法会返回与您不同的结果?
  • @Eric - 我可能错了,但我认为这就像在比较中添加另一个 0 一样简单,即 &lt; 0.00001 应该这样做,因为您的数字有四位小数。似乎应该有一种方法可以轻松地为 any 数字执行此操作,但我还没有找到比这更好的解决方案。
  • @billynoah:这修复了一种情况,但没有描述此测试何时有效以及何时无效。这应该被认为是软件中的一个严重问题:您有一些看起来可以工作的代码,但有时会出错,您不知道它何时会出错以及何时工作。
【解决方案2】:

.1 在二进制浮点中没有精确的表示,这就是导致错误结果的原因。您可以将它们乘以足够大的 10 次方,使它们成为整数,然后使用 %,然后再转换回来。这依赖于它们没有足够大的差异,乘以 10 的幂会导致其中一个溢出/丢失精度。像这样:

$x = 70;
$y = .1;
$factor = 1.0;
while($y*$factor != (int)($y*$factor)){$factor*=10;}
echo ($x*$factor), "\n";
echo ($y*$factor), "\n";
echo (double)(($x*$factor) % ($y*$factor))/$factor;

【讨论】:

  • 数字基于用户在表单中的输入。它们可以是任何数字。
  • 我修复了它,以便唯一基于 int 的大小限制是 y 的最终大小,而不是 x 和 y 之间的差异。允许的差异仅受浮点数的限制。
  • +1 表示“.1 在二进制浮点中没有精确表示”(参见 Patriot 错误:sydney.edu.au/engineering/it/~alum/patriot_bug.html
【解决方案3】:

bitbucket 中有一个纯数学库:https://bitbucket.org/zdenekdrahos/bn-php

那么解决办法就是:

php > require_once 'bn-php/autoload.php';
php > $eval = new \BN\Expression\ExpressionEvaluator();
php > $operators = new \BN\Expression\OperatorsFactory();
php > $eval->setOperators($operators->getOperators(array('%')));
php > echo $eval->evaluate('70 % 0.1'); // 0
0.00000000000000000000

在 php5.3 上测试

学分:http://www.php.net/manual/en/function.bcmod.php#111276

【讨论】:

    【解决方案4】:

    浮点表示因机器而异。幸好有标准。 PHP 通常使用 IEEE 754 双精度格式进行浮点表示,这是最常见的标准之一。 See here 了解更多信息。话虽如此,请查看this calculator 以更好地了解为什么。至于如何,我喜欢 Tim 的解决方案,尤其是在您处理用户输入时。

    【讨论】:

      【解决方案5】:

      正如您所说,当它是一个整数时,使用模运算符可以正常工作,所以为什么不将它设置为对整数进行操作。就我而言,我需要用 0.25 检查整除性:

      $input = 5.251
      $x = round($input, 3); // round in case $input had more decimal places
      $y = .25;
      $result = ($x * 1000) % ($y * 1000);
      

      在你的情况下:

      $input = 70.12
      $x = round($input, 2);
      $y = .1;
      $result = ($x * 100) % ($y * 100);
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-11-12
        • 2011-02-17
        • 1970-01-01
        • 1970-01-01
        • 2012-12-14
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多