পিএইচপি মধ্যে ভাসমান তুলনা করুন


156

আমি এই নমুনা কোডের মতো পিএইচপি-তে দুটি ফ্লোটের তুলনা করতে চাই:

$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একই রকম। পিএইচপি-তে ফ্লোটগুলি পরিচালনা / তুলনা করার কোনও বিশেষ উপায় আছে কি?

যদি হ্যাঁ হয় তবে দয়া করে আমাকে এই সমস্যাটি সমাধান করতে সহায়তা করুন।

বা আমার সার্ভার কনফিগারেশনে সমস্যা আছে?


আমি পেতে a and b are same। এটি কি আপনার পুরো কোড?
পেক্কা

কি সংস্করণ? এটা আমার জন্য ভালই কাজ করে.
gblazex

@ আন্ড্রে সম্ভবত এটি কারণ কারণ প্রকৃত বিশ্বের কেস সম্ভবত উদ্ধৃত উদাহরণের চেয়ে জটিল হতে পারে। এটিকে উত্তর হিসাবে যুক্ত করবেন না কেন?
পেক্কা

2
আপনি floating-pointট্যাগ বিবরণ পড়েছেন ? stackoverflow.com/tags/floating-Point/info ভাসমান-পয়েন্ট সংখ্যা ব্যবহার করার সময় এটি কোনও আচরণ যা সম্ভবত আপনি যে কোনও প্রোগ্রামিং ভাষায় সম্মুখীন হতে পারেন। যেমন দেখুন stackoverflow.com/questions/588004/is-javascripts-math-broken
Piskvor ভবন বাম

উত্তর:


232

আপনি যদি এটি এটি করেন তবে তাদের একই হওয়া উচিত । তবে মনে রাখবেন যে ভাসমান-পয়েন্টের মানগুলির একটি বৈশিষ্ট্য হ'ল সেই গণনাগুলি যা একই মানের ফলস্বরূপ বলে মনে হয় বাস্তবে অভিন্ন হওয়ার দরকার নেই। সুতরাং যদি $aএটি একটি আক্ষরিক হয় .17এবং $bকোনও গণনার মাধ্যমে সেখানে পৌঁছে যায় তবে এটি ভাল হতে পারে যে উভয়ই একই মান প্রদর্শন করে।

সাধারণত আপনি সমতার জন্য ভাসমান-পয়েন্টের মানগুলি কখনই তুলনা করেন না, আপনাকে একটি স্বল্পতম গ্রহণযোগ্য পার্থক্য ব্যবহার করতে হবে:

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

এরকম কিছু.


21
হুঁশিয়ার! একটি স্থির এপসিলন নির্বাচন করা একটি খারাপ উপায় কারণ এটি দেখতে ছোট দেখায়, সংখ্যাটি যখন ছোট হয় তখন এই তুলনাটি নির্ভুল ত্রুটিগুলিতে সত্য ফিরে আসবে। একটি সঠিক উপায় হ'ল এপসিলনের চেয়ে আপেক্ষিক ত্রুটিটি ছোট কিনা তা পরীক্ষা করা। abs($a-$b)>abs(($a-$b)/$b)
পিট বিজল

1
@ আলেকজান্দ্রু: আপনার অর্থ কী তা আমি জানি তবে পিএইচপি এই ক্ষেত্রে একা নয়। আপনাকে এখানে দুটি ব্যবহারের ক্ষেত্রে পৃথক করতে হবে: কোনও ব্যবহারকারীর কাছে একটি নম্বর দেখাচ্ছে। 0.10000000000000000555111512312578270211815834045410156সেক্ষেত্রে প্রদর্শন করা সাধারণত অর্থহীন এবং তারা তার 0.1পরিবর্তে পছন্দ করতে চাই । এবং একটি সংখ্যা লিখতে যাতে এটি আবার একই পদ্ধতিতে পড়তে পারে। আপনি যেমন দেখেন, এটি যতটা পরিষ্কার করা যায় তেমন পরিষ্কার নয়। এবং রেকর্ডের জন্য, আপনি এখনও আমার যেমন দেখানো ভাসমান-পয়েন্ট নম্বরগুলি তুলনা করতে চান কারণ আপনি আসতে পারেন $aএবং $bবিভিন্ন গণনার মাধ্যমে তাদের আলাদা করতে পারেন।
জোয়

