2009-09-04 18 views
38

में गोल इस कोड को देखें: जब मैं Firefox 3.5 में मेरे कंसोल देखनेबड़ी संख्या में ग़लती से जावास्क्रिप्ट

<html> 
<head> 

<script src="http://www.json.org/json2.js" type="text/javascript"></script> 

<script type="text/javascript"> 

    var jsonString = '{"id":714341252076979033,"type":"FUZZY"}'; 
    var jsonParsed = JSON.parse(jsonString); 
    console.log(jsonString, jsonParsed); 


</script> 
</head> 
<body> 
</body> 
</html> 

, jsonParsed का मान:

Object id=714341252076979100 type=FUZZY 

यानी नंबर गोल है। विभिन्न मूल्यों का प्रयास किया, एक ही परिणाम (संख्या गोलाकार)।

मुझे इसके राउंडिंग नियम भी नहीं मिलते हैं। 714341252076979136 714341252076979200 पर गोल किया गया है, जबकि 714341252076979135 714341252076979100 पर गोल किया गया है।

संपादित करें: नीचे पहली टिप्पणी देखें। जाहिर है यह JSON के बारे में नहीं है, लेकिन जावास्क्रिप्ट नंबर हैंडलिंग के बारे में कुछ है। लेकिन सवाल बनी हुई है:

ऐसा क्यों हो रहा है?

+0

सभी उपयोगी सहायक उत्तरों के लिए धन्यवाद, मेरी इच्छा है कि मैं सभी 3 को आधिकारिक उत्तरों के रूप में चिह्नित कर सकूं। – Jaanus

उत्तर

47

जो आप यहां देख रहे हैं वह वास्तव में दो राउंडिंग का प्रभाव है। ईसीएमएस्क्रिप्ट में संख्याएं आंतरिक रूप से डबल-प्रेसिजन फ्लोटिंग-पॉइंट का प्रतिनिधित्व करती हैं। जब id हेक्स में 714341252076979033 (0x9e9d9958274c359) पर सेट किया गया है, तो इसे वास्तव में निकटतम प्रतिनिधित्व करने योग्य डबल-सटीक मान असाइन किया गया है, जो 714341252076979072 (0x9e9d9958274c380) है। जब आप मान मुद्रित करते हैं, तो इसे 15 महत्वपूर्ण दशमलव अंकों तक गोल किया जा रहा है, जो 14341252076979100 देता है।

7

यह इस जेसन पार्सर के कारण नहीं है। बस fbug के कंसोल में 714341252076979033 दर्ज करने का प्रयास करें। आप देखेंगे एक ही 714341252076979100.

जानकारी के लिए इस ब्लॉग पोस्ट देखें: http://www.exploringbinary.com/print-precision-of-floating-point-integers-varies-too

+6

मेरे आलेख को जोड़ने के लिए धन्यवाद, लेकिन यह केवल आधा समस्या बताता है - आंतरिक रूप से गोलाकार मूल्य की प्रिंटिंग। भले ही जावास्क्रिप्ट आपको पूरी चीज प्रिंट करने दे, फिर भी यह गलत होगा - यह निकटतम प्रतिनिधित्व करने योग्य डबल-सटीक मान होगा, जैसा कि नीचे दिए गए अन्य लोगों द्वारा समझाया गया है। –

40

आप जावास्क्रिप्ट की संख्या प्रकार की क्षमता बह निकला रहे हैं, जानकारी के लिए §8.5 of the spec देखते हैं। उन आईडी को स्ट्रिंग होने की आवश्यकता होगी।

आईईईई -754 डबल-परिशुद्धता फ़्लोटिंग पॉइंट (प्रकार जावास्क्रिप्ट उपयोग की तरह) सभी संख्याओं (निश्चित रूप से) का प्रतिनिधित्व नहीं कर सकता है। प्रसिद्ध, 0.1 + 0.2 == 0.3 गलत है। यह पूरी संख्या को प्रभावित कर सकता है जैसे कि यह आंशिक संख्याओं को प्रभावित करता है; एक बार जब आप 9, 007,19 9, 254,740,991 (Number.MAX_SAFE_INTEGER) से ऊपर हो जाते हैं तो यह शुरू होता है।

Number.MAX_SAFE_INTEGER + 1 (9007199254740992) से परे, आईईईई -754 फ्लोटिंग-पॉइंट प्रारूप अब हर लगातार पूर्णांक का प्रतिनिधित्व नहीं कर सकता है। 9007199254740991 + 19007199254740992 है, लेकिन 9007199254740992 + 1भी9007199254740992 क्योंकि 9007199254740993 प्रारूप में नहीं दर्शाया जा सकता है। अगला जो 9007199254740994 हो सकता है। फिर 9007199254740995 नहीं हो सकता है, लेकिन 9007199254740996 कर सकते हैं।

कारण यह है कि हम बिट्स से बाहर हो गए हैं, इसलिए हमारे पास अब 1 एस बिट नहीं है; सबसे कम ऑर्डर बिट अब 2 के गुणकों का प्रतिनिधित्व करता है। आखिरकार, अगर हम आगे बढ़ते रहते हैं, तो हम उस बिट को खो देते हैं और केवल 4 के गुणकों में काम करते हैं। और इसी तरह।

आपके मान अच्छी तरह से उस दहलीज से ऊपर हैं, और इसलिए वे निकटतम प्रतिनिधित्व मूल्य पर गोल हो जाते हैं।


