फ्लोट में तुलना करें


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, भले ही $aऔर $bसमान हो। क्या PHP में फ़्लोट्स को संभालने / तुलना करने का कोई विशेष तरीका है?

यदि हाँ तो कृपया इस मुद्दे को हल करने में मेरी मदद करें।

या मेरे सर्वर कॉन्फ़िगरेशन में कोई समस्या है?


मुझे मिलता है a and b are same। क्या यह आपका पूरा कोड है?
पेक्का

क्या संस्करण? यह मेरे लिए ठीक काम करता है।
gblazex

@ और शायद यह इसलिए है क्योंकि वास्तविक दुनिया का मामला उद्धृत उदाहरण से अधिक जटिल होने की संभावना है। इसे उत्तर के रूप में क्यों नहीं जोड़ा गया?
पेक्का

2
क्या आपने floating-pointटैग विवरण पढ़ा ? stackoverflow.com/tags/floating-point/info यह एक व्यवहार है जिसका आप किसी भी प्रोग्रामिंग भाषा में सामना करेंगे, जब फ्लोटिंग-पॉइंट नंबरों का उपयोग किया जाएगा। उदाहरण के लिए देखें stackoverflow.com/questions/588004/is-javascripts-math-broken
पिस्कोर ने इमारत

जवाबों:


232

यदि आप इसे इस तरह करते हैं, तो उन्हें समान होना चाहिए । लेकिन ध्यान दें कि फ्लोटिंग-पॉइंट वैल्यू की एक विशेषता यह है कि गणना जो एक ही मूल्य में परिणाम लगती है, वास्तव में समान होने की आवश्यकता नहीं है। तो अगर $aएक शाब्दिक है .17और $bएक गणना के माध्यम से वहां आता है तो यह अच्छी तरह से हो सकता है कि वे अलग-अलग हों, यद्यपि दोनों एक ही मूल्य प्रदर्शित करते हैं।

आमतौर पर आप इस तरह की समानता के लिए फ्लोटिंग-पॉइंट मूल्यों की तुलना नहीं करते हैं, आपको एक छोटे से स्वीकार्य अंतर का उपयोग करने की आवश्यकता है:

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

ऐसा कुछ।


21
सावधान! एक निश्चित एप्सिलॉन चुनना एक बुरा तरीका है क्योंकि यह छोटा दिखता है, यह तुलना सटीक त्रुटियों की एक बहुत में वापस आ जाएगी जब संख्या छोटी होती है। एक सही तरीका यह जांचना होगा कि क्या सापेक्ष त्रुटि एप्सिलॉन से छोटी है। abs($a-$b)>abs(($a-$b)/$b)
पीट बिजल

1
@ अलेक्सांद्रु: मुझे पता है कि आपका क्या मतलब है, लेकिन PHP उस संबंध में अकेला नहीं है। आपको यहां दो उपयोग मामलों को भेद करने की आवश्यकता है: एक उपयोगकर्ता को एक संख्या दिखा रहा है। उस मामले में प्रदर्शित 0.10000000000000000555111512312578270211815834045410156करना आमतौर पर व्यर्थ है और वे 0.1इसके बजाय पसंद करेंगे । और एक संख्या लिख ​​रहा है ताकि इसे ठीक उसी तरह से फिर से पढ़ा जा सके। जैसा कि आप देखते हैं, यह उतना स्पष्ट नहीं है जितना कि आप इसे बनाते हैं। और रिकॉर्ड के लिए, आप अभी भी फ्लोटिंग-पॉइंट संख्याओं की तुलना करना चाहते हैं जैसे मैंने दिखाया है क्योंकि आप विभिन्न गणनाओं के माध्यम से $aऔर उन पर पहुंच $bसकते हैं जो उन्हें अलग बना सकते हैं।
जोए

2
अभी भी कुछ किनारे मामले हैं जो यह परीक्षण विफल रहता है। इस तरह के रूप में a=b=0और अगर aसबसे छोटा संभव शून्य सकारात्मक मूल्य नहीं है और bसबसे कम संभव गैर शून्य नकारात्मक मूल्य है, तो परीक्षण गलत रूप से विफल हो जाएगा। यहाँ कुछ अच्छी जानकारी: floating-point-gui.de/errors/comparison
डोम