2
এখনও কিছু প্রান্তের মামলা রয়েছে যা এই পরীক্ষাটি ব্যর্থ। যেমন a=b=0এবং যদি aসর্বনিম্ন সম্ভব কোনটিই শূন্য ধনাত্মক মান নয় এবং bসর্বনিম্ন সম্ভাব্য অ শূন্য নেতিবাচক মান হয় তবে পরীক্ষাটি ভুলভাবে ব্যর্থ হবে। এখানে কিছু ভাল তথ্য: floating-point-gui.de/errors/comparison
ডোম

13
কেন বিভক্তি $b? পিএইচপি ম্যানুয়াল ঠিক কি মাইএসকিউএল ম্যানুয়াল এছাড়াও করেনি একই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 এর আগে পিএইচপি-র পুরানো সংস্করণে সংজ্ঞায়িত করতে হবে)


2
স্পষ্ট করে বলতে গেলে, EPSILON এ ক্ষেত্রে কি মেশিনের অ্যাপসিলন, যা প্রায় 2.2204460492503E-16? এবং, এই তুলনাটি কোনও মাত্রার দুটি ফ্লোটের জন্য কাজ করে?
মাইকেল কর্ডিংলি

1
@ মিশেলকর্ডিংলে না, EPSILONএখানে একটি নির্বিচার ব্যবহারকারী-সংজ্ঞায়িত ধ্রুবক রয়েছে। পিএইচপি-তে কোনও বিল্ট-ইন ধ্রুবক নেই যা কোনও স্থাপত্যের অ্যাপসিলনের নির্দিষ্ট ধারণাকে উপস্থাপন করে। (আরও দেখুন get_defined_constants))
বিশপ

5
PHP_FLOAT_EPSILONক্ষুদ্রতম প্রতিনিধিত্বমূলক ধনাত্মক সংখ্যা x, যাতে x + 1.0! = 1.0। পিএইচপি 7.2.0 হিসাবে উপলব্ধ।
Code4R7

2
এটি আসলে এই ক্ষেত্রে কাজ করে না: = a = 270.10 + 20.10; $ খ = 290.20; যদি (অ্যাবস ($ a- $ b) <PHP_FLOAT_EPSILON) {প্রতি 'একই'; }
নিমোএক্সপি

@ নিমোএক্সপি কারণ এই অভিব্যক্তিগুলি বিভিন্ন সংখ্যা তৈরি করে। 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স্কেল আর্গুমেন্ট জন্য ব্যবহার করতে পারেন ।
Code4R7

19

আগেই বলা হয়েছে, পিএইচপি-তে ভাসমান পয়েন্ট তুলনা (সমান-সমান, বৃহত্তর-চেয়ে, বা কম-কম) করতে গিয়ে খুব সাবধান হন। তবে আপনি যদি কেবলমাত্র কয়েকটি উল্লেখযোগ্য অঙ্কে আগ্রহী হন তবে আপনি এমন কিছু করতে পারেন:

$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যাতে এটি চলছে তা সুস্পষ্ট।
মাইকেল বাটলার

পিএইচপি এর নেটিভ bccomp($a, $b, 2)আপনার সমাধান চেয়ে উচ্চতর। এই উদাহরণে, 2 যথার্থতা। আপনি তুলনা করতে চান ভাসমান পয়েন্টের যে কোনও সংখ্যায় এটি সেট করতে পারেন।
জন মিলার

@ জনমিলার আমি আপনার সাথে একমত নই, তবে বিসিসি কমপ ডিফল্টরূপে উপলভ্য নয়। এটি সক্ষম করার জন্য একটি সংকলন পতাকা, বা একটি এক্সটেনশন ইনস্টল করা প্রয়োজন। মূল অংশ নয়।
মাইকেল বাটলার

