Sprachunterschiede – PHP:C#
Das ich derzeit den mathematischen Aufgaben des projecteuler.net verfallen bin, habe ich bereits erwähnt. Bei der Lösung des 16. Problems
2^15 = 32768 and the sum of its digits is 3 + 2 + 7 + 6 + 8 = 26.
What is the sum of the digits of the number 2^1000?
bin ich dann an auf etwas gestoßen, was sich mir nicht auf Anhieb erschließen will. Ich habe bis dahin alle gestellten Probleme mit C# auf Basis des .NET-Frameworks 3.5 gelöst und so wollte ich auch dieses Problem mit genannten Werkzeugen angehen. Leider konnte ich keine zufriedenstellende Lösung erarbeiten, da die Genauigkeit (oder besser Ungenauigkeit) des double-Datentyps bei der Berechnung
Math.Pow(2d, 1000d).ToString("f0");
zu klein (oder zu groß) war.
10715086071862700000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
00000000000000000000000000000000000000000000000000000000000000000000000
000000000000000000
Gelöst habe ich den ganzen Spökes dann mit PHP. Ein ähnlicher Einzeiler liefert das präzise Ergebnis:
number_format(pow(2, 1000), 0, '', '');
10715086071862673209484250490600018105614048117055336074437503883703510
51124936122493198378815695858127594672917553146825187145285692314043598
45775746985748039345677748242309854210746050623711418779541821530464749
83581941267398767559165543946077062914571196477686542167660429831652624
386837205668069376
Nun werde ich mich also auf die Suche begeben, um festzustellen, warum die Genaugikeit unter C# so stark abweicht (wie man sieht, ist ab der 15. Stelle aufgerundet worden). Wenn jemand die Antwort schon kennt, ich würde mich freuen und es würde mir wahrscheinlich etwas Zeit (von der man eh nie genug hat) sparen.
1. Nachtrag:
Die Microsoft Hilfe sagt es bereits:
The double type contains 64 bits: 1 for sign, 11 for the exponent, and 52 for the mantissa. Its range is +/–1.7E308 with at least 15 digits of precision.
Also mit 15 Stellen Genauigkeit…wie schon erwähnt, wurde ab der 15. Stelle gerundet. Welchen Datentyp kann ich also verwenden, um eine höhere Genauigkeit zu erhalten?! Die Suche geht weiter…
2. Nachtrag
Die Umsetzung erfolgt nach IEEE-754-Standard…also dort mal schauen.
3. Nachtrag
Ok – es ist also amtlich C# unterstützt dies in keinster Weise! Sehr Schade! Vor allem vor dem Hintergrund, dass für das .NET Framework 3.5 im Namespace System.Numeric u.a. ein Datentyp BigInteger geplant war (siehe: Introducing: System.Numeric.BigInteger [Inbar Gazit]), welcher aber ab der Beta 2 nicht mehr enthalten war (siehe: Microsoft .NET Framework 3.5 Beta 2 is Now Available [Inbar Gazit] (in den Kommentaren ist nachzulesen, dass der Datentyp entfernt wurde)). Die Relikte scheinen heute noch sichtbar, kann man doch einen nahezu (bis auf einige wenige Klassen und Delegates) leeren Namespace System.Numeric einbinden.
Noch interessanter wird die Geschichte, wenn man bedenkt, dass die VM Datentypen dieser Genauigkeit unterstützt. Woher ich das weiß? Nun – da kommen wir auch zu dem einzigen Lösungsansatz der sich mir derzeit ergibt: F#.
Eine Möglichkeit den Datentyp BigInteger der F# Bibliothek zu benutzen findet Ihr hier (Auch wenn dort noch von einem BigInteger im Namespace System.Numeric die Rede ist, nein, er wurde nicht umgesetzt!).
Nach der Installation und dem Hinzufügen der Referenz FSharp.Core kann man nun auch mit .NET Bordmitteln 2^1000 mit absoluter Genauigkeit ausrechnen lassen (*hüstel* und zwar mit BigNum und nicht BigInteger
):
BigNum result = BigNumModule.powi(BigNumModule.of_int(2), 1000);
Das Ergebnis erstrahlt zu:
10715086071862673209484250490600018105614048117055336074437503883703510
51124936122493198378815695858127594672917553146825187145285692314043598
45775746985748039345677748242309854210746050623711418779541821530464749
83581941267398767559165543946077062914571196477686542167660429831652624
386837205668069376
Nun kann ich doch beruhigt einschlafen…
Aus meiner Sicht solltest Du zumindest den Datentyp System.Decimal verwenden, da er wie unter (http://msdn.microsoft.com/de-de/library/364x0z75(VS.80).aspx) beschrieben 28-29 Stellen genau arbeitet.
cu Martin
Decimal kann nicht verwendet werden, da es einen maximalen Wert von
+/-79,228,162,514,264,337,593,543,950,335
(+/-7.9228162514264337593543950335E+28)
annehmen kann, was für dieses Problem viel zu klein ist. Zwar hätte Decimal die höchste Genauigkeit, wäre aber dennoch nicht geeignet, da auch 29 signifikante Stellen nicht ausreichend wären.
Hi,
man sollte den Beitrag auch korrekt lesen
(habe ich nun getan).
Was mich wundert: Ich habe unter IronPython 2*1000 eingegeben und das gleiche Ergebnis erzielt wie Du mit PHP. Ich werde mal in den Quellen von IronPython (in C# geschrieben) nachsehen und Dir dann berichten, wie die Kollegen die Lösung herbei”gezaubert” haben.
Hi,
habe naturlich 2**1000 eingegeben
Hi,
wenn es richtig verstanden haben, ist im Namespace “Microsoft.Scripting” die Klasse BigInteger für das Arbeiten mit großen Zahlen implementiert worden.
Na ja.
Hm,
das heißt aber sowohl für den Namespace
Microsoft.Scriptingals auch fürMicrosoft.FSharp.Mathist jeweils eine Zusatz-Installation von Nöten (zum Einen IronPython zum Anderen F#). Was ich äußerst Schade finde, da gewisse Bereiche einfach out of the box nicht abgedeckt werden können. Sicher bietet die funktionale Programmierung für wissenschaftlich Zwecke bessere Möglichkeiten, aber dennoch kann ich mir Szenarien ausmalen, in denen auch eine höhere Genauigkeit als 15 Stellen erforderlich sind und diese mit Bordmitteln nicht umsetzbar sind. Vielleicht legt Microsoft ja in Zukunft doch noch die BigInteger und BigNum Implementierung im System.Numeric Namespace nach. Zu hoffen wäre es.Hi Marcus, in C# kannst du diese einfache Aufgabe nur lösen wenn du dir ein Modul mit hinreichender Arithmetik schreibst (was ja z.T. auch Absicht des Autors ist) . Ich habe selbst u.a. Mathematica verwende es bei Euler aber nicht, da dies reichlich unfair wäre (und nicht im Sinne des Erfinders!). In Sprachen wie Python, Ruby oder Smalltalk z.B., die alle über beliebige Genauigkeit verfügen, ist die Aufgabe schlicht simpel.
Beachte ferner, dass zur Umwandlung in einen String und String nach numerischen Wert die Bedingung “ord(str[i])-48″ den Wert der i-Stelle des Strings liefert. Der Rest ist ein Kinderspiel und Bedarf der 303maligen Summierung (Length(string(2^1000))=304)…
In Component Pascal, auch Black Box genannt, hast du ein kostenloses Oberon-2 System, das eine arbitrary arithmetic mitliefert…
So long OV
PS.: nicht alle Aufgaben bei Euler sind korrekt, be careful