2009-08-11 12 views
0

Ok Ich habe zwei Funktionen von denen die ersten wie so aussieht:f # ganze Zahl Float Modul 1,0 = 1,0?

let dlth x = float (x.ToString().Length) 

, die einen Schwimmer nimmt und gibt die Anzahl der Stellen, der Teil gut funktioniert. Die zweite Funktion sieht wie folgt aus:

let droot x = ((x ** (1./(dlth x))) % 1.) 

der ein Schwimmer nimmt und hebt es an eine Strom gleich 1,0/(Anzahl der Ziffern), nimmt dann das Ergebnis und macht Modul 1.0. Was für eine ganze Zahl Null sein sollte.

so für droot 36. es dauert (36,0 ** (1.0/2.0)) was 6.0 ist dann 6.0 mod 1.0 gleich 0.0;

Jetzt funktioniert das gut bis dahin, wo ich die Nummer 81.0 versuche. (und alle Zahlen höher als 81, die funktionieren sollten), die aus irgendeinem Grund 1.0 zurückgeben und meinen Mustervergleich abwerfen. Kann mir jemand sagen, warum das passiert?

PostScript: Dies ist Teil einer Project Euler-Lösung. Wenn Sie wissen, welches Problem, bitte NICHT post die Projekt Euler Lösung. Ich brauche nur Hilfe herauszufinden, warum der Modul ist lustig zurück Ergebnisse

Antwort

8

Fließkomma-Arithmetik ist voller Gefahr in jeder Sprache. Auf meiner Box

printfn "%f" (0.9999999999999 % 1.0)   

druckt

1.000000 

Hoffentlich, dass Sie in die richtige Richtung wird dazu beitragen, lenken. Wenn Sie wirklich herausfinden wollen, ob ein Gleitkomma "eine Ganzzahl" ist, dann z. Subtrahieren der nächsten ganzen Zahl und Sehen, ob der absolute Wert kleiner als einige Epsilon (z. B. 0,00001) ist, ist ein angemessener Einsatz.

+0

Ich sehe, was du meinst, aber ich denke, mit dem Bereich der Zahlen, die ich überprüfe, wäre es halb unmöglich, einen Test zu erstellen, der für alles funktioniert. Ich denke, was ich tun werde, ist zu sehen, ob ich es überarbeiten kann, um ganz zu vermeiden, schwimmt. – AvatarOfChronos

0

Ich stimme Brian zu, dass es sehr gefährlich ist, sich auf exakte Fließkommadarstellungen zu verlassen, aber ich kann Ihr Problem nicht reproduzieren. Für mich gibt droot 81.0 das erwartete Ergebnis 0.0. Welche Version von F # benutzt du? Laufen Sie auf .NET oder Mono?

+0

Ich habe es auf beiden versucht. Der einzige Unterschied ist, wo das Problem beginnt. auf .net fängt es an, komisch auf 216 zu handeln. auf mono fängt es an, lustig auf 81 zu handeln. beide mono und .net haben das gleiche Problem, das sie einen falschen Modul mit diesem Code zurückbringen. und ich verwende F # 1.9.6.16 – AvatarOfChronos

+0

Ja, das Problem ist, dass (216.0 ** (1.0/3.0)) gibt 5.9999999999999991 ... Der Modulo-Operator gibt dann 0,9 ..., die als 1,0 angezeigt wird. Sie können sich selbst beweisen, dass es nicht wirklich 1.0 ist, indem Sie einen einfachen Gleichheitsvergleich durchführen. Unglücklicherweise müssen Sie sich eine robustere Methode überlegen, um zu berechnen, was Sie wollen. – kvb

+0

Ich sehe nicht, wie die Kubikwurzel von 216 zurückkommt als 5.9999999 ... 1. Ich denke, ich werde herausfinden müssen, wie .NET mathematische Fließkommaoperationen ausführt und was ich davon lernen kann. – AvatarOfChronos

1

Wenn Sie Float-Ungenauigkeiten erhalten, könnten Sie den Performance-Hit mit .NET decimal Datentyp (der in base-10 darstellbare Zahlen darstellen kann) nehmen. Ansonsten verwenden Sie mehr Genauigkeit (double) oder Integer-Mathematik.