17

দেশীয় পিএইচপি তুলনা ব্যবহার করা ভাল হবে :

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-অন্যথায় 1


10

সমতার সাথে তুলনা করার জন্য আপনার যদি ভাসমান পয়েন্টের মান থাকে তবে ওএস, ভাষা, প্রসেসর বা অন্যান্য জাতীয় অভ্যন্তরীণ বৃত্তাকার কৌশলটি ঝুঁকি এড়ানোর একটি সহজ উপায় হল মানগুলির স্ট্রিং প্রতিনিধিত্বের তুলনা করা ।

পছন্দসই ফলাফল তৈরি করতে আপনি নীচের যে কোনওটি ব্যবহার করতে পারেন: https://3v4l.org/rUrEq

স্ট্রিং টাইপ কাস্টিং

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

স্ট্রিং সংযোগ

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

স্ট্রਵਲ ফাংশন

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

স্ট্রিং প্রতিনিধিত্বগুলি সমতা যাচাইয়ের ক্ষেত্রে ভাসমানগুলির চেয়ে অনেক কম চতুর।


অথবা যদি (স্ট্রওয়াল ($ ক) === স্ট্রভাল ($ বি)) {…} আপনি যদি মূল মানগুলি রূপান্তর করতে না চান
একনোভাল

ঠিক আছে, আমার আসল উত্তর ছিল: যদি (''। $ A === ''। $ খ) {…} তবে কেউ এটি সম্পাদনা করেছিলেন। সুতরাং ...
আমে নোমেড

1
@ একনোয়াল আপনি কি দয়া করে আপনার সংশোধনটির বিশদ বর্ণনা করতে পারেন, মনে হচ্ছে আপনি (string)কাস্ট অপারেশনটি রেফারেন্স দ্বারা সম্পাদিত হয়ে আসল ঘোষণাটি পরিবর্তন করে বলছেন ? যদি তা হয় না তবে 3v4l.org/Craas
fyrye

@ ফাইরি হ্যাঁ আমার ধারণা আমি ভুল ছিলাম, উভয় পদ্ধতিরই একই ফলাফল পাওয়া যায় yield
একনোভাল

একটি উদাহরণ ব্যবহারের জন্য উত্তর এবং অন্যান্য সম্পাদনার সমস্ত উদাহরণের সাথে আসল
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

এটি আমার জন্য পিএইচপি 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

পিএইচপি .2.২ এর জন্য, আপনি PHP_FLOAT_EPSILON ( http://php.net/manual/en/reided.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



১. প্রোগ্রামগুলিতে আমরা যেভাবে সংখ্যা লিখি তার সাথে জড়িত একটি বড় সমস্যা। আমরা এগুলিকে দশমিক স্ট্রিং হিসাবে লিখি এবং ফলস্বরূপ আমরা লিখি বেশিরভাগ ভগ্নাংশের সঠিক মেশিনের উপস্থাপনা থাকে না। তাদের সঠিক সসীম ফর্ম নেই কারণ তারা বাইনারিতে পুনরাবৃত্তি করে। প্রতিটি মেশিন ভগ্নাংশ x / 2 n ফর্মের যুক্তিযুক্ত সংখ্যা । এখন, ধ্রুবকগুলি দশমিক এবং প্রতিটি দশমিক ধ্রুবক হল x / (2 এন * 5 মিটার ) ফর্মের যুক্তিযুক্ত সংখ্যা । 5 মিটার সংখ্যাগুলি বিজোড়, সুতরাং তাদের কোনওর জন্য 2 এন ফ্যাক্টর নেই। কেবলমাত্র যখন এম == 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।


ডিজিটালরোস আপনার মন্তব্যে কয়েকটি পদ বোঝা বেশ কঠিন, তবে হ্যাঁ, এটি খুব তথ্যপূর্ণ। এবং আমি এই পদগুলি গুগল করতে যাচ্ছি। ধন্যবাদ :)
সন্তোষ সোনারিকার

