PHPでフロートを比較する


157

次のサンプルコードのように、PHPで2つの浮動小数点数を比較したいと思います。

$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でフロートを処理/比較する特別な方法はありますか?

はいの場合、この問題の解決を手伝ってください。

それとも私のサーバー設定に問題がありますか?


わかりますa and b are same。これはあなたの完全なコードですか?
ペッカ

どのバージョン?私にとってはうまくいきます。
gblazex 2010年

@Andreyこれはおそらく現実のケースが引用された例よりも複雑になる可能性があるためです。回答として追加してみませんか?
ペッカ

2
floating-pointタグの説明を読みましたか?stackoverflow.com/tags/floating-point/infoこれは、浮動小数点数を使用するときに、どのプログラミング言語でも遭遇する可能性が高い動作です。たとえば、stackoverflow.com
questions / 588004 / is

回答:


232

このようにすると、同じになるはずです。ただし、浮動小数点値の特徴は、同じ値になると思われる計算が実際に同一である必要がないことです。その場合は、$aリテラルである.17$b、それはうまく彼らは両方のディスプレイにもかかわらず、同じ値が異なっていることができ、計算によってそこに到着します。

通常、次のように等しいかどうかについて浮動小数点値を比較することはありません。許容できる最小の差を使用する必要があります。

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

そんな感じ。


21
注意してください!固定イプシロンを選択することは、見た目が小さいという理由だけで悪い方法です。数値が小さい場合、この比較は多くの精度エラーでtrueを返します。正しい方法は、相対誤差がイプシロンより小さいかどうかを確認することです。abs($a-$b)>abs(($a-$b)/$b)
Piet Bijl 2013年

1
@Alexandru:私はあなたが何を意味するのか知っていますが、その点でPHPだけではありません。ここでは、2つのユースケースを区別する必要があります。ユーザーに番号を表示する。その場合、表示0.10000000000000000555111512312578270211815834045410156は通常無意味であり、0.1代わりに好みます。そして、まったく同じ方法で再度読み取れるように、数値を書き込みます。ご覧のとおり、実際のところほど明確ではありません。そして、記録のために、あなたはまだあなたが到着することができますので、私が示しているように、浮動小数点数を比較する$a$b、それらが異なることができますさまざまな計算を経て。
ジョーイ

2
このテストが失敗するエッジケースがまだいくつかあります。ようなa=b=0場合にはa可能な限り最小なしゼロ正の値であり、b可能な最小の非ゼロの負の値であり、テストが誤って失敗します。ここにいくつかの良い情報があります:floating-point-gui.de/errors/comparison
Dom

13
なぜ分割するの$bですか?PHPマニュアルはちょうどでしたMySQLのマニュアルがまたやった同じif(abs($a-$b) < $epsilon) HAVING ABS(a - b) <= 0.0001
会計士م

1
@CaslavSabani:これは相対エラーであり、絶対エラーではありません。これは、特にとき(まだ壊れたのです$a == $b == 0が、それはすでに多くの一般的な絶対誤差よりもだ。もし$a$b何百万人であり、そして、あなたはEPSILON場合よりも非常に異なるでなければならないであろう$a$b、どこか近くにある0。より良い議論のための上記参照ドムのリンクこれ。
ジョーイ

65

最初にマニュアルの赤い警告を読んでください。floatが等しいかどうかを比較してはなりません。イプシロン技法を使用する必要があります。

例えば:

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

どこPHP_FLOAT_EPSILONの非常に小さな数を表す定数である(あなたは、7.2の前にPHPの古いバージョンでそれを定義する必要があります)


2
明確にするために、この場合のEPSILONはマシンのイプシロン、つまりおよそ2.2204460492503E-16ですか?そして、この比較は、どんな大きさの2つのフロートでも機能しますか?
Michael Cordingley、2015

1
@MichaelCordingleyいいえ、EPSILONここに任意のユーザー定義定数があります。PHPには、イプシロンのアーキテクチャ固有のアイデアを表す組み込み定数がありません。(も参照してください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; if(abs($ a- $ b)<PHP_FLOAT_EPSILON){echo 'same'; }
NemoXP、

@NemoXP。これらの式は異なる数値を生成するため。echo $a - $b; /* 5.6843418860808E-14 */ echo PHP_FLOAT_EPSILON; /* 2.2204460492503E-16 */問題は、アプリケーションに対して「等しい」をどのように定義するか、数値を等しいと見なすためにどれだけ近いかということです。
Andrey

29

または、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
stefancarlton

私はこれが好きです。ほとんどの解決策は丸めと精度の低下に依存しているようですが、私は12点の精度で緯度と経度の座標を扱っているため、微調整の必要なく正確に比較できます。
Rikaelus

パフォーマンスはどうですか?bccomp()引数として文字列を取ります。とにかくPHP_FLOAT_DIG、スケール引数に使用できます。
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';
}

小数点以下2桁(または3または4)に丸めると、予期した結果が得られます。


1
警告の追加の言葉、私はこれらのようなステートメントでコードベースを散らかすことはお勧めしません。緩やかなフロート比較を行いたい場合は、次のようなメソッドを作成して、loose_float_compare何が起こっているのかを明らかにします。
マイケルバトラー、

PHPのネイティブbccomp($a, $b, 2)は、ソリューションよりも優れています。この例では、2が精度です。比較したい任意の数の浮動小数点に設定できます。
John Miller

@JohnMiller私はあなたに反対していませんが、bccompはデフォルトでは利用できません。コンパイルフラグを有効にするか、拡張機能をインストールする必要があります。コアの一部ではありません。
マイケルバトラー

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. 

2つのオペランドが等しい場合は0を返し、left_operandがright_operandより大きい場合は1を返し、それ以外の場合は-1を返します。


10

等価と比較する浮動小数点値がある場合、OS、言語、プロセッサなどの内部丸め戦略のリスクを回避する簡単な方法は、値の文字列表現を比較することです。

次のいずれかを使用して、目的の結果を生成できます。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){…}が、誰かがそれを編集した。So ...
Ame Nomade

