Sprachunterschiede – PHP:C#

Geschreiben von Marcus KimpenhausaufApr 30, 2008 in entwicklung |

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…

Schlagwörter:, ,

7 Kommentare

  • Martin sagt:
    Safari 525.18 Mac OS X

    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

  • Mozilla Firefox 3.0b5 Windows XP

    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.

  • Martin sagt:
    Safari 525.18 Mac OS X

    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.

  • Martin sagt:
    Safari 525.18 Mac OS X

    Hi,

    habe naturlich 2**1000 eingegeben ;-)

  • Martin sagt:
    Safari 525.18 Mac OS X

    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.

  • Mozilla Firefox 3.0b5 Mac OS X

    Hm,

    das heißt aber sowohl für den Namespace Microsoft.Scripting als auch für Microsoft.FSharp.Math ist 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.

  • Otto Vielstein sagt:
    Internet Explorer 7.0 Windows XP

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

Hinterlasse eine Antwort

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind markiert *

*

Du kannst folgende HTML-Tags benutzen: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

Get Adobe Flash playerPlugin by wpburn.com wordpress themes

Copyright © 2005-2010 marcus' tagebuch All rights reserved.
Desk Mess Mirrored v1.6 theme from BuyNowShop.com.