2009-05-05 8 views
5

मैं जावा और सी/सी ++ दोनों में कई संदर्भ एल्गोरिदम कोडिंग कर रहा हूं। इनमें से कुछ एल्गोरिदम π का उपयोग करते हैं। मैं समान परिणामों को अलग-अलग गोल किए बिना, प्रत्येक एल्गोरिदम के दो कार्यान्वयन के लिए चाहूंगा। ऐसा करने का एक तरीका जिसने लगातार काम किया है, एक कस्टम-डिफ़ाइंड pi स्थिरता का उपयोग करना है जो 3.1415 9 जैसे दोनों भाषाओं में बिल्कुल समान है। हालांकि, यह जावा और जीसीसी पुस्तकालयों में पहले से ही उच्च परिशुद्धता स्थिरांक परिभाषित होने पर पीआई को परिभाषित करने के लिए मूर्खतापूर्ण रूप से हमला करता है।क्या java.lang.Math.PI जीसीसी के एमएफआई के बराबर है?

मैंने कुछ समय तक त्वरित परीक्षण कार्यक्रम लिखने, प्रत्येक पुस्तकालय के लिए प्रलेखन को देखने और फ़्लोटिंग-पॉइंट प्रकारों पर पढ़ने के लिए कुछ समय बिताया है। लेकिन मैं खुद को यह समझाने में सक्षम नहीं हूं कि java.lang.Math.PI (या java.lang.StrictMath.PI) math.h में M_PI के बराबर है, या नहीं है।

जीसीसी 3.4.4 (cygwin) math.h शामिल हैं:

#define M_PI   3.14159265358979323846 
             ^^^^^ 

लेकिन इस

printf("%.20f", M_PI); 

3.14159265358979311600 
       ^^^^^ 

जो पता चलता है कि अंतिम 5 अंक भरोसा नहीं किया जा सकता है का उत्पादन ।

इस बीच, Javadocs का कहना है कि java.lang.Math.PI है:

double मूल्य कि करीब अनुकरणीय, करने के लिए एक चक्र की परिधि के अनुपात के लिए किसी भी अन्य है इसके व्यास।

और

public static final double PI 3.141592653589793d 

जो निरंतर से संदिग्ध अंतिम पाँच अंक अस्वीकार करते हैं।

System.out.printf("%.20f\n", Math.PI); 

पैदा करता

3.14159265358979300000 
       ^^^^^ 

आप फ्लोटिंग प्वाइंट डेटा प्रकार में कुछ विशेषज्ञता है, तो आप मुझे समझा सकते हैं कि इन पुस्तकालय स्थिरांक बिल्कुल बराबर हैं? या वे निश्चित रूप से बराबर नहीं हैं?

उत्तर

2

हाँ, वे बराबर हैं, और उन्हें लगता है कि जीसीसी बीमा होगा और एक ही एल्गोरिथ्म के जावा कार्यान्वयन एक हाथ से परिभाषित pi निरंतर और कटार होगा का उपयोग कर के रूप में कम से कम के रूप में ज्यादा एक ही स्तर – पर हैं उपयोग करते हुए;

एक चेतावनी, S. Lott द्वारा संकेत दिया, कि जीसीसी कार्यान्वयन एक double डेटा प्रकार में M_PI, और नहीं long double होना बहुत जरूरी है तुल्यता सुनिश्चित करने के लिए है। जावा और जीसीसी दोनों अपने संबंधित double डेटा प्रकारों के लिए आईईईई -754 के 64-बिट दशमलव प्रतिनिधित्व का उपयोग करते हैं।

pi_bytes.c:

#include <math.h> 
#include <stdio.h> 
int main() 
{ 
    double pi = M_PI; 
    printf("%016llx\n", *((uint64_t*)&pi)); 
} 

pi_bytes.java:

bytewise प्रतिनिधित्व पुस्तकालय मूल्य का (MSB के लिए LSB), एक double के रूप में व्यक्त, के रूप में ( JeeBee करने के लिए धन्यवाद) इस प्रकार प्राप्त किया जा सकता
class pi_bytes 
{ 
    public static void main(String[] a) 
    { 
     System.out.printf("%016x\n", Double.doubleToRawLongBits(Math.PI)); 
    } 
} 

दोनों चल रहा है:

$ gcc -lm -o pi_bytes pi_bytes.c && ./pi_bytes 
400921fb54442d18 

$ javac pi_bytes.java && java pi_bytes 
400921fb54442d18 

अंतर्निहित प्रतिनिधि M_PI (double के रूप में) और Math.PI के असंतोष उनके बिट्स के समान हैं।

