यह सवाल जटिल है।
मान लीजिए कि हमारे पास एक फ़ंक्शन है, roundTo2DP(num)
जो एक तर्क के रूप में एक फ्लोट लेता है और 2 दशमलव स्थानों पर गोल मान लौटाता है। इन अभिव्यक्तियों में से प्रत्येक का क्या मूल्यांकन करना चाहिए?
roundTo2DP(0.014999999999999999)
roundTo2DP(0.0150000000000000001)
roundTo2DP(0.015)
'स्पष्ट' उत्तर यह है कि पहला उदाहरण 0.01 (क्योंकि यह 0.01 से 0.02 के करीब है) के लिए गोल होना चाहिए, जबकि अन्य दो को 0.02 से गोल करना चाहिए (क्योंकि 0.0150000000000000001 0.01 से 0.01 के करीब है, और क्योंकि 0.015 के बीच बिल्कुल आधा है उन्हें और एक गणितीय सम्मेलन है कि इस तरह की संख्या गोल हो जाती है)।
यह पकड़, जिसका आपने अनुमान लगाया होगा, roundTo2DP
यह संभवत: उन स्पष्ट उत्तरों को लागू करने के लिए लागू नहीं किया जा सकता है, क्योंकि इसमें दिए गए सभी तीन नंबर समान संख्या हैं । IEEE 754 बाइनरी फ़्लोटिंग पॉइंट नंबर (जावास्क्रिप्ट द्वारा उपयोग किया जाने वाला) बिल्कुल गैर-पूर्णांक संख्याओं का प्रतिनिधित्व नहीं कर सकता है, और इसलिए उपरोक्त सभी तीन संख्यात्मक शाब्दिक पास के मान्य फ़्लोटिंग पॉइंट नंबर पर गोल हो जाते हैं। यह संख्या, जैसा कि होता है, बिल्कुल है
0,01499999999999999944488848768742172978818416595458984375
जो 0.01 से 0.02 के करीब है।
आप देख सकते हैं कि आपके ब्राउज़र कंसोल, नोड शेल या अन्य जावास्क्रिप्ट दुभाषिया पर तीनों संख्याएँ समान हैं। बस उनकी तुलना करें:
> 0.014999999999999999 === 0.0150000000000000001
true
इसलिए जब मैं लिखता हूं m = 0.0150000000000000001
, तो जो मैं समाप्त करता हूं , उसका सटीक मूल्यm
उस की 0.01
तुलना में करीब है 0.02
। और फिर भी, अगर मैं m
एक स्ट्रिंग में परिवर्तित ...
> var m = 0.0150000000000000001;
> console.log(String(m));
0.015
> var m = 0.014999999999999999;
> console.log(String(m));
0.015
... मुझे 0.015 मिलता है, जो कि राउंड टू 0.02 होना चाहिए, और जो कि 56-दशमलव-प्लेस नंबर नहीं है, जो मैंने पहले कहा था कि ये सभी नंबर बिल्कुल बराबर थे। तो यह कौन सा काला जादू है?
इसका जवाब ECMAScript विनिर्देशन में पाया जा सकता है, जो 7.1 7.1.1 खंड में है : संख्या प्रकार पर लागू किया गया । यहां कुछ नंबर मी को एक स्ट्रिंग में बदलने के नियम निर्धारित किए गए हैं। प्रमुख भाग बिंदु 5 है, जिसमें एक पूर्णांक s उत्पन्न होता है, जिसका अंक m के स्ट्रिंग प्रतिनिधित्व में उपयोग किया जाएगा :
चलो एन , कश्मीर , और एस पूर्णांक ऐसा है कि हो सकता है कश्मीर ≥ 1, 10 कश्मीर -1 ≤ रों <10 कश्मीर , के लिए संख्या मूल्य रों × 10 एन - कश्मीर है मीटर , और कश्मीर को छोटे से छोटा है। ध्यान दें कि कश्मीर के दशमलव प्रतिनिधित्व अंकों की संख्या है रों , कि एस 10 से विभाज्य नहीं है, और कम से कम सार्थक अंक कि रों जरूरी विशिष्ट इन मापदंडों का निर्धारण नहीं किया गया है।
यहाँ मुख्य भाग की आवश्यकता है कि " k जितना संभव हो उतना छोटा हो"। वह आवश्यकता मात्रा क्या है जो एक संख्या है, जिसे एक संख्या दी गई है m
, मूल्य का कम से कम संभव संख्या में अंकString(m)
होना चाहिए जबकि अभी भी उस आवश्यकता को पूरा करना है । चूंकि हम पहले से ही जानते हैं , यह अब स्पष्ट है कि क्योंNumber(String(m)) === m
0.015 === 0.0150000000000000001
String(0.0150000000000000001) === '0.015'
सच होना चाहिए।
बेशक, इस चर्चा में से किसी ने भी सीधे जवाब नहीं दिया कि क्या लौटना roundTo2DP(m)
चाहिए । यदि इसका m
सटीक मान 0.01499999999999999444888487687421729788184165958984375 है, लेकिन इसका स्ट्रिंग प्रतिनिधित्व '0.015' है, तो सही क्या है उत्तर - गणितीय, व्यावहारिक रूप से, दार्शनिक रूप से, या जो भी - जब हम इसे दो दशमलव स्थानों पर गोल करते हैं।
इसका कोई एक सही उत्तर नहीं है। यह आपके उपयोग के मामले पर निर्भर करता है। आप शायद स्ट्रिंग प्रतिनिधित्व का सम्मान करना चाहते हैं और जब ऊपर की तरफ गोल होते हैं:
- प्रतिनिधित्व किया जा रहा मूल्य स्वाभाविक रूप से असतत है, उदाहरण के लिए दीनार जैसी 3-दशमलव-मुद्रा में मुद्रा की मात्रा। इस स्थिति में, 0.015 जैसी संख्या का सही मान 0.015 है , और 0.0149999999 ... यह बाइनरी फ़्लोटिंग पॉइंट में मिलने वाला प्रतिनिधित्व एक गोल त्रुटि है। (निश्चित रूप से, कई तर्क करेंगे, यथोचित, कि आपको ऐसे मूल्यों को संभालने के लिए एक दशमलव पुस्तकालय का उपयोग करना चाहिए और पहले स्थान पर बाइनरी फ्लोटिंग पॉइंट नंबरों के रूप में उनका प्रतिनिधित्व कभी नहीं करना चाहिए।)
- उपयोगकर्ता द्वारा मान टाइप किया गया था। इस मामले में, फिर से दर्ज की गई सटीक दशमलव संख्या निकटतम बाइनरी फ्लोटिंग पॉइंट प्रतिनिधित्व की तुलना में अधिक 'सही' है।
दूसरी ओर, आप शायद बाइनरी फ़्लोटिंग पॉइंट वैल्यू का सम्मान करना चाहते हैं और नीचे की तरफ गोल करते हैं जब आपका मूल्य एक अंतर्निहित निरंतर पैमाने से होता है - उदाहरण के लिए, यदि यह एक सेंसर से रीडिंग है।
इन दो दृष्टिकोणों को अलग-अलग कोड की आवश्यकता होती है। संख्या के स्ट्रिंग प्रतिनिधित्व का सम्मान करने के लिए, हम अपने स्वयं के गोलाई को काफी हद तक लागू कर सकते हैं (जो कि हमारे अपने राउंडिंग को लागू करते हैं, जो सीधे स्ट्रिंग प्रतिनिधित्व, अंकों के आधार पर कार्य करता है, उसी एल्गोरिदम का उपयोग करके जब आप स्कूल में उपयोग करते थे। संख्याओं को गोल करने का तरीका सिखाया गया। नीचे एक उदाहरण दिया गया है, जो ओपी को दशमलव बिंदु के बाद ट्रेलिंग जीरो को छीनकर "केवल आवश्यक होने पर" 2 दशमलव स्थानों की संख्या का प्रतिनिधित्व करने की आवश्यकता का सम्मान करता है; बेशक, आपको अपनी सटीक आवश्यकताओं के लिए इसे मोड़ने की आवश्यकता हो सकती है।
/**
* Converts num to a decimal string (if it isn't one already) and then rounds it
* to at most dp decimal places.
*
* For explanation of why you'd want to perform rounding operations on a String
* rather than a Number, see http://stackoverflow.com/a/38676273/1709587
*
* @param {(number|string)} num
* @param {number} dp
* @return {string}
*/
function roundStringNumberWithoutTrailingZeroes (num, dp) {
if (arguments.length != 2) throw new Error("2 arguments required");
num = String(num);
if (num.indexOf('e+') != -1) {
// Can't round numbers this large because their string representation
// contains an exponent, like 9.99e+37
throw new Error("num too large");
}
if (num.indexOf('.') == -1) {
// Nothing to do
return num;
}
var parts = num.split('.'),
beforePoint = parts[0],
afterPoint = parts[1],
shouldRoundUp = afterPoint[dp] >= 5,
finalNumber;
afterPoint = afterPoint.slice(0, dp);
if (!shouldRoundUp) {
finalNumber = beforePoint + '.' + afterPoint;
} else if (/^9+$/.test(afterPoint)) {
// If we need to round up a number like 1.9999, increment the integer
// before the decimal point and discard the fractional part.
finalNumber = Number(beforePoint)+1;
} else {
// Starting from the last digit, increment digits until we find one
// that is not 9, then stop
var i = dp-1;
while (true) {
if (afterPoint[i] == '9') {
afterPoint = afterPoint.substr(0, i) +
'0' +
afterPoint.substr(i+1);
i--;
} else {
afterPoint = afterPoint.substr(0, i) +
(Number(afterPoint[i]) + 1) +
afterPoint.substr(i+1);
break;
}
}
finalNumber = beforePoint + '.' + afterPoint;
}
// Remove trailing zeroes from fractional part before returning
return finalNumber.replace(/0+$/, '')
}
उदाहरण उपयोग:
> roundStringNumberWithoutTrailingZeroes(1.6, 2)
'1.6'
> roundStringNumberWithoutTrailingZeroes(10000, 2)
'10000'
> roundStringNumberWithoutTrailingZeroes(0.015, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.015000', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(1, 1)
'1'
> roundStringNumberWithoutTrailingZeroes('0.015', 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes(0.01499999999999999944488848768742172978818416595458984375, 2)
'0.02'
> roundStringNumberWithoutTrailingZeroes('0.01499999999999999944488848768742172978818416595458984375', 2)
'0.01'
उपरोक्त फ़ंक्शन संभवतः वह है जो आप कभी भी उन गवाहों की संख्या से बचने के लिए उपयोग करना चाहते हैं जिन्हें उन्होंने गलत तरीके से दर्ज किया है।
(एक विकल्प के रूप में, आप राउंड 10 लाइब्रेरी की भी कोशिश कर सकते हैं जो एक समान रूप से अलग-अलग कार्यान्वयन के साथ व्यवहार करने वाला फ़ंक्शन प्रदान करता है।)
लेकिन क्या होगा यदि आपके पास दूसरे प्रकार की संख्या है - एक निरंतर पैमाने से लिया गया मूल्य, जहां यह सोचने का कोई कारण नहीं है कि कम दशमलव स्थानों के साथ अनुमानित दशमलव अभ्यावेदन उन लोगों की तुलना में अधिक सटीक हैं जो अधिक हैं? उस स्थिति में, हम स्ट्रिंग प्रतिनिधित्व का सम्मान नहीं करना चाहते हैं, क्योंकि यह प्रतिनिधित्व (जैसा कि समझाया गया है) पहले से ही क्रमबद्ध है; हम "0.014999999 ... 375 राउंड 0.015 तक, जो 0.02 तक राउंड करते हैं, इसलिए 0.014999999 ... 375 राउंड अप 0.02" कहने की गलती नहीं करना चाहते हैं।
यहां हम केवल बिल्ट-इन toFixed
विधि का उपयोग कर सकते हैं । ध्यान दें कि Number()
स्ट्रिंग द्वारा वापस बुलाए जाने पर toFixed
, हमें एक संख्या मिलती है , जिसके स्ट्रिंग प्रतिनिधित्व में कोई अनुगामी शून्य नहीं होता है (जिस तरह से जावास्क्रिप्ट एक संख्या के स्ट्रिंग प्रतिनिधित्व की गणना करता है, इस उत्तर में पहले चर्चा की गई है)।
/**
* Takes a float and rounds it to at most dp decimal places. For example
*
* roundFloatNumberWithoutTrailingZeroes(1.2345, 3)
*
* returns 1.234
*
* Note that since this treats the value passed to it as a floating point
* number, it will have counterintuitive results in some cases. For instance,
*
* roundFloatNumberWithoutTrailingZeroes(0.015, 2)
*
* gives 0.01 where 0.02 might be expected. For an explanation of why, see
* http://stackoverflow.com/a/38676273/1709587. You may want to consider using the
* roundStringNumberWithoutTrailingZeroes function there instead.
*
* @param {number} num
* @param {number} dp
* @return {number}
*/
function roundFloatNumberWithoutTrailingZeroes (num, dp) {
var numToFixedDp = Number(num).toFixed(dp);
return Number(numToFixedDp);
}