C और Java राउंड अलग-अलग क्यों तैरते हैं?




floating-point printf (2)

निष्कर्ष

इस स्थिति में जावा स्पेसिफिकेशन के लिए एक परेशानी का सामना करना पड़ता है। नंबर ०.६४४६६44४ ९९९९९९ 45६६६६६87१87५87५६५६५६५२६२०२४६५६५४२ ९ converted५ to५ पहले ०.०४४ ९ ६75६ and५ में परिवर्तित हुआ है और फिर ०.६४४६ ९ ६6 तक चला है।

इसके विपरीत, C कार्यान्वयन केवल 0.644696874999999999470645661858725361526012420654296875 से सीधे आठ अंको तक गोल हो जाता है, जो 0.64469687 का उत्पादन करता है।

प्रारंभिक

Double , जावा IEEE-754 बुनियादी 64-बिट बाइनरी फ्लोटिंग-पॉइंट का उपयोग करता है। इस प्रारूप में, स्रोत पाठ में संख्या का निकटतम मान, 0.644696875, 0.644696874999999947064566185872536152626242420654296875 है, और मेरा मानना ​​है कि यह String.format("%10.8f",0.644696875) साथ स्वरूपित होने वाला वास्तविक मान है। 1

जावा विशिष्टता क्या कहती है

Double प्रकार और f प्रारूप के साथ प्रारूपण के लिए प्रलेखन कहता है:

… यदि सटीकता अंक की संख्या से कम है, जो कि Double.toString(double) Float.toString(float) या Double.toString(double) द्वारा लौटे स्ट्रिंग में दशमलव बिंदु के बाद क्रमशः दिखाई देगा, तो गोल आधा एल्गोरिथ्म का उपयोग करके मूल्य को गोल किया जाएगा । अन्यथा, शून्य को सटीक तक पहुंचने के लिए जोड़ा जा सकता है ...

चलो विचार करें "स्ट्रिंग द्वारा लौटाया गया ... Double.toString(double) "। नंबर के लिए 0.6446968749999999470645661858725361526012420654296875, यह स्ट्रिंग "0.644696875" है। ऐसा इसलिए है क्योंकि जावा स्पेसिफिकेशन का कहना है कि Double मानों के सेट के भीतर संख्या को विशिष्ट रूप से अलग करने के लिए स्ट्रींग केवल पर्याप्त दशमलव अंक पैदा करता है , और इस मामले में "0.644696875" के पास पर्याप्त अंक हैं। 2

दशमलव बिंदु के बाद उस संख्या में नौ अंक होते हैं, और "%10.8f" आठ अनुरोध करता है, इसलिए ऊपर दिए गए उद्धरण में कहा गया है कि "मान" गोल है। किस मूल्य का यह अर्थ है- format का वास्तविक संचालन, जो कि 0.644696874999999947064566185872536152626242420654296875 है, या उस स्ट्रिंग का उल्लेख है, "0.64469475"? चूंकि उत्तरार्द्ध एक संख्यात्मक मान नहीं है, इसलिए मुझे पूर्व के अर्थ के लिए "मूल्य" की उम्मीद होगी। हालाँकि, दूसरा वाक्य कहता है, "अन्यथा [यदि अधिक अंकों का अनुरोध किया जाता है], शून्य को जोड़ा जा सकता है ..." यदि हम format के वास्तविक ऑपरेंड का उपयोग कर रहे थे, तो हम इसके अंकों को दिखाएंगे, शून्य का उपयोग नहीं करेंगे। लेकिन, यदि हम स्ट्रिंग को एक संख्यात्मक मान के रूप में लेते हैं, तो इसके दशमलव प्रतिनिधित्व में दिखाए गए अंकों के बाद केवल शून्य होगा। तो ऐसा लगता है कि यह व्याख्या का इरादा है, और जावा कार्यान्वयन उसी के अनुरूप दिखाई देते हैं।

इसलिए, "%10.8f" साथ इस संख्या को प्रारूपित करने के लिए, हम पहले इसे 0.644696875 में परिवर्तित करते हैं और फिर इसे राउंड हाफ अप नियम का उपयोग करके राउंड करते हैं, जो 0.64469688 का उत्पादन करता है।

यह एक बुरा विनिर्देश है क्योंकि:

  • इसके लिए दो चक्कर लगाने पड़ते हैं, जिससे त्रुटि बढ़ सकती है।
  • गोलाई हार्ड-टू-प्रेडिक्ट और हार्ड-टू-कंट्रोल स्थानों में होती है। दो दशमलव स्थानों के बाद कुछ मानों को गोल किया जाएगा। 13. के बाद कुछ राउंड किया जाएगा। कोई प्रोग्राम आसानी से यह अनुमान नहीं लगा सकता है या इसके लिए समायोजित नहीं कर सकता है।

