2012-04-12 5 views
9

Ich portiere Programm von C# nach Java. Ich habe eine Tatsache konfrontiert, dassMath "pow" in Java und C# zurück etwas andere Ergebnisse?

Java

Math.pow(0.392156862745098,1./3.) = 0.7319587495200227 

C#

Math.Pow(0.392156862745098, 1.0/3.0) =0.73195874952002271 

diese letzte Ziffer, um einen ausreichenden Unterschiede führen in weiteren Berechnungen. Gibt es eine Möglichkeit, C# 's Powder zu emulieren?

Thanx

+5

Ihr Code wird, auch nicht reproduzierbare Ergebnisse in .net geben, so es vergessen . Related: http://stackoverflow.com/questions/6683059/are-floating-point-numbers-consistent-in-c-can-they-be – CodesInChaos

+12

Wirklich? Die 17. signifikante Ziffer gibt Ihnen "ausreichende Unterschiede in weiteren Berechnungen"? Können Sie uns ein Beispiel geben? –

+2

Wenn man es mit calc.exe & wolfram alpha vergleicht, ist die 1 sowieso falsch. Ich würde bei der Implementierung von Java bleiben. –

Antwort

34

Nur um zu bestätigen, was Chris Shain schrieb, bekomme ich die gleichen Binärwerte:

// Java 
public class Test 
{ 
    public static void main(String[] args) 
    { 
     double input = 0.392156862745098; 
     double pow = Math.pow(input, 1.0/3.0);    
     System.out.println(Double.doubleToLongBits(pow)); 
    } 
} 

// C# 
using System; 

public class Test 
{ 
    static void Main() 
    { 
     double input = 0.392156862745098; 
     double pow = Math.Pow(input, 1.0/3.0);    
     Console.WriteLine(BitConverter.DoubleToInt64Bits(pow)); 
    } 
} 

Ausgabe von beiden: 4604768117848454313

Mit anderen Worten sind die doppelten Werte genau das gleiche Bitmuster und alle Unterschiede, die Sie sehen (vorausgesetzt, Sie erhalten die gleichen Ergebnisse), sind auf Formatierung anstatt auf einen Unterschied im Wert zurückzuführen. By the way, ist der genaue Wert dieses Doppels

0.73195874952002271118800535987247712910175323486328125 

Jetzt lohnt es sich unter Hinweis darauf, dass deutlich seltsame Dinge in Gleitkomma-Arithmetik passieren können, vor allem, wenn Optimierungen ermöglichen 80-Bit-Arithmetik in einigen Situationen, andere aber nicht, usw.

Wie Henk sagt, wenn ein Unterschied in den letzten ein oder zwei Problemen Sie Probleme verursacht, dann ist Ihr Design gebrochen.

+2

+1 für das Follow-up, das die Äquivalenz des binären Ausgangs zeigt. –

+1