और डैगर; – जैसा कि Steve Schnepp द्वारा उल्लेख किया गया है, गणित कार्यों जैसे पाप, कोस, एक्सप इत्यादि का उत्पादन समान होने की गारंटी नहीं है, भले ही उन गणनाओं के इनपुट बिटव समान हैं।

8

आप जो करना चाहते हैं वह पीआई मानों के लिए कच्चे बिट पैटर्न को प्रिंट करना और उनकी तुलना करना है।

जावा में http://java.sun.com/j2se/1.5.0/docs/api/java/lang/Double.html#doubleToRawLongBits(double) विधि का उपयोग करें जो आपको बाइनरी के रूप में प्रिंट करना चाहिए।

जावा 5 देता है:

  • पीआई 3 है।4614256656552045848 (धन्यवाद ब्रूनो): 141592653589793
  • कच्चे बिट्स आप double pi = M_PI; printf("%lld\n", pi); कर सकते हैं एक ही 64 बिट पूर्णांक प्राप्त करने के लिए, 4614256656552045848
  • बाइनरी 100000000001001001000011111101101010100010001000010110100011000

सी में है।

+3

सी में, आप डबल पीआई = एमएफआई कर सकते हैं; printf ("% lld \ n", pi); एक ही 64 बिट पूर्णांक प्राप्त करने के लिए: 4614256656552045848 –

+1

धन्यवाद ब्रूनो, जो कि मूल प्रश्न का बहुत अधिक उत्तर देता है। – JeeBee

-3

नहीं, वे बराबर नहीं हैं, उनके पास स्मृति में अलग-अलग प्रस्तुति है।

सामान्यतः जब आप 2 फ़्लोटिंग पॉइंट मानों की तुलना करना चाहते हैं तो आपको == का उपयोग नहीं करना चाहिए (और यदि ऐसा है तो आप टर्मिनल 'बराबर' के साथ काम नहीं कर सकते हैं)। आपको ईपीएसलॉन के साथ तुलना का उपयोग करना चाहिए।

double eps = 0.0000001; 
if (Math.abs (Java_PI - Another_Pi) <= eps) 
    System.out.println ("equals"); 
1

एक डबल केवल तरह signficand 52 बिट तो मैं लगता है कि केवल आप लगभग 15 आधार में 10 अंक है जो की व्याख्या करता है तुम क्यों 5 शून्य है जब आप 20 अंकों के लिए पूछना देता है।

11

निम्नलिखित नोट करें।

दो संख्याएं 16 दशमलव स्थानों के समान हैं। यह लगभग 48 बिट्स हैं जो समान हैं।

आईईईई 64-बिट फ्लोटिंग-पॉइंट नंबर में, यह सभी बिट्स हैं जो संकेत या एक्सपोनेंट नहीं हैं।

#define M_PI में 21 अंक हैं; यह लगभग 63 बिट परिशुद्धता है, जो एक आईईईई 80-बिट फ्लोटिंग-पॉइंट मान के लिए अच्छा है।

मुझे लगता है कि आप क्या देख रहे हैं M_PI मूल्य में बिट्स का सामान्य छंटनी है।

+0

+1 बड़े पैमाने पर चरमपंथी सटीक समय डेल्टा माप करते समय इस समस्या को ठीक से चलाएं। 15 अंकों को आप 64-बिट डबल के साथ प्राप्त करते हैं। – basszero

3

उसी मूल्य की गणना करना बहुत मुश्किल होगा, भले ही शुरुआती मान समान हों

फ़्लोटिंग पॉइंट गणना परिणाम कुछ समय से एक आर्किटेक्चर से दूसरे (लगता है कि x86/PowerPC उदाहरण के लिए) एक कंपाइलर से दूसरे (सोचें जीसीसी/एमएस सी ++) और यहां तक ​​कि एक ही कंपाइलर के साथ, लेकिन विभिन्न संकलन विकल्पों के साथ। हमेशा नहीं, लेकिन कभी-कभी (आमतौर पर गोल करते समय)। आम तौर पर बहुत देर तक अनजान होने के लिए पर्याप्त समस्या (बहुत से पुनरावृत्तियों के बाद सोचें, और कई गोल करने वाले मतभेदों के बाद सोचें)

यह क्रॉस-प्लेटफ़ॉर्म मल्टी-प्लेयर गेम के लिए काफी मुश्किल बनाता है जो गैमेस्टेट के प्रत्येक पुनरावृत्ति को समकालिक रूप से गणना करता है (प्रत्येक नोड केवल इनपुट प्राप्त करता है, न कि वास्तविक डेटा संरचनाओं)।