(इसके अलावा, यह एक शर्म की बात है कि उन्होंने लिखा "शून्य हो सकता है" जोड़ा जा सकता है। क्यों नहीं "अन्यथा, शून्य को सटीक तक पहुंचने के लिए जोड़ा जाता है" "हो सकता है" के साथ, ऐसा लगता है जैसे वे कार्यान्वयन को एक विकल्प दे रहे हैं, हालांकि मुझे संदेह है? मतलब "हो सकता है" की भविष्यवाणी इस बात पर की गई है कि क्या ज़ीरो को सटीकता तक पहुँचाने की आवश्यकता है, इस बात पर नहीं कि क्या कार्यान्वयनकर्ता उन्हें जोड़ना चाहता है।)

पाद लेख

1 जब स्रोत पाठ में 0.644696875 को Double में परिवर्तित किया जाता है, तो मेरा मानना ​​है कि परिणाम को Double प्रारूप में निकटतम 0.644696875 होना चाहिए। (मैंने इसे जावा प्रलेखन में स्थित नहीं किया है, लेकिन यह पहचान के व्यवहार को कार्यान्वित करने की आवश्यकता के जावा दर्शन को फिट करता है, और मुझे संदेह है कि रूपांतरण Double.valueOf(String s) अनुसार किया जाता है, जिसे इसकी आवश्यकता होती है ।) Double से 0.644696875, 0.6446968749999999470645661858725361526012420654296875 है।

2 कम अंकों के साथ, सात-अंकीय 0.64469687 अपर्याप्त है, क्योंकि इसका निकटतम मान 0.6446968 6999999997777191940404832847416400909423828125 है । तो आठ अंको को विशिष्ट रूप से भेदने के लिए 0.6446968 7499999994946666658725361526012420654296875 की आवश्यकता है

फ्लोटिंग पॉइंट नंबर 0.644696875 पर विचार करें। आइए इसे जावा और C का उपयोग करके आठ दशमलव तक एक स्ट्रिंग में परिवर्तित करें:

जावा

import java.lang.Math;
public class RoundExample{
     public static void main(String[] args){
        System.out.println(String.format("%10.8f",0.644696875));
     }
}

परिणाम: 0.6446968 8

इसे स्वयं आज़माएँ: http://tpcg.io/oszC0w

सी

#include <stdio.h>

int main()
{
    printf("%10.8f", 0.644696875); //double to string
    return 0;
}

परिणाम: 0.6446968 7

इसे स्वयं आज़माएं: http://tpcg.io/fQqSRF

प्रश्न

अंतिम अंक अलग क्यों है?

पृष्ठभूमि

नंबर ०.६४४६ ९ ६ cannot68५ को मशीन नंबर के रूप में प्रतिनिधित्व नहीं किया जा सकता। इसे अंश 2903456606016923/4503599627370496 के रूप में दर्शाया गया है जिसका मान 0.6446968749999999 है

यह वास्तव में एक किनारे का मामला है। लेकिन मैं वास्तव में अंतर के स्रोत के रूप में उत्सुक हूं।

संबंधित: https://mathematica.stackexchange.com/questions/204359/is-numberform-double-rounding-numbers


संभवतः यहाँ क्या हो रहा है कि वे संख्या को स्ट्रिंग में बदलने के लिए थोड़े अलग तरीकों का उपयोग कर रहे हैं, जो एक गोल त्रुटि का परिचय देता है। यह भी संभव है कि जिस विधि से संकलन के दौरान स्ट्रिंग को एक फ्लोट में परिवर्तित किया जाता है, वह उनके बीच भिन्न होता है, जो फिर से गोलाई के कारण थोड़ा अलग मान दे सकता है।

हालांकि याद रखें, फ्लोट में इसके अंश के लिए सटीकता के 24 बिट्स हैं, जो ~ 7.22 दशमलव अंकों [log10 (2) * 24] के लिए निकलता है, और पहले 7 अंक उनके बीच सहमत होते हैं, इसलिए यह केवल अंतिम कुछ महत्वपूर्ण बिट्स हैं विभिन्न।

फ़्लोटिंग पॉइंट मठ की मजेदार दुनिया में आपका स्वागत है, जहाँ 2 + 2 हमेशा 4 के बराबर नहीं होता है।







printf