* "bei der Fließkomma-Arithmetik können merkwürdige Dinge passieren, insbesondere wenn Optimierungen eine 80-Bit-Arithmetik zulassen" * - Weitere Informationen finden Sie in meiner [Frage zur C# Fließkomma-Konsistenz] (http://stackoverflow.com/questions/ 6683059/sind-Fließkommazahlen-konsistent-in-c-können sie sein) –

+0

Nein, Math.pow unterscheidet sich in C# und Java. Bitte beachten Sie Double.doubleToLongBits (Math.pow (0.39215686274509803,1.0/3.0)) und BitConverter.DoubleToInt64Bits (Math.Pow (0.39215686274509803,1.0/3.0)). –

18

Wenn Ihre Berechnungen auf diese Art von Unterschied empfindlich sind, dann werden Sie andere Maßnahmen müssen (ein Redesign).

+6

kann ich nicht widersprechen, aber das ist nicht viel von einer "Antwort" an und für sich ... –

+3

Es ist keine direkte Antwort, nein. Aber das ist das Problem mit [XY-Problemen] (http://meta.stackexchange.com/questions/66377). Erwarten Sie ein Stück Java - Code, um C# 's pow zu emulieren? –

8

Sowohl Java als auch C# geben eine IEEE-Gleitkommazahl (speziell eine Doppel) aus Math.Pow zurück. Der Unterschied, den Sie sehen, ist fast sicher auf die Formatierung zurückzuführen, wenn Sie die Zahl als Dezimalzahl anzeigen. Der zugrunde liegende (binäre) Wert ist wahrscheinlich der gleiche, und Ihre Mathe-Probleme liegen woanders.

+1

Es ist leicht möglich, dass die letzten Ziffern tatsächlich abweichen. .net garantiert keine spezifischen Ergebnisse und macht von dieser Freiheit Gebrauch. – CodesInChaos

+1

Beide geben einen doppelten * typed * -Wert zurück - es sei denn, es wird ein FPU "POW" verwendet (z. B. ist es eine Funktion der FPU, keine "strikten" Einstellungen in den Sprachen), dann könnte der Algorithmus verwendet werden sei subtil anders ... wäre interessant, einen kleinen Ausschnitt zu haben, um dies hier zu zeigen. –

1

Gleitkommaarithmetik ist inhärent ungenau. Sie behaupten, dass die C# -Antwort "besser" ist, aber keiner von ihnen ist so genau. Zum Beispiel Wolfram Alpha (die in der Tat sehr viel genauer ist) gibt diese Werte:

http://www.wolframalpha.com/input/?i=Pow%280.392156862745098%2C+1.0+%2F+3.0%29

Wenn eine Einheit Unterschied in der 17. Stelle wird später Berechnungen verursacht schief gehen, dann denke ich, dass es ein Problem mit Ihrem Mathe, nicht mit Java-Implementierung von pow. Sie müssen darüber nachdenken, wie Sie Ihre Berechnungen so umstrukturieren, dass sie sich nicht auf so kleine Unterschiede verlassen.

+0

Es gibt Situationen, in denen Sie die letzten Ziffern der tatsächlichen Ergebnisse nicht interessieren, solange sie konsistent und reproduzierbar sind. Aber in diesem Fall können Sie die eingebauten Floatingpoint-Typen von .net nicht verwenden. – CodesInChaos

+1

Ich wurde auf diesem Punkt korrigiert - Fließkomma-Mathematik ist sehr präzise.Das Problem ist, dass es auf ** binären ** Zahlen operiert, so dass es keine genaue, endende Konvertierung von dezimal zu binär geben kann (ähnlich gibt es keine genaue terminierende Repräsentation von 1/3 in Dezimal, aber in Basis-3 würde es ausgedrückt werden als .1). Wenn Ihre Eingaben nicht genau in Binärform ausgedrückt werden können, werden sie gerundet, und wenn Sie Ihre Ausgabe als Dezimalzahl formatieren, kann sie erneut gerundet werden. –

+0

"Sehr präzise" ist ein subjektiver Begriff. Es ist wahrscheinlich präziser, als Sie jemals brauchen werden, wenn Sie nicht mit sehr chaotischen Systemen arbeiten. Mein Punkt ist, dass es mit einer begrenzten Anzahl von Speicherplatz (64 Bits) eine Obergrenze für die Genauigkeit gibt, mit der Sie Zahlen unabhängig von der Basis ausdrücken können. –

13

diese letzte Ziffer führt zu einer ausreichenden Unterschiede in weiteren Berechnungen

das unmöglich ist, weil sind sie die gleiche Anzahl.A double hat nicht genug Präzision, um zwischen 0.7319587495200227 und 0.73195874952002271 zu unterscheiden; sie sind beide vertreten als

0.73195874952002271118800535987247712910175323486328125. 

Der Unterschied die Rundung ist: Java wird mit 16 signifikanten Stellen und C# 17 verwendet Aber das ist nur ein Display Problem.