Dezimalzahl in (gekürzten) Bruch umwandeln (UDF)

  • Hier meine beiden kleinen Funktionen:

    Dezimalzahl in Bruch umwandeln
    [autoit]

    #include-once
    #include <Array.au3>

    [/autoit] [autoit][/autoit] [autoit]

    Func _DezimalzahlInBruch ($fDezimal, $bKuerzen = True)
    Local $aVorkomma[2], $aNachkomma[2], $aBruch[2]

    [/autoit] [autoit][/autoit] [autoit]

    $aZahl = StringSplit($fDezimal, "", 2);StringLen($fDezimal) = UBound($aZahl)-1

    [/autoit] [autoit][/autoit] [autoit]

    If StringInStr($fDezimal, ",") Then StringReplace($fDezimal, ",", ".", 1);optional: Ersetzung eines Dezimalkommas durch einen Dezimalpunkt
    $iPunkt = _ArraySearch($aZahl, ".")
    If $iPunkt < 0 Then SetError(1);Zahl ohne Dezimaltrennzeichen

    [/autoit] [autoit][/autoit] [autoit]

    For $i = 0 To $iPunkt-1
    $aVorkomma[1] &= $aZahl[$i]
    Next
    $aVorkomma[0] = StringLen($aVorkomma[1])

    [/autoit] [autoit][/autoit] [autoit]

    $aNachkomma[0] = StringLen($fDezimal)-($iPunkt+1);Länge der Nachkommazahl
    For $i = $iPunkt+1 To UBound($aZahl)-1
    $aNachkomma[1] &= $aZahl[$i];Nachkommazahl
    Next

    [/autoit] [autoit][/autoit] [autoit]

    $aBruch[0] = $aNachkomma[1];Dividend
    $aBruch[1] = 10^$aNachkomma[0];= 10^StringLen($aNachkomma[1]);Divisor

    [/autoit] [autoit][/autoit] [autoit]

    Select
    Case $aVorkomma[0] > 1 Or $aVorkomma[1] > 0
    $aBruch[0] += $aVorkomma[1] * $aBruch[1]
    If $bKuerzen = False Then
    Return $aBruch
    Else
    ContinueCase
    EndIf
    Case Else
    Return _BruchKuerzen ($aBruch[0], $aBruch[1])
    EndSelect
    EndFunc

    [/autoit]
    Bruch kürzen
    [autoit]

    Func _BruchKuerzen ($iDividend, $iDivisor)
    Local $aBruch[2] = [$iDividend, $iDivisor], $aTeiler[2] = [2, 5]
    Do
    For $i = 0 To 1
    If Not Mod($aBruch[0], $aTeiler[$i]) And Not Mod($aBruch[1], $aTeiler[$i]) Then
    For $n = 0 To 1
    $aBruch[$n] = $aBruch[$n] / $aTeiler[$i]
    Next
    EndIf
    Next
    Until (Mod($aBruch[0], $aTeiler[0]) Or Mod($aBruch[1], $aTeiler[0])) And (Mod($aBruch[0], $aTeiler[1]) Or Mod($aBruch[1], $aTeiler[1]))
    Return $aBruch
    EndFunc

    [/autoit]
    Beispielskript
    [autoit]

    Global $aErgebnis[2], $fDezimal = 18.8

    [/autoit] [autoit][/autoit] [autoit]

    $aErgebnis = _DezimalzahlInBruch ($fDezimal, False)
    ConsoleWrite($fDezimal & " = " & $aErgebnis[0] & " / " & $aErgebnis[1] & @CRLF)
    $aErgebnis = _DezimalzahlInBruch ($fDezimal, True)
    ConsoleWrite($fDezimal & " = " & $aErgebnis[0] & " / " & $aErgebnis[1] & @CRLF)
    Exit

    [/autoit]
  • Interessante Vorgehensweise, aber sind die Arrays wirklich notwendig? Wenn du es ohne Arrays umwandeln würdest würde außerdem immer ein gekürzter Bruch herauskommen (wenn du die Methode verwenden würdest die ich gerade im Kopf habe), du bräuchtest also die 2. Funktion nicht mehr. ^^

  • Ich hätte das so gemacht:
    Die Nachkommastellen werden mit jeder Zahl von 2 bis zu dem Limit multipliziert und danach wird geschaut ob das Ergebnis 1 ist. So findet man automatisch immer den Bruch mit dem kleinsten Nenner zuerst.

    Spoiler anzeigen
    [autoit]

    MsgBox(64, "-16.0625", _(-16.0625))
    Func _($n, $l=10000)
    Local $i = 0, $b = ($n < 0)
    Local $f = Abs($n), $s = ""
    If ($f > 1) Then
    $i = Int($f)
    $f = $f - $i
    EndIf
    For $j = 2 To $l Step 1
    If ($f*$j = 1) Then
    If $b Then $s &= "-("
    If $i Then $s &= $i & " + "
    $s &= ($f*$j) & "/" & $j
    If $b Then $s &= ")"
    Return $s
    EndIf
    If ($f*$j > 1) Then ExitLoop
    Next
    Return SetError(1, 0, $n)
    EndFunc

    [/autoit]
  • Du kannst auch Brüche durch den größten gemeinsamen Teiler kürzen.

    Beispiel:

    [autoit]


    ConsoleWrite("18.8 = " & Float2Frac(18.8) & @LF)
    ConsoleWrite("0.75 = " & Float2Frac(0.75) & @LF)

    [/autoit][autoit][/autoit][autoit][/autoit][autoit]

    Func Float2Frac($fFloat)
    $iDec = StringLen(StringRegExpReplace($fFloat, "\d+\.(\d*)", "\1"))
    $iZaehler = $fFloat * 10^$iDec
    $iNenner = 10^$iDec
    $iGGT = ggT($iZaehler, $iNenner)
    Return $iZaehler / $iGGT & " / " & $iNenner / $iGGT
    EndFunc

    [/autoit][autoit][/autoit][autoit]

    Func ggT($a, $b) ;coded by UEZ 2012
    If Not IsInt($a) Then Return SetError(1, 0, 0)
    If Not IsInt($b) Then Return SetError(2, 0, 0)
    If $a = $b Then Return Abs($a)
    If Not $a And $b Then Return Abs($a)
    If $a And Not $b Then Return Abs($b)
    If ($a And $b = 1) Or ($a = 1 And $b) Then Return 1
    Local $iMod
    Do
    $iMod = Mod($a, $b)
    If Not $iMod Then ExitLoop
    $a = $b
    $b = $iMod
    Until False
    Return $b
    EndFunc

    [/autoit]

    Gruß,
    UEZ

    Auch am Arsch geht ein Weg vorbei...

    ¯\_(ツ)_/¯

  • UEZ:
    Anstatt Deiner eigenen ggT-Funktion könnte man auch den Euklidischen Algorithmus verwenden (den Du in Deiner Do-Until-Schleife praktisch verwendest):

    [autoit]

    Func _EuklidischerAlgorithmus_I ($a, $b);Berechnet den größten gemeinsamen Teiler der beiden Zahlen $a und $b [rekursive Version]
    If $b = 0 Then
    Return $a
    Else
    Return _EuklidischerAlgorithmus_I ($b, Mod($a, $b))
    EndIf
    EndFunc

    [/autoit][autoit]

    Func _EuklidischerAlgorithmus_II ($a, $b);s. o. [iterative Version]
    Local $h
    While $b <> 0
    $h = Mod($a, $b)
    $a = $b
    $b = $h
    WEnd
    Return $a
    EndFunc

    [/autoit]