13
क्यों बंटा $b? पीएचपी मैनुअल अभी किया MySQL मैनुअल भी किया था एक हीif(abs($a-$b) < $epsilon) HAVING ABS(a - b) <= 0.0001
लेखाकार م

1
@ कैस्लावसबानी: यह सापेक्ष है, पूर्ण त्रुटि नहीं। यह अभी भी टूट गया है (खासकर जब $a == $b == 0, लेकिन यह पहले से ही निरपेक्ष त्रुटि की तुलना में बहुत अधिक सामान्य है। यदि $aऔर $bलाखों में हैं, तो अपने EPSILONअगर तुलना में बहुत अलग होना चाहिए था $aऔर $bकहीं के करीब हैं 0इसके बाद के संस्करण की एक बेहतर विचार विमर्श के लिए। डोम के देखें लिंक यह।
जॉय

64

पहले मैनुअल में लाल चेतावनी पढ़ें । आपको कभी भी समानता के लिए तैरने की तुलना नहीं करनी चाहिए। आपको एप्सिलॉन तकनीक का इस्तेमाल करना चाहिए।

उदाहरण के लिए:

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

जहां PHP_FLOAT_EPSILONबहुत कम संख्या का प्रतिनिधित्व निरंतर है (आपको इसे 7.2 से पहले PHP के पुराने संस्करणों में परिभाषित करना होगा)


2
स्पष्ट करने के लिए, क्या इस मामले में EPSILON मशीन एप्सिलॉन है, जो लगभग 2.2204460492503E-16 है? और, क्या यह तुलना किसी परिमाण के दो फ्लोट के लिए काम करती है?
माइकल कॉर्डिंगले

1
@MichaelCordingley नहीं, EPSILONयहाँ एक उपयोगकर्ता-परिभाषित स्थिरांक है। PHP में आर्किटेक्चर के विशिष्ट विचार एप्सिलॉन के निर्माण में निरंतर निर्मित नहीं होता है। (यह भी देखें get_defined_constants।)
बिशप

5
PHP_FLOAT_EPSILONसबसे छोटा प्रतिनिधित्व योग्य धनात्मक संख्या x, ताकि x + 1.0! = 1.0। PHP के रूप में उपलब्ध 7.2.0।
कोड ४ आर Code

2
यह वास्तव में इस मामले में काम नहीं करता है: $ a = 270.10 + 20.10; $ b = 290.20; if (abs ($- $ b) <PHP_FLOAT_EPSILON) {गूंज 'वही'; }
नेमोएक्सपी

@NemoXP क्योंकि वे भाव अलग-अलग संख्याएँ उत्पन्न करते हैं। echo $a - $b; /* 5.6843418860808E-14 */ echo PHP_FLOAT_EPSILON; /* 2.2204460492503E-16 */सवाल यह है कि आप अपने आवेदन के लिए "बराबर" को कैसे परिभाषित करना चाहते हैं, संख्याओं को कितना समान माना जाना चाहिए।
एंड्री

28

या बीसी गणित कार्यों का उपयोग करने की कोशिश करें:

<?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
आपके द्वारा "स्केल" का उपयोग करने से आप "स्केल" से चूक गए हैं, इस प्रकार आप वास्तव में 0 से 0 की तुलना मैनुअल के अनुसार कर रहे हैं: php.net/manual/en/function.bccomp.php
stefancarlton

मुझे यह पसंद आ रहा है। अधिकांश समाधान राउंडिंग और सटीक खोने पर भरोसा करने लगते हैं, लेकिन मैं अक्षांश और देशांतर के साथ समन्वय कर रहा हूं, जो सटीकता के 12 बिंदुओं के साथ है और ऐसा लगता है कि उनकी तुलना बिना किसी ट्विकिंग के सही रूप से की जा रही है।
रिकेलस

प्रदर्शन के बारे में क्या? bccomp()तर्क के रूप में तार लेता है। वैसे भी आप PHP_FLOAT_DIGस्केल तर्क के लिए उपयोग कर सकते हैं ।
कोड 4 आर 7

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 सटीक है। आप इसे निर्धारित कर सकते हैं कि आप कितने भी फ्लोटिंग पॉइंट्स की तुलना करना चाहते हैं।
जॉन मिलर

@ जॉनमिलर मैं आपसे असहमत नहीं हूँ, लेकिन संक्षिप्त रूप से डिफ़ॉल्ट रूप से उपलब्ध नहीं है। इसके लिए या तो एक संकलित ध्वज सक्षम होना चाहिए, या एक एक्सटेंशन स्थापित होना चाहिए। कोर का हिस्सा नहीं।
माइकल बटलर

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 अगर दो ऑपरेंड बराबर हैं, तो 1 अगर लेफ्ट_ऑपरैंड दाएं_परैंड से बड़ा है, तो -1।


10

यदि आपके पास समानता की तुलना करने के लिए फ्लोटिंग पॉइंट वैल्यू हैं, तो ओएस, भाषा, प्रोसेसर या इतने पर आंतरिक राउंडिंग रणनीति के जोखिम से बचने का एक सरल तरीका है , मूल्यों के स्ट्रिंग प्रतिनिधित्व की तुलना करना।

आप वांछित परिणाम का उत्पादन करने के लिए निम्नलिखित में से किसी का उपयोग कर सकते हैं: https://3v4l.org/rUrEq

स्ट्रिंग प्रकार कास्टिंग

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

स्ट्रिंग कॉनटेनटेशन

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

आवेग समारोह

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

जब यह समानता की जाँच करने की बात आती है, तो स्ट्रिंग का प्रतिनिधित्व फ़्लोट्स की तुलना में बहुत कम है।


या यदि (यदि ($ a) === स्ट्रगल ($ b)) {…} यदि आप मूल मूल्यों को परिवर्तित नहीं करना चाहते हैं
Ekonoval

ठीक है, मेरा मूल उत्तर था: अगर (''। $ a === ''। $ b) {...} लेकिन किसी ने इसे संपादित किया है। तो ...
एएम नोमडे

1
@ इकोनोवल क्या आप अपने संशोधन के बारे में विस्तार से बता सकते हैं, ऐसा प्रतीत होता है कि आप यह दावा कर रहे हैं कि (string)मूल घोषणा को बदलते हुए, कास्ट ऑपरेशन को संदर्भ द्वारा किया जाता है? यदि ऐसा नहीं है तो मामला 3v4l.org/Craas
fyrye

@fyrye हाँ मुझे लगता है कि मैं गलत था, दोनों दृष्टिकोण समान परिणाम देते हैं।
एकोनोवाल

एक उदाहरण के उपयोग और मूल के साथ अन्य संपादन के सभी उदाहरणों का जवाब देने के लिए अपडेट किया गया
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- पीएचपी 7.2 को अद्यतन करने की आवश्यकता होती है जो हर कोई मौजूदा बड़ा / वर्ष प्रणालियों 2- केवल के लिए यह काम करता है के लिए आसानी से कर सकते ==हैं और !=नहीं बल्कि >, >=, <,<=
evilReiko

2

यदि आप इसे इस तरह लिखते हैं कि यह शायद काम करेगा, तो मुझे लगता है कि आपने इसे प्रश्न के लिए सरल बना दिया है। (और प्रश्न को सरल और संक्षिप्त रखना सामान्य रूप से बहुत अच्छी बात है।)

लेकिन इस मामले में मुझे लगता है कि एक परिणाम एक गणना है और एक परिणाम एक स्थिर है।

यह फ्लोटिंग पॉइंट प्रोग्रामिंग के कार्डिनल नियम का उल्लंघन करता है: कभी भी समानता की तुलना न करें।

इसके कारण एक सा सूक्ष्म हैं 1 लेकिन क्या याद रखना महत्वपूर्ण है कि वे आम तौर पर नहीं काम (सिवाय अभिन्न मान के लिए, विडंबना यह) करते हैं और है कि वैकल्पिक की तर्ज पर एक फजी तुलना है:

if abs(a - y) < epsilon



1. एक बड़ी समस्या यह है कि जिस तरह से हम कार्यक्रमों में संख्याएँ लिखते हैं। हम उन्हें दशमलव तार के रूप में लिखते हैं, और इसके परिणामस्वरूप हम जो भी अंश लिखते हैं, उनमें सटीक मशीन निरूपण नहीं होता है। उनके पास सटीक परिमित रूप नहीं हैं क्योंकि वे बाइनरी में दोहराते हैं। प्रत्येक मशीन अंश x / 2 n रूप की एक परिमेय संख्या है । अब, स्थिरांक दशमलव हैं और प्रत्येक दशमलव स्थिरांक रूप x / (2 n * 5 m ) का एक परिमेय संख्या है । 5 मीटर संख्या विषम हैं, इसलिए उनमें से किसी के लिए 2 एन कारक नहीं है । केवल जब m == 0 अंश के द्विआधारी और दशमलव विस्तार दोनों में एक परिमित प्रतिनिधित्व है। तो, १.२५ सटीक है क्योंकि यह ५ / (२ * ५ ० है), लेकिन 0.1 नहीं है क्योंकि यह 1 / (2 0 * 5 1 ) है। वास्तव में, श्रृंखला में 1.01 .. 1.99 में से केवल 3 संख्याएं बिल्कुल प्रतिनिधित्व योग्य हैं: 1.25, 1.50 और 1.75।


DigitalRoss अपनी टिप्पणी में कुछ शब्दों को समझना काफी कठिन है, लेकिन हाँ, इसकी बहुत जानकारीपूर्ण। और मैं इन शब्दों को google करने जा रहा हूं। धन्यवाद :)
संतोष सोनिकर