इसलिए यदि जावा वीएम से देशी होस्ट में भी एक ही भाषा (सी/सी ++) परिणाम अलग-अलग हो सकते हैं तो यह अलग भी हो सकता है।

अद्यतन:

मैं स्रोत मैंने पढ़ा नहीं मिल सकता है, लेकिन मैं इस मामले पर एक paper by Sun पाया।

जैसा कि आपने स्वयं उत्तर दिया है, java.lang.Math.PI और GCC के M_PI को समान मान प्रबंधित किया जा सकता है। शैतान इन मूल्यों के उपयोग में छुपाता है। आईईईई गणित कार्यों (पाप, कॉस, एक्सपी, ...) के आउटपुट को निर्दिष्ट नहीं करता है। इसलिए यह गणना का आउटपुट है जो आवश्यक नहीं है।

+0

शायद यह सामान्य मामले में एक वास्तविक समस्या है, लेकिन यहां नहीं। जैसा कि इस सवाल में उल्लेख किया गया है, "ऐसा करने का एक तरीका जो लगातार काम करता है, एक कस्टम-परिभाषित पीआई स्थिरता का उपयोग करना है जो 3.1415 9 जैसे दोनों भाषाओं में बिल्कुल समान है।" –

+0

एक संदर्भ लिंक जोड़ा गया। –

1

आप की तरह अधिक सटीकता के लिए BigDecimal उपयोग कर सकते हैं:

private static final BigDecimal PI = new BigDecimal(
"3.1415926535897932384626433832795028841971693993751058209749445923078164062862089986280348253421170679" + 
    "8214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196" + 
    "4428810975665933446128475648233786783165271201909145648566923460348610454326648213393607260249141273" + 
    "7245870066063155881748815209209628292540917153643678925903600113305305488204665213841469519415116094" + 
    "3305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912" + 
    "9833673362440656643086021394946395224737190702179860943702770539217176293176752384674818467669405132" + 
    "0005681271452635608277857713427577896091736371787214684409012249534301465495853710507922796892589235" + 
    "4201995611212902196086403441815981362977477130996051870721134999999837297804995105973173281609631859" + 
    "5024459455346908302642522308253344685035261931188171010003137838752886587533208381420617177669147303" + 
    "5982534904287554687311595628638823537875937519577818577805321712268066130019278766111959092164201989" + 
    "3809525720106548586327886593615338182796823030195203530185296899577362259941389124972177528347913151" + 
    "5574857242454150695950829533116861727855889075098381754637464939319255060400927701671139009848824012" + 
    "8583616035637076601047101819429555961989467678374494482553797747268471040475346462080466842590694912" + 
    "9331367702898915210475216205696602405803815019351125338243003558764024749647326391419927260426992279" + 
    "6782354781636009341721641219924586315030286182974555706749838505494588586926995690927210797509302955" + 
    "3211653449872027559602364806654991198818347977535663698074265425278625518184175746728909777727938000" + 
    "8164706001614524919217321721477235014144197356854816136115735255213347574184946843852332390739414333" + 
    "4547762416862518983569485562099219222184272550254256887671790494601653466804988627232791786085784383" + 
    "8279679766814541009538837863609506800642251252051173929848960841284886269456042419652850222106611863" + 
    "0674427862203919494504712371378696095636437191728746776465757396241389086583264599581339047802759009" + 
    "9465764078951269468398352595709825822620522489407726719478268482601476990902640136394437455305068203" + 
    "4962524517493996514314298091906592509372216964615157098583874105978859597729754989301617539284681382" + 
    "6868386894277415599185592524595395943104997252468084598727364469584865383673622262609912460805124388" + 
    "4390451244136549762780797715691435997700129616089441694868555848406353422072225828488648158456028506" + 
    "0168427394522674676788952521385225499546667278239864565961163548862305774564980355936345681743241125" 
); 

public static void main(String... args) throws InterruptedException { 
    System.out.println("PI to " + PI.scale() + " digits is " + PI); 
    System.out.println("PI^2 to " + PI.scale() + " digits is " + 
      PI.multiply(PI).setScale(PI.scale(), BigDecimal.ROUND_HALF_UP)); 
} 
+1

उम, धन्यवाद। और जीसीसी के लिए? –

-1

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

चूंकि स्थिरांक की कोई पुस्तकालय नहीं थी, इसलिए मैंने 4 * एटन (1।) या एकोस (-1।) का उपयोग किया था।

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