फ्लोटिंग पॉइंट वैल्यू के साथ परेशानी यह है कि वे बिट्स की एक निश्चित मात्रा के साथ (निरंतर) मूल्यों की अनंत राशि का प्रतिनिधित्व करने की कोशिश कर रहे हैं। तो स्वाभाविक रूप से, खेलने में कुछ नुकसान होना चाहिए, और आपको कुछ मूल्यों के साथ काट दिया जाएगा।
जब कोई कंप्यूटर 1.275 को एक फ्लोटिंग पॉइंट मान के रूप में संग्रहीत करता है, तो यह वास्तव में याद नहीं करेगा कि यह 1.275 था या 1.27499999999999993, या यहां तक कि 1.27500000000000002। इन मूल्यों को दो दशमलव तक चक्कर लगाने के बाद अलग-अलग परिणाम देने चाहिए, लेकिन वे नहीं करेंगे, क्योंकि कंप्यूटर के लिए वे फ्लोटिंग पॉइंट मानों के रूप में संग्रहीत करने के बाद बिल्कुल समान दिखते हैं , और खोए हुए डेटा को पुनर्स्थापित करने का कोई तरीका नहीं है। किसी भी आगे की गणना केवल इस तरह की खराबी को जमा करेगी।
इसलिए, यदि सटीक मायने रखता है, तो आपको शुरू से ही फ्लोटिंग पॉइंट वैल्यू से बचना होगा। सबसे सरल विकल्प हैं
- एक समर्पित पुस्तकालय का उपयोग करें
- मूल्यों के आसपास भंडारण और गुजरने के लिए तार का उपयोग करें (स्ट्रिंग संचालन के साथ)
- पूर्णांक का उपयोग करें (उदाहरण के लिए आप अपने वास्तविक मूल्य के सौवें हिस्से के आसपास से गुजर सकते हैं, उदाहरण के लिए डॉलर में राशि के बजाय सेंट में राशि)
उदाहरण के लिए, जब पूर्णांकों का उपयोग सौवें की संख्या को संग्रहीत करने के लिए किया जाता है, तो वास्तविक मूल्य को खोजने का कार्य काफी सरल है:
function descale(num, decimals) {
var hasMinus = num < 0;
var numString = Math.abs(num).toString();
var precedingZeroes = '';
for (var i = numString.length; i <= decimals; i++) {
precedingZeroes += '0';
}
numString = precedingZeroes + numString;
return (hasMinus ? '-' : '')
+ numString.substr(0, numString.length-decimals)
+ '.'
+ numString.substr(numString.length-decimals);
}
alert(descale(127, 2));
तार के साथ, आपको गोलाई की आवश्यकता होगी, लेकिन यह अभी भी प्रबंधनीय है:
function precise_round(num, decimals) {
var parts = num.split('.');
var hasMinus = parts.length > 0 && parts[0].length > 0 && parts[0].charAt(0) == '-';
var integralPart = parts.length == 0 ? '0' : (hasMinus ? parts[0].substr(1) : parts[0]);
var decimalPart = parts.length > 1 ? parts[1] : '';
if (decimalPart.length > decimals) {
var roundOffNumber = decimalPart.charAt(decimals);
decimalPart = decimalPart.substr(0, decimals);
if ('56789'.indexOf(roundOffNumber) > -1) {
var numbers = integralPart + decimalPart;
var i = numbers.length;
var trailingZeroes = '';
var justOneAndTrailingZeroes = true;
do {
i--;
var roundedNumber = '1234567890'.charAt(parseInt(numbers.charAt(i)));
if (roundedNumber === '0') {
trailingZeroes += '0';
} else {
numbers = numbers.substr(0, i) + roundedNumber + trailingZeroes;
justOneAndTrailingZeroes = false;
break;
}
} while (i > 0);
if (justOneAndTrailingZeroes) {
numbers = '1' + trailingZeroes;
}
integralPart = numbers.substr(0, numbers.length - decimals);
decimalPart = numbers.substr(numbers.length - decimals);
}
} else {
for (var i = decimalPart.length; i < decimals; i++) {
decimalPart += '0';
}
}
return (hasMinus ? '-' : '') + integralPart + (decimals > 0 ? '.' + decimalPart : '');
}
alert(precise_round('1.275', 2));
alert(precise_round('1.27499999999999993', 2));
ध्यान दें कि यह फ़ंक्शन निकटतम से गोल होता है , शून्य से दूर होता है , जबकि IEEE 754 निकटतम के लिए गोलाई की सिफारिश करता है, फ़्लोटिंग पॉइंट ऑपरेशंस के लिए डिफ़ॉल्ट व्यवहार के रूप में भी । ऐसे संशोधनों को पाठक के लिए एक अभ्यास के रूप में छोड़ दिया जाता है :)