Frage

Ich kodiere mehrere Referenzalgorithmen sowohl in Java als auch in C/C ++. Einige dieser Algorithmen verwenden π. Ich möchte, dass die beiden Implementierungen jedes Algorithmus produzieren identisch Ergebnisse, ohne unterschiedlich abzurunden. Eine Möglichkeit, dies zu tun, die bisher konsequent funktioniert hat pi Konstant, was in beiden Sprachen genau gleich ist, wie beispielsweise 3.14159. Es kommt mir jedoch als albern an, PI zu definieren, wenn bereits in den Java- und GCC-Bibliotheken Konstanten mit hoher Präzision definiert sind.

Ich habe einige Zeit damit verbracht, schnelle Testprogramme zu schreiben, die Dokumentation für jede Bibliothek zu betrachten und über Gleitkomma-Typen zu lesen. Aber ich konnte mich nicht davon überzeugen, dass Java.lang.math.pi (oder java.lang.strictmath.pi) in Math.H.H.

GCC 3.4.4 (Cygwin) Math.h enthält:

#define M_PI            3.14159265358979323846
                                         ^^^^^

aber dieses

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

produziert

3.14159265358979311600
                 ^^^^^

Dies deutet darauf hin, dass die letzten 5 Ziffern nicht vertrauenswürdig sind.

In der Zwischenzeit sagen Javadocs, dass Java.lang.math.pi::

Das double Wert, der näher als jeder andere ist zu Pi, das Verhältnis des Umfangs eines Kreises zu seinem Durchmesser.

und

public static final double PI  3.141592653589793d

was die fragwürdigen letzten fünf Ziffern aus der Konstante weglassen.

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

produziert

3.14159265358979300000
                 ^^^^^

Wenn Sie über Fachkenntnisse in Floating-Punkt-Datentypen verfügen, können Sie mich davon überzeugen, dass diese Bibliothekskonstanten genau gleich sind? Oder dass sie definitiv nicht gleich sind?

War es hilfreich?

Lösung 4

Ja, sie sind gleich, und die Verwendung dieser Verwendung wird sicherstellen pi konstant würde.

Eine Einschränkung, angedeutet von S. lott, ist, dass die GCC -Implementierung gelten muss M_PI in einem double Datentyp und nicht long double, um die Äquivalenz zu gewährleisten. Sowohl Java als auch GCC scheinen die 64-Bit-Dezimalvertretung von IEEE-754 für ihren jeweiligen double Datentypen. Die byteweise Darstellung (MSB bis LSB) des Bibliothekswerts, ausgedrückt als a double, kann wie folgt erhalten werden (dank Jebee):

pi_bytes.c:

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

pi_bytes.java:

class pi_bytes
{
   public static void main(String[] a)
   {
      System.out.printf("%016x\n", Double.doubleToRawLongBits( Math.PI ) );
   }
}

Beide laufen:

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

$ javac pi_bytes.java && java pi_bytes
400921fb54442d18

Die zugrunde liegenden Darstellungen von M_PI (Als ein double) und Math.PI sind identisch, bis zu ihren Teilen.

† - Wie festgestellt von Steve Schnepp, Es ist nicht garantiert, dass die Ausgabe von Mathematikfunktionen wie Sünde, Cos, Exp usw. identisch ist, selbst wenn die Eingaben zu diesen Berechnungen bitweise identisch sind.

Andere Tipps

Beachte das Folgende.

Die beiden Zahlen sind mit 16 Dezimalstellen gleich. Das sind fast 48 Bit, die gleich sind.

In einer IEEE 64-Bit-schwimmenden Punktzahl sind dies alle Teile, die keine Anzeichen oder Exponenten gibt.

Das #define M_PI hat 21 Ziffern; Das sind ungefähr 63 Bit Präzision, was gut für einen 80-Bit-Schwimmpunktwert IEEE ist.

Was ich denke, Sie sehen M_PI Wert.

Sie möchten das Rohbitmuster für die PI -Werte ausdrucken und vergleichen.

In Java benutzen Sie die http://java.sun.com/j2se/1.5.0/docs/api/java/lang/double.html#doubletorawlongbits(double) Methode, um den langen Wert zu erhalten, den Sie als binär drucken sollten.

Java 5 gibt:

  • PI ist 3.141592653589793
  • Rohbits ist 4614256656552045848
  • Binary ist 1000000000010010010000111111011010100010001000010110100011000

In C können Sie tun double pi = M_PI; printf("%lld\n", pi); Um die gleiche 64 -Bit -Ganzzahl zu erhalten: 4614256656552045848 (danke Bruno).

Es wäre sehr schwierig, den gleichen Wert zu berechnen, Auch wenn die Startwerte gleich sind.

Die Ergebnisse der Gleitkomma -Berechnung unterscheiden sich irgendwann von einer Architektur zu einem anderen (z. B. x86/powerpc), von einem Compiler zu einem anderen (denken Sie an GCC/MS C ++) und sogar mit demselben Compiler, aber unterschiedlichen Kompilierungsoptionen. Nicht immer, aber manchmal (normalerweise beim Absenken). Normalerweise genug, damit das Problem bis zu spät unbemerkt bleibt (denken Sie nach vielen vielen Iterationen und vielen, vielen Rundungsunterschieden)

Dies macht es für plattformübergreifende Multi-Player-Spiele ziemlich schwierig, die jede Iteration des GameState synchron berechnen (jeder Knoten empfängt nur die Eingabe, nicht die tatsächlichen Datenstrukturen).

Wenn auch in derselben Sprache (C/C ++) Ergebnisse unterschiedlich sein kann, kann dies auch unterschiedlich sein.

Aktualisieren:

Ich kann die Quelle, die ich gelesen habe, nicht finden, aber ich habe eine gefunden Papier von Sonne zu diesem Thema.

Als hätten Sie sich selbst geantwortet, Java.lang.math.pi und GCCs M_PI können den gleichen Wert haben. Der Teufel versteckt sich in der Verwendung dieser Werte. IEEE gibt nicht die Ausgabe von Mathematikfunktionen an (sin, cos, exp, ...). Deshalb ist es das Ausgabe der Berechnung Das ist nicht unbedingt dasselbe.

Ein Doppel hat nur 52 Bit Significand, daher denke ich, dass Sie nur etwa 15 Basis 10 -Ziffern erhalten, was erklären würde, warum Sie 5 Nullen haben, wenn Sie nach 20 Ziffern fragen.

Sie können BigDecimal für mehr Genauigkeit verwenden wie:

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));
}

Bringt Erinnerungen daran, einen Wert für PI in Forran zu erhalten.

Da es keine Bibliotheken von Konstanten gab, habe ich entweder 4*Atan (1.) oder ACOS (-1) verwendet.

Nein, sie sind nicht gleich, sie haben eine andere Präsentation im Gedächtnis.

Im Allgemeinen, wenn Sie 2 schwimmende Punktwerte vergleichen möchten, dürfen Sie == nicht verwenden (und wenn ja, können Sie nicht mit termin 'gleichen' arbeiten). Sie sollten einen Vergleich mit Epsilon verwenden.

double eps = 0.0000001;
if (Math.abs (Java_PI - Another_Pi) <= eps)
  System.out.println ("equals");
Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top