क्या यह तैरने पर तुलना करने के लिए पर्याप्त सुरक्षित नहीं है, बशर्ते आप हर बार परिणाम को गोल कर रहे हों और कुछ महत्वपूर्ण अंकों के भीतर हों? दूसरे शब्दों मेंround($float, 3) == round($other, 3)
माइकल बटलर

2

यहाँ फ़्लोटिंग पॉइंट्स या दशमलव संख्याओं की तुलना करने के लिए समाधान है

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

एक decimalचर कास्ट करें, stringऔर आप ठीक हो जाएंगे।


1

समानता के लिए फ़्लोट्स की तुलना में एक भोले हे (एन) एल्गोरिदम है।

आपको प्रत्येक फ्लोट मान को एक स्ट्रिंग में बदलना होगा, फिर पूर्णांक तुलना ऑपरेटरों के उपयोग से प्रत्येक फ्लोट के स्ट्रिंग प्रतिनिधित्व के बाईं ओर से शुरू होने वाले प्रत्येक अंक की तुलना करें। तुलना से पहले पूर्णांक में अनुक्रमणिका स्थिति में अंकों को स्वतः निरूपित किया जाएगा। दूसरे से बड़ा पहला अंक लूप को तोड़ देगा और फ्लोट को घोषित करेगा कि यह दोनों में से बड़ा है। औसतन, 1/2 * n तुलना होगी। एक दूसरे के बराबर तैरने के लिए, n तुलनाएं होंगी। यह एल्गोरिथ्म के लिए सबसे खराब स्थिति है। सबसे अच्छा मामला परिदृश्य यह है कि प्रत्येक फ्लोट का पहला अंक अलग है, जिससे केवल एक तुलना होती है।

