programing

php의 플로트 비교

bestcode 2022. 12. 27. 21:17
반응형

php의 플로트 비교

다음 샘플 코드와 같이 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';
}

에서는 이드음음, 음음음음음 of of of of 의 결과를 합니다.else" " 가 "if즉 "", "", "", "",$a ★★★★★★★★★★★★★★★★★」$b★★★★★를 취급한 방법이 ?PHP로 플로트를 취급/비교하는 특별한 방법이 있습니까?

만약 그렇다면 이 문제를 해결할 수 있도록 도와주세요.

아니면 서버 설정에 문제가 있나요?

이렇게 하면 똑같을 거예요.그러나 부동소수점 값의 특징은 같은 값을 얻을 수 있다고 생각되는 계산이 실제로 동일할 필요는 없다는 점에 유의하십시오.그래서 만약에$a는 문자 의 자자그 is a a a a a이다..17 ★★★★★★★★★★★★★★★★★」$b두 가지 모두 같은 값을 표시하지만 계산을 통해 서로 다를 수 있습니다.

일반적으로 부동소수점 값은 다음과 같이 비교하지 않습니다. 허용 가능한 최소 차이를 사용해야 합니다.

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

그런 거.

먼저 설명서의 빨간색 경고를 읽으십시오.평등함을 위해 수레를 비교해서는 안 된다.엡실론 기술을 사용해야 합니다.

예를 들어 다음과 같습니다.

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

서 ''는PHP_FLOAT_EPSILON(72 ).

또는 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)

네이티브 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을 반환합니다.

앞에서 설명한 바와 같이 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)로 반올림하면 예상된 결과가 발생합니다.

부동소수점 값을 동등과 비교할 수 있는 경우 OS, 언어, 프로세서 등의 내부 반올림 전략의 위험을 피하기 위한 간단한 방법은 의 문자열 표현을 비교하는 것입니다.

다음 중 하나를 사용하여 원하는 결과를 얻을 수 있습니다.https://3v4l.org/rUrEq

스트링 타입 캐스팅

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

문자열 연결

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

스트립 함수

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

스트링 표현은 플로트보다 훨씬 덜 까다롭습니다.

이것은 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
}

허용 가능한 소수점 수가 적은 경우 다음 사항이 적절하게 작동합니다(단, 엡실론 솔루션보다 성능이 느리더라도).

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

부동소수점 또는 소수점 비교에 대한 해법은 다음과 같습니다.

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

adecimal to 에 variable 。string괜찮을 거야

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 미묘하지만 기억해야 할 중요한 것은 그것들이 보통 작동하지 않는다는 것과( 아이러니하게도, 적분값의 경우는 제외), 그 대안은 다음과 같은 점에서 애매한 비교라는 것입니다.

if abs(a - y) < epsilon



1. 주요 문제 중 하나는 프로그램에서 숫자를 쓰는 방식과 관련이 있습니다.소수 문자열로 쓰기 때문에 대부분의 분수는 정확한 기계 표현을 가지고 있지 않습니다.그들은 2진법으로 반복되기 때문에 정확한 유한 형태를 가지고 있지 않다.모든 기계 분수는 x/2 형식의n 유리수입니다.이제 상수는 십진수이고 모든 십진수 상수는 x/(2n * 5m) 형식의 유리수입니다.5개의m 숫자가 홀수이기 때문에 어느 숫자에도 2개의 계수가 없습니다n.m == 0일 때만 분수의 이항 및 십진수 확장 모두에 유한한 표현이 있습니다.따라서 1.25는 5/(20*5)이므로2 정확하지만 0.1은 1/(2*51)이므로0 정확하지 않습니다.실제로 시리즈 1.01, 1.99에서는 1.25, 1.50 및 1.75의 3개의 숫자만 정확하게 나타낼 수 있습니다.

플로트의 등식을 비교하는 것은 순진한 O(n) 알고리즘입니다.

각 플로트 값을 문자열로 변환한 다음 정수 비교 연산자를 사용하여 각 플로트의 문자열 표현 왼쪽에서 시작하는 각 숫자를 비교해야 합니다.PHP는 비교하기 전에 각 인덱스 위치의 숫자를 정수로 자동 캐스트합니다.첫 번째 숫자가 다른 숫자보다 크면 루프가 끊어지고 루프가 속한 플로트가 두 자리 중 큰 자리라고 선언됩니다.평균적으로 1/2 * n 비교가 이루어집니다.서로 같은 플로트의 경우 n개의 비교가 있습니다.이것은 알고리즘의 최악의 시나리오입니다.가장 좋은 시나리오는 각 플로트의 첫 번째 숫자가 달라서 하나의 비교만 발생시키는 것입니다.

유용한 결과를 생성하기 위해 원시 부동 값에는 INTEGER Comparison 연산자를 사용할 수 없습니다.정수를 비교하지 않기 때문에 이러한 연산의 결과는 의미가 없습니다.의미 없는 결과를 생성하는 각 연산자의 도메인을 위반하고 있습니다.이것은 델타 비교에도 유효합니다.

정수 비교 연산자는 정수 비교 연산자를 사용합니다.

심플한 솔루션:

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

?>

2019

TL;DR

아래와 같이 내 기능을 사용하세요.if(cmpFloats($a, '==', $b)) { ... }

  • 읽기/쓰기/변경 용이:cmpFloats($a, '<=', $b)bccomp($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 산술 함수를 사용합니다.(wtf-aha-gotcha의 많은 순간을 얻을 수 있습니다)
  2. PHP_FLOAT_EPSILON(PHP 7.2)을 사용하여 @Gladhon의 답변을 시도할 수 있습니다.
  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)

사용할 소수 자릿수를 결정하고 사용할 수 있습니다.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';
}

이것은, 2개의 변수의 타입이 혼재하고 있는 경우(스트링 캐스트등의 다른 방식과는 달리), 다음과 같이 동작합니다.

$a = '1.0000'; // Fetched from a DECIMAL db column
$b = 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)

간단한 답변:

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

이것은 부동 소수점 숫자를 다루는 데 제 개인 라이브러리의 유용한 수업입니다.원하는 대로 트윗하여 클래스 메서드에 원하는 솔루션을 삽입할 수 있습니다:-).

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

언급URL : https://stackoverflow.com/questions/3148937/compare-floats-in-php

반응형