আপনি প্রতিবার ফলাফলটি গোল করে চলেছেন এবং কয়েকটি উল্লেখযোগ্য অঙ্কের মধ্যে রয়েছেন, এমন কি ফ্লোটে তুলনা করা কি যথেষ্ট নিরাপদ নয়? অন্য কথায়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

সমতার জন্য ভাসমান তুলনা করার জন্য একটি নিষ্পাপ ও (এন) অ্যালগরিদম রয়েছে।

আপনাকে অবশ্যই প্রতিটি ফ্লোট মানকে একটি স্ট্রিংয়ে রূপান্তর করতে হবে, তারপরে প্রতিটি ফ্লোটের স্ট্রিং প্রতিনিধিত্বের বাম দিক থেকে শুরু করে প্রতিটি অঙ্কের সাথে পূর্ণসংখ্যার তুলনা অপারেটরগুলি ব্যবহার করে তুলনা করতে হবে। পিএইচপি তুলনা করার আগে প্রতিটি সূচক অবস্থানে অঙ্কটি অটোকাস্ট করে। অন্যটির চেয়ে বড় প্রথম অঙ্কটি লুপটি ভেঙে ফ্লোটটিকে ঘোষণা করবে যে এটি দুটিয়ের বৃহত্তর হিসাবে সম্পর্কিত। গড়ে ১/২ * এন তুলনা হবে। একে অপরের সমান ভাসমানগুলির জন্য, 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;
  }
 }

?>

1

2019

টি এল; ডিআর

নীচে আমার ফাংশনটি ব্যবহার করুন if(cmpFloats($a, '==', $b)) { ... }

  • পড়তে / লিখতে / পরিবর্তন করা সহজ: cmpFloats($a, '<=', $b)বনামbccomp($a, $b) <= -1
  • কোন নির্ভরতা প্রয়োজন।
  • যে কোনও পিএইচপি সংস্করণ দিয়ে কাজ করে।
  • নেতিবাচক সংখ্যা নিয়ে কাজ করে।
  • আপনি কল্পনা করতে পারেন দীর্ঘতম দশমিক নিয়ে কাজ করে।
  • ডাউনসাইড: বিসিপি কম্পিউটারের চেয়ে সামান্য ধীর ()

সারসংক্ষেপ

আমি রহস্য উন্মোচন করব।

$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. বিসি ম্যাথ ফাংশন ব্যবহার করুন । (আপনি এখনও ডাব্লুটিএফ-আহা-গ্যাচা মুহুর্তগুলি পাবেন)
  2. আপনি PHP_FLOAT_EPSILON (পিএইচপি 7.2) ব্যবহার করে @ গ্ল্যাডন এর উত্তর চেষ্টা করতে পারেন।
  3. যদি এর সাথে ভাসমান তুলনা করা হয় ==এবং !=আপনি সেগুলি স্ট্রিংয়ে টাইপকাস্ট করতে পারেন তবে এটি পুরোপুরি কাজ করা উচিত:

স্ট্রিং সহ কাস্ট টাইপ করুন :

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

বা এর সাথে টাইপকাস্ট number_format():

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

সতর্কতা:

গাণিতিকভাবে ভাসমান (গুণমান, বিভাজন ইত্যাদির) সাথে তুলনা করে এমন সমাধানগুলি এড়ান যা বেশিরভাগ ক্ষেত্রে তারা কিছু সমস্যা সমাধান করবে এবং অন্যান্য সমস্যাগুলি প্রবর্তন করবে।


প্রস্তাবিত সমাধান

আমি খাঁটি পিএইচপি ফাংশন তৈরি করেছি (কোনও অবক্ষয় / গ্রন্থাগার / এক্সটেনশনের প্রয়োজন নেই)। প্রতিটি অঙ্ককে স্ট্রিং হিসাবে পরীক্ষা করে এবং তুলনা করে। নেতিবাচক সংখ্যা নিয়েও কাজ করে।

/**
 * 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

@ ইভিলরিকো থেকে ফাংশনটিতে এর মতো কিছু বাগ রয়েছে:

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

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.