आप उपयोगी परिणामों को उत्पन्न करने के इरादे से कच्चे फ्लोट के मूल्यों पर इंटीग्रेटर कम्पेनर्स का उपयोग नहीं कर सकते। इस तरह के ऑपरेशन के परिणामों का कोई मतलब नहीं है क्योंकि आप पूर्णांकों की तुलना नहीं कर रहे हैं। आप प्रत्येक ऑपरेटर के डोमेन का उल्लंघन कर रहे हैं जो अर्थहीन परिणाम उत्पन्न करता है। यह डेल्टा तुलना के लिए भी आयोजित करता है।

पूर्णांक तुलना संचालकों का उपयोग करें कि वे किसके लिए डिज़ाइन किए गए हैं: पूर्णांक की तुलना करना।

सरल समाधान:

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

टी एल; डॉ

इस तरह से नीचे मेरे फ़ंक्शन का उपयोग करें if(cmpFloats($a, '==', $b)) { ... }

  • पढ़ने / लिखने / बदलने में आसान: cmpFloats($a, '<=', $b)बनामbccomp($a, $b) <= -1
  • कोई निर्भरता की जरूरत है।
  • किसी भी PHP संस्करण के साथ काम करता है।
  • नकारात्मक संख्याओं के साथ काम करता है।
  • सबसे लंबे दशमलव के साथ काम करता है जिसकी आप कल्पना कर सकते हैं।
  • डाउनसाइड: बॉकम्पो की तुलना में थोड़ा धीमा ()

सारांश

मैं रहस्य का अनावरण करूंगा।

$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. बीसी गणित कार्यों का उपयोग करें । (आप अभी भी बहुत सारे 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)

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

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.