आप बिट्स के बारे में उत्सुक हैं, तो यहां क्या होता है: एक आईईईई-754 द्विआधारी डबल परिशुद्धता फ्लोटिंग प्वाइंट नंबर एक संकेत बिट, प्रतिपादक के 11 बिट्स (जो संख्या के समग्र पैमाने को परिभाषित करता है है , 2 की शक्ति के रूप में [क्योंकि यह एक बाइनरी प्रारूप है]), और 52 बिट्स महत्व (लेकिन प्रारूप इतना चालाक है कि यह उन 52 बिट्स में से 53 बिट सटीकता प्राप्त करता है)। एक्सपोनेंट का उपयोग कैसे जटिल होता है (described here), लेकिन में बहुत अस्पष्ट शब्द, यदि हम एक्सपोनेंट में एक जोड़ते हैं, तो महत्व का मूल्य दोगुना हो जाता है, क्योंकि एक्सपोनेंट 2 की शक्तियों के लिए उपयोग किया जाता है (फिर, वहां चेतावनी, यह प्रत्यक्ष नहीं है, वहां चतुरता है)।

तो चलो मूल्य 9007199254740991 को देखो (उर्फ, Number.MAX_SAFE_INTEGER):

 
   +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit 
  / +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent 
 //  | +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand 
//   |/            | 
0 10000110011 1111111111111111111111111111111111111111111111111111 
                = 9007199254740991 (Number.MAX_SAFE_INTEGER) 

प्रतिपादक मूल्य, 10000110011, इसका मतलब है कि हर बार जब हम significand करने के लिए एक जोड़ने के लिए, संख्या का प्रतिनिधित्व अप 1 से (चला जाता है पूरा नंबर 1, हमने बहुत पहले आंशिक संख्याओं का प्रतिनिधित्व करने की क्षमता खो दी)।

लेकिन अब यह महत्व भरा हुआ है। उस संख्या को पार करने के लिए, हमें एक्सपोनेंट को बढ़ाना होगा, जिसका अर्थ है कि यदि हम किसी को महत्व में जोड़ते हैं, तो प्रदर्शित संख्या का मूल्य 2 तक नहीं जाता है, न कि 1 (क्योंकि एक्सपोनेंट 2 पर लागू होता है, इसका आधार द्विआधारी चल बिंदु संख्या):

 
   +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit 
  / +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent 
 //  | +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand 
//   |/            | 
0 10000110100 0000000000000000000000000000000000000000000000000000 
                = 9007199254740992 (Number.MAX_SAFE_INTEGER + 1) 

ठीक है, कि, ठीक है क्योंकि 9007199254740991 + 19007199254740992 वैसे भी है। परंतु! हम 9007199254740993 का प्रतिनिधित्व नहीं कर सकते हैं। हम बिट्स से बाहर हो गए हैं। हम significand करने के लिए सिर्फ 1 जोड़ेंगे, तो वह मूल्य के लिए 2 जोड़ता है:

 
   +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− sign bit 
  / +−−−−−−−+−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−− exponent 
 //  | +−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−−+− significand 
//   |/            | 
0 10000110100 0000000000000000000000000000000000000000000000000001 
                = 9007199254740994 (Number.MAX_SAFE_INTEGER + 3) 

प्रारूप सिर्फ अब और विषम संख्या का प्रतिनिधित्व नहीं कर सकते हैं, जैसा कि हम मूल्य में वृद्धि, प्रतिपादक बहुत बड़ा है।

आखिरकार, हम फिर से महत्वपूर्ण बिट्स से बाहर निकलते हैं और एक्सपोनेंट को बढ़ाना पड़ता है, इसलिए हम केवल 4 के गुणकों का प्रतिनिधित्व करने में सक्षम होते हैं। फिर 8 के गुणक। फिर 16 के गुणक।

+4

मुझे यह जवाब पसंद है क्योंकि यह वास्तव में आपको बताता है कि समस्या को हल कैसे करें। – jsh

2

समस्या यह है कि आपकी संख्या जावास्क्रिप्ट की तुलना में अधिक सटीकता की आवश्यकता है।

क्या आप संख्या को स्ट्रिंग के रूप में भेज सकते हैं? दो भागों में अलग?

4

जावास्क्रिप्ट डबल परिशुद्धता चल बिन्दु मान, 53 बिट्स की कुल परिशुद्धता यानी उपयोग करता है, लेकिन आप

ceil(lb 714341252076979033) = 60 

बिट्स की जरूरत है वास्तव में मूल्य का प्रतिनिधित्व करने के लिए।

निकटतम बिल्कुल प्रदर्शनीय संख्या है 714341252076979072 (बाइनरी में मूल संख्या लिख ​​0 के साथ पिछले 7 अंक की जगह और ऊपर दौर क्योंकि उच्चतम प्रतिस्थापित अंकों 1 था)।

आपको इस संख्या के बजाय 714341252076979100 मिलेगा क्योंकि ईसीएमए -262, §9.8.1 द्वारा वर्णित ToString() दस की शक्तियों और 53 बिट परिशुद्धता के साथ काम करता है, ये सभी संख्या बराबर हैं।

1

जावास्क्रिप्ट केवल 9 000 मिलियन मिलियन तक की सटीक पूरी संख्या को नियंत्रित कर सकता है (यह 9 शून्य के साथ 9 है)। उससे अधिक और आपको कचरा मिलता है। संख्याओं को पकड़ने के लिए तारों का उपयोग करके इसके आसपास काम करें। यदि आपको इन संख्याओं के साथ गणित करने की आवश्यकता है, तो अपने स्वयं के फ़ंक्शंस लिखें या देखें कि क्या आप उनके लिए लाइब्रेरी पा सकते हैं: मैं पूर्व को सुझाव देता हूं क्योंकि मुझे पुस्तकालयों को पसंद नहीं आया है। शुरू करने के लिए, मेरे दो कार्यों को another answer पर देखें।

संबंधित मुद्दे