1
@Ekonoval変更について詳しく説明してください。(string)キャスト操作が参照によって実行され、元の宣言が変更されていると主張しているようです。もしそうならそうではない 3v4l.org/Craas
fyrye

@fyryeええ、私は間違っていたと思います。どちらのアプローチでも同じ結果が得られます。
Ekonoval

答えを更新して、例の使用法と他の編集のすべての例をオリジナルとともに示します
fyrye

4

許容できる小数点の数が有限である場合、以下は適切に機能します(ただし、イプシロンソリューションよりもパフォーマンスは遅くなります)。

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

3

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 -これがためにのみ動作==して!=ではなく>>=<<=
evilReiko

2

あなたがそのようにそれを書くならば、それはおそらくうまくいくでしょう、それであなたは質問のためにそれを簡略化したと思います。(そして、質問を単純で簡潔にすることは、通常、非常に良いことです。)

しかし、この場合、1つの結果が計算であり、1つの結果が定数であると想像します。

これは、浮動小数点プログラミングの基本的なルールに違反します。等値比較を行わないでください。

これの理由は少し微妙な1ですが、覚えておくべき重要なことは、それらが通常は機能しないことです(皮肉なことに、積分値を除いて)。

if abs(a - y) < epsilon



1.大きな問題の1つは、プログラムでの数値の記述方法に関係しています。それらを10進数の文字列として書き込みます。その結果、私たちが書くほとんどの分数は正確なマシン表現を持ちません。バイナリで繰り返されるため、正確な有限形式はありません。すべての機械部分はx / 2 nの形式の有理数です。現在、定数は10進数であり、すべての10進数定数はx /(2 n * 5 m)の形式の有理数です。5 mの数値は奇数なので、それらのいずれにも2 nの係数はありません。m == 0の場合のみ、分数の2進および10進展開の両方に有限表現があります。したがって、1.25は5 /(2 2 * 5 0であるため正確です)しかし、0.1は1 /(2 0 * 5 1)であるため、そうではありません。実際、系列1.01 .. 1.99では、1.25、1.50、および1.75の3つの数値しか正確に表現できません。


DigitalRossは、コメント内のいくつかの用語を理解するのは非常に困難ですが、そうです。そして、私はこれらの用語をググるつもりです。ありがとう:)
Santosh Sonarikar

毎回結果を丸め、有効数字が数桁以内であれば、浮動小数点数で比較するのに十分安全ではありませんか?言い換えればround($float, 3) == round($other, 3)
マイケル・バトラー

2

浮動小数点または10進数を比較するためのソリューションは次のとおりです

//$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は、比較の前に、各インデックス位置の数字を整数にオートキャストします。最初の桁が他の桁よりも大きいと、ループが中断され、それが属する浮動小数点数が2つのうち大きい方として宣言されます。平均すると、1/2 * nの比較になります。フロートが互いに等しい場合、n回の比較が行われます。これは、アルゴリズムの最悪のシナリオです。最良のシナリオは、各フロートの最初の桁が異なり、1つの比較のみを引き起こすことです。

有用な結果を生成する目的で、未加工のfloat値に対して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バージョンで動作します。
  • 負の数で動作します。
  • あなたが想像できる最も長い10進数で動作します。
  • 欠点: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 }
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.