【问题标题】:Compare floats in php比较 php 中的浮点数
【发布时间】:2010-06-30 11:51:46
【问题描述】:

我想比较 PHP 中的两个浮点数,就像下面的示例代码:

$a = 0.17;
$b = 1 - 0.83; //0.17
if($a == $b ){
 echo 'a and b are same';
}
else {
 echo 'a and b are not same';
}

在此代码中,它返回 else 条件而不是 if 条件的结果,即使 $a 和 $b 相同。有什么特殊的方法来处理/比较 PHP 中的浮点数吗?

如果是,请帮我解决这个问题。

还是我的服务器配置有问题?

【问题讨论】:

标签: php floating-point


【解决方案1】:

如果你这样做,它们应该是一样的。但请注意,浮点值的一个特征是 似乎 得出相同值的计算实际上不需要完全相同。因此,如果 $a 是文字 .17 和 $b 通过计算到达那里,很可能它们是不同的,尽管它们都显示相同的值。

通常你永远不会像这样比较浮点值的相等性,你需要使用可接受的最小差异:

if (abs(($a-$b)/$b) < 0.00001) {
  echo "same";
}

类似的东西。

【讨论】:

  • 当心!选择固定的 epsilon 是一种不好的方法,因为它看起来很小,当数字很小时,这种比较会在很多精度错误中返回 true。正确的方法是检查相对误差是否小于 epsilon。 abs($a-$b) > abs(($a-$b)/$b)
  • @Alexandru:我知道你的意思,但 PHP 在这方面并不孤单。您需要在这里区分两个用例:向用户显示一个数字。在那种情况下,显示0.10000000000000000555111512312578270211815834045410156 通常是没有意义的,他们更喜欢0.1。并写一个数字,以便可以以完全相同的方式再次读取它。如您所见,它并不像您想象的那么清晰。作为记录,您仍然希望像我展示的那样比较浮点数,因为您可以通过不同的计算得出$a 和$b,这会使它们不同。
  • 仍有一些边缘情况导致此测试失败。例如a=b=0,如果a 是可能的最小非零正值,b 是可能的最小非零负值,则测试将错误地失败。这里有一些很好的信息:floating-point-gui.de/errors/comparison
  • 为什么要在$b 上划分? PHP manual 刚刚做了 if(abs($a-$b) &lt; $epsilon) MySQL manual 也做了同样的事情 HAVING ABS(a - b) &lt;= 0.0001
  • @CaslavSabani:这是相对的,而不是绝对的错误。它仍然坏了(尤其是在$a == $b == 0 时,但它已经比绝对错误更普遍。如果$a 和$b 是数百万,那么你的EPSILON 必须与$a 有很大不同和 $b 接近于 0。有关此问题的更好讨论,请参阅上面的 Dom 链接。
【解决方案2】:

首先阅读红色警告in the manual。你绝不能比较浮点数是否相等。您应该使用 epsilon 技术。

例如:

if (abs($a-$b) < PHP_FLOAT_EPSILON) { … }

其中PHP_FLOAT_EPSILON 是一个常数,代表一个非常小的数字(您必须在 PHP 7.2 之前的旧版本中定义它)

【讨论】:

  • 澄清一下,在这种情况下 EPSILON 是机器 epsilon,大约是 2.2204460492503E-16?而且,这种比较是否适用于任何大小的两个浮点数?
  • @MichaelCordingley 不,EPSILON 这是一个任意的用户定义常量。 PHP 没有内置常量来表示体系结构的特定 epsilon 概念。 (另见get_defined_constants。)
  • PHP_FLOAT_EPSILON 可表示的最小正数 x,因此 x + 1.0 != 1.0。自 PHP 7.2.0 起可用。
  • 这在这种情况下实际上不起作用:$a = 270.10 + 20.10; $b = 290.20; if (abs($a-$b)
  • @NemoXP 因为这些表达式产生不同的数字。 echo $a - $b; /* 5.6843418860808E-14 */ echo PHP_FLOAT_EPSILON; /* 2.2204460492503E-16 */ 问题是您想如何为您的应用程序定义“相等”,数字应该多接近才能被视为相等。
【解决方案3】:

或者尝试使用 bc 数学函数:

<?php
$a = 0.17;
$b = 1 - 0.83; //0.17

echo "$a == $b (core comp oper): ", var_dump($a==$b);
echo "$a == $b (with bc func)  : ", var_dump( bccomp($a, $b, 3)==0 );

结果:

0.17 == 0.17 (core comp oper): bool(false)
0.17 == 0.17 (with bc func)  : bool(true)

【讨论】:

  • 在您使用 bccomp 时,您错过了“比例”,因此您实际上是根据手册比较 0 和 0:php.net/manual/en/function.bccomp.php
  • 我喜欢这个。大多数解决方案似乎都依赖于舍入和丢失精度,但我正在处理具有 12 个精度点的纬度和经度坐标,这似乎可以准确地比较它们而无需调整。
  • 性能怎么样? bccomp() 将字符串作为参数。无论如何,您可以使用 PHP_FLOAT_DIG 作为 scale 参数。
【解决方案4】:

最好使用native PHP comparison:

bccomp($a, $b, 3)
// Third parameter - the optional scale parameter
// is used to set the number of digits after the decimal place
// which will be used in the comparison. 

如果两个操作数相等则返回 0,如果 left_operand 相等则返回 1 大于 right_operand,否则为 -1。

【讨论】:

  • 值得注意的是,bc 函数都是截断的,而不是取整的。例如,您可能期望 bccomp(0.9999999, 1.000000, 2) 返回零,但这将返回 -1。
  • @Cynewulf,对我来说这是一个正确的行为。如果不在你的,使用 PHP_FLOAT_EPSILON
【解决方案5】:

如前所述,在 PHP 中进行浮点比较(无论是等于、大于还是小于)时要非常小心。但是,如果您只对几个有效数字感兴趣,您可以执行以下操作:

$a = round(0.17, 2);
$b = round(1 - 0.83, 2); //0.17
if($a == $b ){
    echo 'a and b are same';
}
else {
    echo 'a and b are not same';
}

使用四舍五入到小数点后 2 位(或 3 或 4)将导致预期结果。

【讨论】:

  • 额外的警告,我不建议在你的代码库中乱扔这样的语句。如果您想做一个松散的浮点比较,请创建一个像 loose_float_compare 这样的方法,这样很明显会发生什么。
  • PHP 的原生 bccomp($a, $b, 2) 优于您的解决方案。在此示例中,2 是精度。您可以将其设置为您想要比较的任意数量的浮点数。
  • @JohnMiller 我不同意你的观点,但默认情况下 bccomp 不可用。它需要启用编译标志或安装扩展。不是核心的一部分。
【解决方案6】:

如果你有浮点值来比较相等,一个简单的方法来避免操作系统、语言、处理器等的内部舍入策略的风险,是比较字符串表示的值。

您可以使用以下任何方法来产生所需的结果:https://3v4l.org/rUrEq

字符串类型转换

if ( (string) $a === (string) $b) { … }

字符串连接

if ('' . $a === '' . $b) { … }

strval 函数

if (strval($a) === strval($b)) { … }

在检查相等性时,字符串表示形式比浮点数少得多。

【讨论】:

  • 或者如果您不想转换原始值,则为 if ( strval($a) === strval($b)) { ... }
  • 好吧,我原来的答案是:if (''.$a === ''.$b) { ... } 但有人编辑了它。所以...
  • @Ekonoval 您能否详细说明您的修改,看来您是在断言 (string) 强制转换操作是通过引用执行的,更改了原始声明?如果是这样的话3v4l.org/Craas
  • @fyrye 是的,我想我错了,这两种方法产生相同的结果。
  • 更新了答案以给出示例用法以及其他编辑的所有示例以及原始示例
【解决方案7】:

这适用于我在 PHP 5.3.27 上。

$payments_total = 123.45;
$order_total = 123.45;

if (round($payments_total, 2) != round($order_total, 2)) {
   // they don't match
}

【讨论】:

    【解决方案8】:

    如果您可以接受的小数点数量有限,则以下方法可以很好地工作(尽管性能比 epsilon 解决方案慢):

    $a = 0.17;
    $b = 1 - 0.83; //0.17
    
    if (number_format($a, 3) == number_format($b, 3)) {
        echo 'a and b are same';
    } else {
        echo 'a and b are not same';
    }
    

    【讨论】:

      【解决方案9】:

      这是比较浮点数或十进制数的解决方案

      //$fd['someVal'] = 2.9;
      //$i for loop variable steps 0.1
      if((string)$fd['someVal']== (string)$i)
      {
          //Equal
      }
      

      将decimal 变量转换为string 就可以了。

      【讨论】:

        【解决方案10】:

        对于 PHP 7.2,您可以使用 PHP_FLOAT_EPSILON (http://php.net/manual/en/reserved.constants.php):

        if(abs($a-$b) < PHP_FLOAT_EPSILON){
           echo 'a and b are same';
        }
        

        【讨论】:

        • 很好的解决方案。但是:1- 需要更新 PHP 7.2,对于现有的大型/旧系统,并非每个人都可以轻松完成 2- 这仅适用于 == 和 !=,但不适用于 &gt;、&gt;=、&lt;、&lt;=
        【解决方案11】:

        如果你这样写它可能会起作用,所以我想你已经为这个问题简化了它。 (保持问题简单明了通常是一件非常好的事情。)

        但在这种情况下,我认为一个结果是计算,一个结果是常数。

        这违反了浮点编程的基本规则:永远不要进行相等比较。

        这样做的原因有点微妙1,但重要的是要记住它们通常不起作用(具有讽刺意味的是,对于整数值除外)并且替代方法是模糊比较行:

        if abs(a - y) < epsilon
        



        1. 主要问题之一涉及我们在程序中编写数字的方式。我们将它们写为十进制字符串,因此我们编写的大多数分数都没有精确的机器表示。它们没有精确的有限形式,因为它们以二进制形式重复。每个机器分数都是 x/2n 形式的有理数。现在,常量是十进制的,每个十进制常量都是 x/(2n * 5m) 形式的有理数。 5m 数字是奇数,因此它们中的任何一个都没有 2n 因数。只有当 m == 0 时,分数的二进制和十进制扩展才有有限表示。所以,1.25 是准确的,因为它是 5/(22*50) 但 0.1 不是因为它是 1/(20*5 1)。事实上,在 1.01 .. 1.99 系列中,只有 3 个数字可以精确表示:1.25、1.50 和 1.75。

        【讨论】:

        • DigitalRoss 很难理解您评论中的几个术语,但是是的,它的信息量很大。我将用谷歌搜索这些术语。谢谢:)
        • 如果您每次都将结果四舍五入并且在几个有效数字范围内,那么对浮点数进行比较是否足够安全?换句话说round($float, 3) == round($other, 3)
        【解决方案12】:

        比较浮点数是否相等有一个简单的 O(n) 算法。

        您必须将每个浮点值转换为字符串,然后使用整数比较运算符从每个浮点字符串表示的左侧开始比较每个数字。 PHP 会在比较之前将每个索引位置的数字自动转换为整数。第一个大于另一个的数字将打破循环并将其所属的浮点数声明为两者中的较大者。平均而言,将进行 1/2 * n 次比较。对于彼此相等的浮点数,将进行 n 次比较。这是算法的最坏情况。最好的情况是每个浮点数的第一个数字不同,只进行一次比较。

        您不能对原始浮点值使用整数比较运算符来生成有用的结果。此类操作的结果没有意义,因为您不是在比较整数。您违反了每个运算符的域,这会产生无意义的结果。这也适用于 delta 比较。

        将整数比较运算符用于它们的设计目的:比较整数。

        简化的解决方案:

        <?php
        
        function getRand(){
          return ( ((float)mt_rand()) / ((float) mt_getrandmax()) );
         }
        
         $a = 10.0 * getRand();
         $b = 10.0 * getRand();
        
         settype($a,'string');
         settype($b,'string');
        
         for($idx = 0;$idx<strlen($a);$idx++){
          if($a[$idx] > $b[$idx]){
           echo "{$a} is greater than {$b}.<br>";
           break;
          }
          else{
           echo "{$b} is greater than {$a}.<br>";
           break;
          }
         }
        
        ?>
        

        【讨论】:

          【解决方案13】:

          2019

          TL;DR

          在下面使用我的函数,比如if(cmpFloats($a, '==', $b)) { ... }

          • 易于读/写/更改:cmpFloats($a, '&lt;=', $b) vs bccomp($a, $b) &lt;= -1
          • 不需要依赖项。
          • 适用于任何 PHP 版本。
          • 适用于负数。
          • 适用于您能想象到的最长小数。
          • 缺点:比 bccomp() 稍慢

          总结

          我会揭开谜底的。

          $a = 0.17;
          $b = 1 - 0.83;// 0.17 (output)
                        // but actual value internally is: 0.17000000000000003996802888650563545525074005126953125
          if($a == $b) {
              echo 'same';
          } else {
              echo 'different';
          }
          // Output: different
          

          因此,如果您尝试以下方法,它将是相等的:

          if($b == 0.17000000000000003) {
              echo 'same';
          } else {
              echo 'different';
          }
          // Output "same"
          

          如何获取float的实际值?

          $b = 1 - 0.83;
          echo $b;// 0.17
          echo number_format($a, 100);// 0.1700000000000000399680288865056354552507400512695312500000000000000000000000000000000000000000000000
          

          如何比较?

          1. 使用BC Math functions。 (你仍然会得到很多 wtf-aha-gotcha 时刻)
          2. 您可以尝试@Gladhon 的回答,使用 PHP_FLOAT_EPSILON (PHP 7.2)。
          3. 如果将浮点数与 == 和 != 进行比较,您可以将它们类型转换为字符串,它应该可以正常工作:

          使用字符串进行类型转换:

          $b = 1 - 0.83;
          if((string)$b === (string)0.17) {
              echo 'if';
          } else {
              echo 'else';
          }
          // it will output "if"
          

          或者使用number_format()进行类型转换:

          $b = 1 - 0.83;
          if(number_format($b, 3) === number_format(0.17, 3)) {
              echo 'if';
          } else {
              echo 'else';
          }
          // it will output "if"
          

          警告:

          避免涉及以数学方式处理浮点数(乘法、除法等)然后进行比较的解决方案,大多数情况下它们会解决一些问题并引入其他问题。


          建议的解决方案

          我创建了纯 PHP 函数(不需要依赖项/库/扩展)。检查并比较每个数字作为字符串。也适用于负数。

          /**
           * Compare numbers (floats, int, string), this function will compare them safely
           * @param Float|Int|String  $a         (required) Left operand
           * @param String            $operation (required) Operator, which can be: "==", "!=", ">", ">=", "<" or "<="
           * @param Float|Int|String  $b         (required) Right operand
           * @param Int               $decimals  (optional) Number of decimals to compare
           * @return boolean                     Return true if operation against operands is matching, otherwise return false
           * @throws Exception                   Throws exception error if passed invalid operator or decimal
           */
          function cmpFloats($a, $operation, $b, $decimals = 15) {
              if($decimals < 0) {
                  throw new Exception('Invalid $decimals ' . $decimals . '.');
              }
              if(!in_array($operation, ['==', '!=', '>', '>=', '<', '<='])) {
                  throw new Exception('Invalid $operation ' . $operation . '.');
              }
          
              $aInt = (int)$a;
              $bInt = (int)$b;
          
              $aIntLen = strlen((string)$aInt);
              $bIntLen = strlen((string)$bInt);
          
              // We'll not used number_format because it inaccurate with very long numbers, instead will use str_pad and manipulate it as string
              $aStr = (string)$a;//number_format($a, $decimals, '.', '');
              $bStr = (string)$b;//number_format($b, $decimals, '.', '');
          
              // If passed null, empty or false, then it will be empty string. So change it to 0
              if($aStr === '') {
                  $aStr = '0';
              }
              if($bStr === '') {
                  $bStr = '0';
              }
          
              if(strpos($aStr, '.') === false) {
                  $aStr .= '.';
              }
              if(strpos($bStr, '.') === false) {
                  $bStr .= '.';
              }
          
              $aIsNegative = strpos($aStr, '-') !== false;
              $bIsNegative = strpos($bStr, '-') !== false;
          
              // Append 0s to the right
              $aStr = str_pad($aStr, ($aIsNegative ? 1 : 0) + $aIntLen + 1 + $decimals, '0', STR_PAD_RIGHT);
              $bStr = str_pad($bStr, ($bIsNegative ? 1 : 0) + $bIntLen + 1 + $decimals, '0', STR_PAD_RIGHT);
          
              // If $decimals are less than the existing float, truncate
              $aStr = substr($aStr, 0, ($aIsNegative ? 1 : 0) + $aIntLen + 1 + $decimals);
              $bStr = substr($bStr, 0, ($bIsNegative ? 1 : 0) + $bIntLen + 1 + $decimals);
          
              $aDotPos = strpos($aStr, '.');
              $bDotPos = strpos($bStr, '.');
          
              // Get just the decimal without the int
              $aDecStr = substr($aStr, $aDotPos + 1, $decimals);
              $bDecStr = substr($bStr, $bDotPos + 1, $decimals);
          
              $aDecLen = strlen($aDecStr);
              //$bDecLen = strlen($bDecStr);
          
              // To match 0.* against -0.*
              $isBothZeroInts = $aInt == 0 && $bInt == 0;
          
              if($operation === '==') {
                  return $aStr === $bStr ||
                         $isBothZeroInts && $aDecStr === $bDecStr;
              } else if($operation === '!=') {
                  return $aStr !== $bStr ||
                         $isBothZeroInts && $aDecStr !== $bDecStr;
              } else if($operation === '>') {
                  if($aInt > $bInt) {
                      return true;
                  } else if($aInt < $bInt) {
                      return false;
                  } else {// Ints equal, check decimals
                      if($aDecStr === $bDecStr) {
                          return false;
                      } else {
                          for($i = 0; $i < $aDecLen; ++$i) {
                              $aD = (int)$aDecStr[$i];
                              $bD = (int)$bDecStr[$i];
                              if($aD > $bD) {
                                  return true;
                              } else if($aD < $bD) {
                                  return false;
                              }
                          }
                      }
                  }
              } else if($operation === '>=') {
                  if($aInt > $bInt ||
                     $aStr === $bStr ||
                     $isBothZeroInts && $aDecStr === $bDecStr) {
                      return true;
                  } else if($aInt < $bInt) {
                      return false;
                  } else {// Ints equal, check decimals
                      if($aDecStr === $bDecStr) {// Decimals also equal
                          return true;
                      } else {
                          for($i = 0; $i < $aDecLen; ++$i) {
                              $aD = (int)$aDecStr[$i];
                              $bD = (int)$bDecStr[$i];
                              if($aD > $bD) {
                                  return true;
                              } else if($aD < $bD) {
                                  return false;
                              }
                          }
                      }
                  }
              } else if($operation === '<') {
                  if($aInt < $bInt) {
                      return true;
                  } else if($aInt > $bInt) {
                      return false;
                  } else {// Ints equal, check decimals
                      if($aDecStr === $bDecStr) {
                          return false;
                      } else {
                          for($i = 0; $i < $aDecLen; ++$i) {
                              $aD = (int)$aDecStr[$i];
                              $bD = (int)$bDecStr[$i];
                              if($aD < $bD) {
                                  return true;
                              } else if($aD > $bD) {
                                  return false;
                              }
                          }
                      }
                  }
              } else if($operation === '<=') {
                  if($aInt < $bInt || 
                     $aStr === $bStr ||
                     $isBothZeroInts && $aDecStr === $bDecStr) {
                      return true;
                  } else if($aInt > $bInt) {
                      return false;
                  } else {// Ints equal, check decimals
                      if($aDecStr === $bDecStr) {// Decimals also equal
                          return true;
                      } else {
                          for($i = 0; $i < $aDecLen; ++$i) {
                              $aD = (int)$aDecStr[$i];
                              $bD = (int)$bDecStr[$i];
                              if($aD < $bD) {
                                  return true;
                              } else if($aD > $bD) {
                                  return false;
                              }
                          }
                      }
                  }
              }
          }
          

          $a = 1 - 0.83;// 0.17
          $b = 0.17;
          if($a == $b) {
              echo 'same';
          } else {
              echo 'different';
          }
          // Output: different (wrong)
          
          if(cmpFloats($a, '==', $b)) {
              echo 'same';
          } else {
              echo 'different';
          }
          // Output: same (correct)
          

          【讨论】:

          • 删除这个。,,,
          • @QuolonelQuestions 为什么会这样?
          【解决方案14】:

          来自@evilReiko 的函数存在如下一些错误:

          cmpFloats(-0.1, '==', 0.1); // Expected: false, actual: true
          cmpFloats(-0.1, '<', 0.1); // Expected: true, actual: false
          cmpFloats(-4, '<', -3); // Expected: true, actual: true
          cmpFloats(-5.004, '<', -5.003); // Expected: true, actual: false
          

          在我的函数中我已经修复了这些错误,但无论如何在某些情况下这个函数会返回错误的答案:

          cmpFloats(0.0000001, '==', -0.0000001); // Expected: false, actual: true
          cmpFloats(843994202.303411, '<', 843994202.303413); // Expected: true, actual: false
          cmpFloats(843994202.303413, '>', 843994202.303411); // Expected: true, actual: false
          

          比较浮动的固定函数

          function cmpFloats($a, $operation, $b, $decimals = 15)
          {
              if ($decimals < 0) {
                  throw new Exception('Invalid $decimals ' . $decimals . '.');
              }
              if (!in_array($operation, ['==', '!=', '>', '>=', '<', '<='])) {
                  throw new Exception('Invalid $operation ' . $operation . '.');
              }
          
              $aInt = (int)$a;
              $bInt = (int)$b;
          
              $aIntLen = strlen((string)$aInt);
              $bIntLen = strlen((string)$bInt);
          
              // We'll not used number_format because it inaccurate with very long numbers, instead will use str_pad and manipulate it as string
              $aStr = (string)$a;//number_format($a, $decimals, '.', '');
              $bStr = (string)$b;//number_format($b, $decimals, '.', '');
          
              // If passed null, empty or false, then it will be empty string. So change it to 0
              if ($aStr === '') {
                  $aStr = '0';
              }
              if ($bStr === '') {
                  $bStr = '0';
              }
          
              if (strpos($aStr, '.') === false) {
                  $aStr .= '.';
              }
              if (strpos($bStr, '.') === false) {
                  $bStr .= '.';
              }
          
              $aIsNegative = strpos($aStr, '-') !== false;
              $bIsNegative = strpos($bStr, '-') !== false;
          
              // Append 0s to the right
              $aStr = str_pad($aStr, ($aIsNegative ? 1 : 0) + $aIntLen + 1 + $decimals, '0', STR_PAD_RIGHT);
              $bStr = str_pad($bStr, ($bIsNegative ? 1 : 0) + $bIntLen + 1 + $decimals, '0', STR_PAD_RIGHT);
          
              // If $decimals are less than the existing float, truncate
              $aStr = substr($aStr, 0, ($aIsNegative ? 1 : 0) + $aIntLen + 1 + $decimals);
              $bStr = substr($bStr, 0, ($bIsNegative ? 1 : 0) + $bIntLen + 1 + $decimals);
          
              $aDotPos = strpos($aStr, '.');
              $bDotPos = strpos($bStr, '.');
          
              // Get just the decimal without the int
              $aDecStr = substr($aStr, $aDotPos + 1, $decimals);
              $bDecStr = substr($bStr, $bDotPos + 1, $decimals);
          
              $aDecLen = strlen($aDecStr);
              //$bDecLen = strlen($bDecStr);
          
              // To match 0.* against -0.*
              $isBothZeroInts = $aInt == 0 && $bInt == 0;
          
              if ($operation === '==') {
                  return $aStr === $bStr ||
                      ($isBothZeroInts && $aDecStr === $bDecStr && $aIsNegative === $bIsNegative);
              } elseif ($operation === '!=') {
                  return $aStr !== $bStr ||
                      $isBothZeroInts && $aDecStr !== $bDecStr;
              } elseif ($operation === '>') {
                  if ($aInt > $bInt) {
                      return true;
                  } elseif ($aInt < $bInt) {
                      return false;
                  } else {// Ints equal, check decimals
                      if ($aIsNegative !== $bIsNegative) {
                          return (!$aIsNegative && $bIsNegative);
                      }
          
                      if ($aDecStr === $bDecStr) {
                          return false;
                      } else {
                          for ($i = 0; $i < $aDecLen; ++$i) {
                              $aD = (int)$aDecStr[$i];
                              $bD = (int)$bDecStr[$i];
          
                              if ($aIsNegative && $bIsNegative) {
                                  if ($aD < $bD) {
                                      return true;
                                  } elseif ($aD > $bD) {
                                      return false;
                                  }
                              } else {
                                  if ($aD > $bD) {
                                      return true;
                                  } elseif ($aD < $bD) {
                                      return false;
                                  }
                              }
                          }
                      }
                  }
              } elseif ($operation === '>=') {
                  if ($aInt > $bInt ||
                      $aStr === $bStr ||
                      $isBothZeroInts && $aDecStr === $bDecStr) {
                      return true;
                  } elseif ($aInt < $bInt) {
                      return false;
                  } else {// Ints equal, check decimals
                      if ($aIsNegative !== $bIsNegative) {
                          return (!$aIsNegative && $bIsNegative);
                      }
          
                      if ($aDecStr === $bDecStr) {// Decimals also equal
                          return true;
                      } else {
                          for ($i = 0; $i < $aDecLen; ++$i) {
                              $aD = (int)$aDecStr[$i];
                              $bD = (int)$bDecStr[$i];
          
                              if ($aIsNegative && $bIsNegative) {
                                  if ($aD < $bD) {
                                      return true;
                                  } elseif ($aD > $bD) {
                                      return false;
                                  }
                              } else {
                                  if ($aD > $bD) {
                                      return true;
                                  } elseif ($aD < $bD) {
                                      return false;
                                  }
                              }
                          }
                      }
                  }
              } elseif ($operation === '<') {
                  if ($aInt < $bInt) {
                      return true;
                  } elseif ($aInt > $bInt) {
                      return false;
                  } else {// Ints equal, check decimals
                      if ($aIsNegative !== $bIsNegative) {
                          return ($aIsNegative && !$bIsNegative);
                      }
          
                      if ($aDecStr === $bDecStr) {
                          return false;
                      } else {
                          for ($i = 0; $i < $aDecLen; ++$i) {
                              $aD = (int)$aDecStr[$i];
                              $bD = (int)$bDecStr[$i];
          
                              if ($aIsNegative && $bIsNegative) {
                                  if ($aD > $bD) {
                                      return true;
                                  } elseif ($aD < $bD) {
                                      return false;
                                  }
                              } else {
                                  if ($aD < $bD) {
                                      return true;
                                  } elseif ($aD > $bD) {
                                      return false;
                                  }
                              }
                          }
                      }
                  }
              } elseif ($operation === '<=') {
                  if ($aInt < $bInt ||
                      $aStr === $bStr ||
                      $isBothZeroInts && $aDecStr === $bDecStr) {
                      return true;
                  } elseif ($aInt > $bInt) {
                      return false;
                  } else {// Ints equal, check decimals
                      if ($aIsNegative !== $bIsNegative) {
                          return ($aIsNegative && !$bIsNegative);
                      }
          
                      if ($aDecStr === $bDecStr) {// Decimals also equal
                          return true;
                      } else {
                          for ($i = 0; $i < $aDecLen; ++$i) {
                              $aD = (int)$aDecStr[$i];
                              $bD = (int)$bDecStr[$i];
          
                              if ($aIsNegative && $bIsNegative) {
                                  if ($aD > $bD) {
                                      return true;
                                  } elseif ($aD < $bD) {
                                      return false;
                                  }
                              } else {
                                  if ($aD < $bD) {
                                      return true;
                                  } elseif ($aD > $bD) {
                                      return false;
                                  }
                              }
                          }
                      }
                  }
              }
          }
          

          回答您的问题

          $a = 1 - 0.83;// 0.17
          $b = 0.17;
          if($a == $b) {
              echo 'same';
          } else {
              echo 'different';
          }
          // Output: different (wrong)
          
          if(cmpFloats($a, '==', $b)) {
              echo 'same';
          } else {
              echo 'different';
          }
          // Output: same (correct)
          

          【讨论】:

            【解决方案15】:

            简单回答:

            if( floatval( (string) $a ) >= floatval( (string) $b) ) { //do something }
            

            【讨论】:

            • 如果 PHP 能够正确评估浮点值,人们就没有办法在这里比较浮点值。
            【解决方案16】:

            使用和决定要使用的小数位数非常有用。 number_format

                        $a = 0.17;
                        $b = 1 - 0.83;  //0.17
                        $dec = 2;
                        if(number_format($a,$dec) == number_format($b,$dec))
                        {
                            echo 'a and b are same';
                        }
                        else 
                        {
                            echo 'a and b are not same';
                        }
                        //output : a and b are same
            

            【讨论】:

              【解决方案17】:

              这是我个人库中用于处理浮点数的有用类。您可以根据自己的喜好调整它并将任何您喜欢的解决方案插入到类方法中:-)。

              /**
               * A class for dealing with PHP floating point values.
               * 
               * @author Anthony E. Rutledge
               * @version 12-06-2018
               */
              final class Float extends Number
              {
                  // PHP 7.4 allows for property type hints!
              
                  private const LESS_THAN = -1;
                  private const EQUAL = 0;
                  private const GREATER_THAN = 1;
              
                  public function __construct()
                  {
              
                  }
              
                  /**
                   * Determines if a value is an float.
                   * 
                   * @param mixed $value
                   * @return bool
                   */
                  public function isFloat($value): bool
                  {
                      return is_float($value);
                  }
              
                  /**
                   * A method that tests to see if two float values are equal.
                   * 
                   * @param float $y1
                   * @param float $y2
                   * @return bool
                   */
                  public function equals(float $y1, float $y2): bool
                  {
                      return (string) $y1 === (string) $y2;
                  }
              
                  /**
                   * A method that tests to see if two float values are not equal.
                   * 
                   * @param float $y1
                   * @param float $y2
                   * @return bool
                   */
                  public function isNotEqual(float $y1, float $y2): bool
                  {
                      return !$this->equals($y1, $y2);
                  }
              
                  /**
                   * Gets the bccomp result.
                   * 
                   * @param float $y1
                   * @param float $y2
                   * @return int
                   */
                  private function getBccompResult(float $y1, float $y2): int
                  {
                      $leftOperand = (string) $y1;
                      $rightOperand = (string) $y2;
              
                      // You should check the format of the float before using it.
              
                      return bccomp($leftOperand, $rightOperand);
                  }
              
                  /**
                   * A method that tests to see if y1 is less than y2.
                   * 
                   * @param float $y1
                   * @param float $y2
                   * @return bool
                   */
                  public function isLess(float $y1, float $y2): bool
                  {
                      return ($this->getBccompResult($y1, $y2) === self::LESS_THAN);
                  }
              
                  /**
                   * A method that tests to see if y1 is less than or equal to y2.
                   * 
                   * @param float $y1
                   * @param float $y2
                   * @return bool
                   */
                  public function isLessOrEqual(float $y1, float $y2): bool
                  {
                      $bccompResult = $this->getBccompResult($y1, $y2);
                      return ($bccompResult === self::LESS_THAN || $bccompResult === self::EQUALS);
                  }
              
                  /**
                   * A method that tests to see if y1 is greater than y2.
                   * 
                   * @param float $y1
                   * @param float $y2
                   * @return bool
                   */
                  public function isGreater(float $y1, float $y2): bool
                  {
                      return ($this->getBccompResult($y1, $y2) === self::GREATER_THAN);
                  }
              
                  /**
                   * A method that tests to see if y1 is greater than or equal to y2.
                   * 
                   * @param float $y1
                   * @param float $y2
                   * @return bool
                   */
                  public function isGreaterOrEqual(float $y1, float $y2): bool
                  {
                      $bccompResult = $this->getBccompResult($y1, $y2);
                      return ($bccompResult === self::GREATER_THAN || $bccompResult === self::EQUALS);
                  }
              
                  /**
                   * Returns a valid PHP float value, casting if necessary.
                   * 
                   * @param mixed $value
                   * @return float
                   *
                   * @throws InvalidArgumentException
                   * @throws UnexpectedValueException
                   */
                  public function getFloat($value): float
                  {
                      if (! (is_string($value) || is_int($value) || is_bool($value))) {
                          throw new InvalidArgumentException("$value should not be converted to float!");
                      }
              
                      if ($this->isFloat($value)) {
                          return $value;
                      }
              
                      $newValue = (float) $value;
              
                      if ($this->isNan($newValue)) {
                          throw new UnexpectedValueException("The value $value was converted to NaN!");
                      }
              
                      if (!$this->isNumber($newValue)) {
                          throw new UnexpectedValueException("The value $value was converted to something non-numeric!");
                      }
              
                      if (!$this->isFLoat($newValue)) {
                          throw new UnexpectedValueException("The value $value was not converted to a floating point value!");
                      }
              
                      return $newValue;
                  }
              }
              ?>
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2017-02-15
                • 2011-10-23
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多