比较php中的浮动


156

我想比较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条件。有没有什么特殊的方法来处理/比较PHP中的浮点数?$a$b

如果是,请帮助我解决此问题。

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


我懂了a and b are same。这是您的完整代码吗?
Pekka 2010年

什么版本的?这对我来说可以。
gblazex 2010年

@Andrey可能是这样,因为实际情况可能比引用的示例更复杂。为什么不将其添加为答案?
Pekka 2010年

2
您阅读floating-point标签说明了吗?stackoverflow.com/tags/floating-point/info使用浮点数时,在任何编程语言中都可能会遇到这种情况。参见例如stackoverflow.com/questions/588004/is-javascripts-math-broken
Piskvor在

Answers:


232

如果您这样做,他们应该是相同的。但是请注意,浮点值的一个特征是看起来可能导致相同值的计算实际上不必相同。因此,如果$a是文字.17$b通过计算到达该文字,则尽管它们都显示相同的值,但很可能它们是不同的。

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

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

这样的事情。


21
谨防!选择固定的epsilon是不好的方法,因为它看起来很小,而当数字较小时,此比较将在很多精度误差下返回true。正确的方法是检查相对误差是否小于ε。abs($a-$b)>abs(($a-$b)/$b)
Piet Bijl 2013年

1
@Alexandru:我知道您的意思,但是PHP在这方面并不孤单。您需要在此处区分两个用例:向用户显示数字。在这种情况下,显示0.10000000000000000555111512312578270211815834045410156通常是毫无意义的,他们宁愿选择0.1。并写一个数字,以便可以完全相同的方式再次读取它。如您所见,它不像您想象的那么清晰。出于记录目的,您仍然希望像我显示的那样比较浮点数,因为您可以得出$a$b通过不同的计算来使它们有所不同。
2014年

2
在某些极端情况下,此测试失败。例如,a=b=0并且如果a是可能的最小非零正值并且b是可能的最小非零负值,则测试将错误地失败。此处提供一些有用的信息:float-point-gui.de/errors/comparison
Dom 2013年

13
为什么要分开$b?在PHP手册只是做if(abs($a-$b) < $epsilon) MySQL手册也做了同样的HAVING ABS(a - b) <= 0.0001
会计师م

1
@CaslavSabani:这是相对的,不是绝对的错误。它仍然是坏的(尤其是when $a == $b == 0,但是它已经比绝对错误要普遍得多。如果$aand $b处于百万级,那么您EPSILON将不得不与if $a$b接近某处非常不同0。请参阅上方的Dom链接,以更好地讨论这一点。
乔伊

64

阅读手册中的红色警告请先。您绝不能比较浮点数是否相等。您应该使用epsilon技术。

例如:

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

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


2
为了澄清,在这种情况下EPSILON是机器epsilon,大约是2.2204460492503E-16吗?而且,此比较对两个大小的浮点都有效吗?
Michael Cordingley

1
@MichaelCordingley不,EPSILON这是用户定义的任意常量。PHP没有内置常数来表示体系结构的epsilon特定概念。(另请参见get_defined_constants。)
主教

5
PHP_FLOAT_EPSILON最小可表示的正数x,因此x + 1.0!= 1.0。自PHP 7.2.0起可用。
Code4R7

2
在这种情况下,这实际上不起作用:$ a = 270.10 + 20.10; $ b = 290.20; 如果(abs($ a- $ b)<PHP_FLOAT_EPSILON){回显'相同'; }
NemoXP '19

@NemoXP,因为这些表达式产生不同的数字。echo $a - $b; /* 5.6843418860808E-14 */ echo PHP_FLOAT_EPSILON; /* 2.2204460492503E-16 */问题是您想如何为应用程序定义“相等”,应将数字接近多少才能视为相等。
安德烈

28

或尝试使用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)

2
在使用bccomp时,您错过了“标度”,因此您实际上是根据手册将0与0进行比较:php.net/manual/en/function.bccomp.php
stefancarlton

我喜欢这个 大多数解决方案似乎都依赖于舍入和丢失精度,但是我正在处理12点精度的纬度和经度坐标,这似乎可以准确地比较它们而无需进行调整。
Rikaelus

性能如何?bccomp()将字符串作为参数。无论如何,您都可以使用PHP_FLOAT_DIGscale参数。
Code4R7

19

如前所述,在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';
}

使用舍入到小数点后两位(或3或4)将产生预期的结果。


1
额外的警告是,我不建议您使用此类语句来乱码您的代码库。如果要进行松散的浮点比较,请使用类似loose_float_compare这样的方法,这样可以很明显地看到发生了什么。
Michael Butler '18

PHP的本机bccomp($a, $b, 2)优于您的解决方案。在此示例中,2是精度。您可以将其设置为要比较的任意数量的浮点数。
John Miller

@JohnMiller我不同意您的意见,但是默认情况下bccomp不可用。它要求启用编译标志或安装扩展。不属于核心。
Michael Butler

17

最好使用本机PHP比较

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大于right_operand,则返回1,否则返回-1。


10

如果您有浮点值要与相等值进行比较,那么避免操作系统,语言,处理器等内部舍入策略的风险的一种简单方法是比较字符串表示形式值。

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

字符串类型转换

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

字符串串联

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

strval函数

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

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


或if(strval($ a)=== strval($ b)){…}如果您不想转换原始值
Ekonoval

好吧,我的原始答案是:if(''。$ a ===''。$ b){…}但有人编辑了它。所以...
Ame Nomade

1
@Ekonoval您能否详细说明您的修改,看来您是在断言强制转换(string)操作是通过引用执行的,因此更改了原始声明?如果是这样,那么情况并非如此 3v4l.org/Craas
fyrye

@fyrye是的,我想我错了,两种方法都产生相同的结果。
Ekonoval

更新了答案以提供用法示例以及其他所有修改示例以及原始内容
fyrye

4

如果您可以接受的小数点数量有限,那么以下方法会很好地工作(尽管其性能比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';
}

4

这对我适用于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
}


2

如果您以这种方式编写它可能会起作用,那么我想您已经针对问题简化了它。(使问题简单明了通常是一件好事。)

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

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

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

if abs(a - y) < epsilon



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


DigitalRoss在您的评论中很难理解几个术语,但是可以,它非常有用。我将用谷歌搜索这些术语。谢谢:)
Santosh Sonarikar 2012年

如果您每次都舍入结果并且在几个有效数字以内,那么对浮点数进行比较是否足够安全?换句话说round($float, 3) == round($other, 3)
Michael Butler

2

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

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

decimal变量强制转换为string,您就可以了。


1

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

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

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

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

简化解决方案:

<?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;
  }
 }

?>

1

2019年

TL; DR

这样使用我的功能 if(cmpFloats($a, '==', $b)) { ... }

  • 易于读取/写入/更改: cmpFloats($a, '<=', $b) vsbccomp($a, $b) <= -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"

如何获得浮点数的实际值?

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

您如何比较?

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

用字符串类型转换

$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)

1

@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)

0

这是我的个人库中一个有用的类,用于处理浮点数。您可以按自己的喜好对其进行调整,然后将所需的任何解决方案插入类方法中:-)。

/**
 * 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;
    }
}
?>

0

简单答案:

if( floatval( (string) $a ) >= floatval( (string) $b) ) { //do something }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.