Dezimalzahlen in Bruch umwandeln

Anfängerfragen zum Programmieren mit PureBasic.
Nino
Beiträge: 1300
Registriert: 13.05.2010 09:26
Wohnort: Berlin

Re: Dezimalzahlen in Bruch umwandeln

Beitrag von Nino »

Hi Stargåte,

interessanter Code, danke!

Die Prüfung

Code: Alles auswählen

If IsInfinity(Double) Or IsNAN(Double) Or Double > 1e16 Or Double < 1e-16
   ProcedureReturn ""
EndIf
sollte weiter zu Beginn der Prozedur durchgeführt werden, teste mal

Code: Alles auswählen

Debug ToFraction(NaN())
;-)

Und warum all dies in einen Topf werfen? Ich würde es eher so machen:

Code: Alles auswählen

If IsInfinity(Double)
   ProcedureReturn StrD(Infinity())
ElseIf IsNAN(Double)
   ProcedureReturn StrD(NaN())
EndIf
Die Prüfung auf Infinity sollte dann VOR

Code: Alles auswählen

If Double < 0
stehen, damit zwischen +Infinity und -Infinity unterschieden werden kann.

Mich irritiert ein bisschen die Bezeichnung "MaxRelativeError". Relativ wozu? Sind 1e-6 usw. nicht absolute Fehler?
Benutzeravatar
STARGÅTE
Kommando SG1
Beiträge: 6999
Registriert: 01.11.2005 13:34
Wohnort: Glienicke
Kontaktdaten:

Re: Dezimalzahlen in Bruch umwandeln

Beitrag von STARGÅTE »

Nino hat geschrieben:Mich irritiert ein bisschen die Bezeichnung "MaxRelativeError". Relativ wozu? Sind 1e-6 usw. nicht absolute Fehler?
Der Fehler ist relativ zur Zahl selbst. Also eine Art Präzision der Zahl.
1e-6 heißt, dass etwa 6 Ziffern (vor + nach dem Komma) richtig sein sollten.
Somit geben ToFraction(0.3333333) und ToFraction(33333.33) beide etwas mit drittel zurück.
Um die Prüfung werde ich mich noch mal kümmern.
PB 6.01 ― Win 10, 21H2 ― Ryzen 9 3900X, 32 GB ― NVIDIA GeForce RTX 3080 ― Vivaldi 6.0 ― www.unionbytes.de
Aktuelles Projekt: Lizard - Skriptsprache für symbolische Berechnungen und mehr
Nino
Beiträge: 1300
Registriert: 13.05.2010 09:26
Wohnort: Berlin

Re: Dezimalzahlen in Bruch umwandeln

Beitrag von Nino »

Ich habe den Code für meine Bedürfnisse entspr. meiner obigen Bemerkungen angepasst. Die eigentliche Berechnung habe ich nicht geändert. Vielen Dank nochmal für den schönen Code!

Anm.: Die in PB eingebaute Funktion Assert() ist leider erst verfügbar, nachdem man die Datei "PureUnit.res" in den Unterordner "Residents\" verschoben hat.

Code: Alles auswählen

Procedure.s ToFraction (Double.d, MaxRelativeError.d=1e-6)
   Protected.q LowNumerator, LowDenominator
   Protected.q HighNumerator, HighDenominator
   Protected.q Numerator, Denominator
   Protected.d Fraction, OldFraction
   Protected.i Factor, Iteration
   Protected sign$
   
   If IsNAN(Double) Or IsInfinity(Double)
      ProcedureReturn StrD(double)         ; NaN, +Infinity or -Infinity
   EndIf
   
   If Double < 0
      Double = Abs(Double)
      sign$ = "-"
   EndIf
   
   If Double > 1e16
      ProcedureReturn "Overflow"
   ElseIf Double < 1e-16
      ProcedureReturn "Underflow"
   EndIf
   
   LowNumerator = 0
   LowDenominator = 1
   HighNumerator = 1
   HighDenominator = Round(1/Double, #PB_Round_Down) - 1
   Numerator   = LowNumerator  + HighNumerator
   Denominator = HighNumerator + HighDenominator
   Fraction = Infinity()
   
   Repeat
      Iteration + 1
      OldFraction = Fraction
      Fraction = 1 - Double * Denominator / Numerator
      ; Debug "Iteration "+RSet(Str(Iteration),3)+":  " + Str(Numerator) + " / " + Str(Denominator) + "   ( δ = "+Fraction+" )"
      Factor = Abs(Fraction) / (Abs(OldFraction) - Abs(Fraction))
      If Factor < 1 Or Fraction > 0 Or OldFraction > 0
         Factor = 1
      EndIf
      If Fraction < -MaxRelativeError          ; zu klein
         LowNumerator   = Numerator
         LowDenominator = Denominator
         Numerator   + Factor*HighNumerator
         Denominator + Factor*HighDenominator
      ElseIf Fraction > MaxRelativeError       ; zu groß
         HighNumerator   = Numerator
         HighDenominator = Denominator
         Numerator   + Factor*LowNumerator
         Denominator + Factor*LowDenominator
      Else
         ProcedureReturn sign$ + Str(Numerator) + "/" + Str(Denominator)
      EndIf
   ForEver
EndProcedure


CompilerIf #PB_Compiler_IsMainFile
   ; -- Ein paar Brüche:
   Assert(ToFraction(0.375) = "3/8")
   Assert(ToFraction(0.3333333) = "1/3")
   Assert(ToFraction(0.0625) = "1/16")
   Assert(ToFraction(-1.15) = "-23/20")
   Assert(ToFraction(13.4) = "67/5")
   
   Assert(ToFraction(127/307) = "127/307")
   Assert(ToFraction(1018/717.0) = "1018/717")
   Assert(ToFraction(-73/105.0) = "-73/105")
   Assert(ToFraction(1.0/1000.0) = "1/1000")
   Assert(ToFraction(7.0/10000.0) = "7/10000")
   Assert(ToFraction(999.0/1000.0) = "999/1000")
   Assert(ToFraction(9999.0/10000.0, 1e-8) = "9999/10000")
   Assert(ToFraction(499999.0/999999.0, 1e-12) = "499999/999999")
   
   ; -- Näherungsbrüche für Pi:   ; https://de.wikipedia.org/wiki/Kreiszahl#Näherungsbrüche_der_Kreiszahl
   Assert(ToFraction(#PI, 1e-3) = "22/7")
   Assert(ToFraction(#PI, 1e-5) = "355/113")
   
   ; -- Spezielle Argumente:
   Assert(ToFraction(NaN()) = "NaN")
   Assert(ToFraction( Infinity()) = "+Infinity")
   Assert(ToFraction(-Infinity()) = "-Infinity")
   Assert(ToFraction( 1e17)  = "Overflow")
   Assert(ToFraction(-1e17)  = "Overflow")
   Assert(ToFraction( 1e-17) = "Underflow")
   Assert(ToFraction(-1e-17) = "Underflow")
CompilerEndIf
Antworten