Rundungsfunktion selbst schreiben

mit matplotlib, NumPy, pandas, SciPy, SymPy und weiteren mathematischen Programmbibliotheken.
Antworten
hannah01
User
Beiträge: 2
Registriert: Freitag 4. Dezember 2020, 18:56

Hallo :)

ich - absoluter noob ohne irgendeine Ahnung - würde/muss eine Funktion implementieren, die mir einen float auf l Nachkommastellen rundet. Das Problem ist, dass ich überall nur die Information finde, dass es dafür eine Funktion gibt - diese darf ich aber nicht benutzen. Was ich habe, ist eine Funktion, die mir eine normalisierte Dasrtellung meiner Zahl a la 0,... auspuckt. Jetzt weiß ich nicht mehr weiter.. Rechnerisch weiß ich, was passieren muss; falls die l+1-ste Stelle meines floats > 4 ist, muss ich an der xl-ten Stelle +1 rechnen, aber wie ich das jetzt technisch umsetze.. Keine Ahnung.

Falls mir jemand helfen könnte, wäre ich wirklich sehr dankbar!
Benutzeravatar
sparrow
User
Beiträge: 4187
Registriert: Freitag 17. April 2009, 10:28

Eine Möglichkeit: Du wandelst das den Wert in eine Zeichenkette und arbeitest damit. Auf die eizelnen "Elemente" - sprich Zeichen kannst du dann beliebig zugreifen.
Benutzeravatar
__blackjack__
User
Beiträge: 13077
Registriert: Samstag 2. Juni 2018, 10:21
Wohnort: 127.0.0.1
Kontaktdaten:

@hannah01: Wie soll denn gerundet werden? So wie `round()`, oder so wie Du das im Beitrag beschreibst?

Ansonsten wäre ein Tipp da nichts mit Darstellungen zu machen sondern über ganze Zahlen zu gehen.
„All religions are the same: religion is basically guilt, with different holidays.” — Cathy Ladman
tonikae
User
Beiträge: 90
Registriert: Sonntag 23. Februar 2020, 10:27

hannah01 hat geschrieben: Freitag 4. Dezember 2020, 19:07 würde/muss eine Funktion implementieren, die mir einen float auf l Nachkommastellen rundet. Das Problem ist, dass ich überall nur die Information finde, dass es dafür eine Funktion gib
So spontan würde mir da folgendes einfallen.

Code: Alles auswählen

import math

a=3.45346

print(a)
print ("{0:.4f}".format(a)) 
Das rundet die 4.Nachkommstelle(nach den üblichen 1-4 und 5-9-Regeln), abhängig von der 5. Nachkommstelle.
Ob es das ist was du suchst,weiss ich natürlich nicht.
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@sparrow: bei der Stringdarstellung von floats gibt es viele Fälle zu beachten.
@tonikae: nein, es wird nicht so gerundet, wie man es in der Schule lernt, sondern statistisch ausgeglichen. Und ich glaube kaum, dass wenn round nicht erlaubt ist, dieser Weg erlaubt ist.
tonikae
User
Beiträge: 90
Registriert: Sonntag 23. Februar 2020, 10:27

Sirius3 hat geschrieben: Samstag 5. Dezember 2020, 10:43 @sparrow: bei der Stringdarstellung von floats gibt es viele Fälle zu beachten.
@tonikae: nein, es wird nicht so gerundet, wie man es in der Schule lernt, sondern statistisch ausgeglichen. Und ich glaube kaum, dass wenn round nicht erlaubt ist, dieser Weg erlaubt ist.
Das zu beurteilen solltest du mal besser dem OP überlassen, anstatt hier den Klugscheisser zu spielen.
hannah01
User
Beiträge: 2
Registriert: Freitag 4. Dezember 2020, 18:56

tonikae hat geschrieben: Samstag 5. Dezember 2020, 15:22
Sirius3 hat geschrieben: Samstag 5. Dezember 2020, 10:43 @sparrow: bei der Stringdarstellung von floats gibt es viele Fälle zu beachten.
@tonikae: nein, es wird nicht so gerundet, wie man es in der Schule lernt, sondern statistisch ausgeglichen. Und ich glaube kaum, dass wenn round nicht erlaubt ist, dieser Weg erlaubt ist.
Das zu beurteilen solltest du mal besser dem OP überlassen, anstatt hier den Klugscheisser zu spielen.
Ich darf keine Funktion aus irgendeiner Bibliothek benutzen sondern soll alles selbst schreiben.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

tonikae hat geschrieben: Samstag 5. Dezember 2020, 15:22 Das zu beurteilen solltest du mal besser dem OP überlassen, anstatt hier den Klugscheisser zu spielen.
Ich habe dich bereits mehrfach angezaehlt. Das ist die letzte Warnung: bei der naechsten Beschimpfung bist du raus.

Und in der Sache hat Sirius3 natuerlich eh recht. Das du das nicht erkennst, sondern stattdessen zum Angriff blaest, spricht Baende. Wer auch immer heute oder in Zukunft mit dir zusammen arbeiten muss, dem spreche ich mein tief empfundenes Mitleid aus. Rechthaberische Vollpfosten ohne Ahnung braucht keiner im Team.

Nachtrag: der TE hat das (wie zu erwarten) bestaetigt.
__deets__
User
Beiträge: 14528
Registriert: Mittwoch 14. Oktober 2015, 14:29

@hannah01: der von __blackjack__ genannte Weg ist der vielversprechenste. Wenn du aus dem gegebenen x fuer die Stelle die entsprechende Zehnerpotenz berechnest, kannst du deine Zahl damit erstmal multiplizieren. Und danach durch eine einfache Subtraktion den Rest bestimmen. Ob der dann groesser oder kleiner gleich 0.4 ist, bestimmt, ob du dann addierst oder nicht. Und danach wieder durch die Zehnerpotenz dividieren.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1017
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Kaufmännisches runden ist nicht so toll.
Aber das könnte man selbst implementieren.

Code: Alles auswählen

def kround(number, ndigits=None):
    """
    Kaufmännisches runden.
    """
    if ndigits is not None:
        exponent = 10 ** ndigits
    else:
        exponent = 1
    number *= exponent
    number, rest = divmod(number, 1)
    if rest >= 0.5:
        number += 1
    number /= exponent
    return number
>>> kround(3.55, 1) == round(3.55, 1)
False

>>> kround(3.54, 1) == round(3.54, 1)
True
Das symmetrische Runden ist besser, wenn man z.B. Einzelwerte erst rundet und dann aufsummiert.


Zitat: https://de.wikipedia.org/wiki/Rundung#S ... hes_Runden
Das kaufmännische Runden erzeugt kleine systematische Fehler, da das Aufrunden um 0,5 vorkommt, das Abrunden um 0,5 jedoch nie; das kann Statistiken geringfügig verzerren. Die mathematische Rundung rundet von der genauen Mitte zwischen zwei Ziffern immer zur nächsten geraden Ziffer auf oder ab. Dadurch wird im Mittel etwa ebenso oft auf- wie abgerundet, zumindest wenn die Ursprungszahlen stochastisch sind. (Gegenbeispiel: Sind kleine Zahlen häufiger als große, kann systematisch häufiger nach unten als nach oben gerundet werden, siehe Benfordsches Gesetz.)
PS: Diese Anfeindungen sind scheiße!
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

@DeaD_EyE: wichtig ist es, bei den Testfällen auch alle Fälle abzudecken, zum Beispiel negative Zahlen.
Wenn man als Defaultwert ndigits=0 wählt, braucht man auch keine Sonderbehandlung.
Benutzeravatar
DeaD_EyE
User
Beiträge: 1017
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Sirius3 hat geschrieben: Montag 7. Dezember 2020, 18:25 Wenn man als Defaultwert ndigits=0 wählt, braucht man auch keine Sonderbehandlung.
Das hätte ich wahrscheinlich sogar gemacht, wenn ich nicht einfach die Signatur von round kopiert hätte.
Dort ist komischerweise None der Standardwert, aber round ist ja in C implementiert.
Die Funktion round liefert ein int zurück, wenn ndigits ein None ist und wenn man ndigits angibt (int), bekommt man einen float.
Das hatte ich in der kround nicht implementiert und an negative Zahlen habe ich erst gar nicht gedacht.

Eine weitere Sache, die man wissen sollte, wie divmod funktioniert und speziell bei negativen Zahlen ist es etwas komisch.
Damit ich keinen Knoten im Kopf bekomme, nehme ich jetzt den Betrag (abs) und copiere vorher noch das Vorzeichen.

Das mit dem None habe ich auch drin gelassen, aber so erweitert, dass bei None immer ein Int zurückgeliefert wird.
Den Docstring habe ich zur Hälfte von Wikipedia geklaut.

Code: Alles auswählen

def kround(number, ndigits=None):
    """
    Kaufmännisches runden.
    
    Liefert einen int zurück, wenn
    ndigits == None
    
    Ansonsten gibt ndigits die
    Stellen hinter dem Komma an und
    ein Float wird zurück gegeben.
    
    
    Das Kaufmännische Runden (nicht negativer Zahlen) geschieht wie folgt:
    - Ist die Ziffer an der ersten wegfallenden
      Dezimalstelle eine 0, 1, 2, 3 oder 4,
      dann wird abgerundet.
    - Ist die Ziffer an der ersten wegfallenden
      Dezimalstelle eine 5, 6, 7, 8 oder 9,
      dann wird aufgerundet.
    
      13.3749 € ≈ 13.37 €
      13.3750 € ≈ 13.38 €

    >>> kround(13.3749, 2)
        13.37
    >>> kround(13.3750, 2)
        13.38
      
    Negative Zahlen werden nach ihrem Betrag gerundet,
    bei einer 5 also weg von null (engl: Away from Zero)

      -13.3749 € ≈ -13.37 €
      -13.3750 € ≈ -13.38 €
    
    >>> kround(-13.3749, 2)
        -13.37
    >>> kround(-13.3750, 2)
        -13.38
    
    Diese Rundungsregel wird durch die Norm DIN 1333 beschrieben.  
    """
    sign = -1 if number < 0 else 1
    if ndigits is not None:
        exponent = 10 ** ndigits
    else:
        exponent = 1
    number *= exponent
    number, rest = divmod(abs(number), 1)
    if rest >= 0.5:
        number += 1
    number /= exponent
    number *= sign
    if ndigits is None:
        return int(number)
    else:
        return number
Wenn man die Funktion verschönern will, beseitigt man am besten den Spezialfall None.

PS: Wer mag, kann ja ein Unittest schreiben. Aber Anfänger wollen sich wahrscheinlich damit nicht so lange aufhalten.
PPS: ndigits könnte auch negativ sein. Auch nicht getestet :-D
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
narpfel
User
Beiträge: 645
Registriert: Freitag 20. Oktober 2017, 16:10

@DeaD_EyE: Die Unittests sind schon da, man muss sie nur ausführen:

Code: Alles auswählen

python -m doctest kround.py
Benutzeravatar
DeaD_EyE
User
Beiträge: 1017
Registriert: Sonntag 19. September 2010, 13:45
Wohnort: Hagen
Kontaktdaten:

Leider falsche Einrückung, und deswegen sind die Doctests kaputt.
Habs mal korrigiert, aber doctests ersetzen keine Unittests und das sollte man sich nicht darauf verlassen.
Da ich sie aber reingeschrieben habe, hätte ich es auch gleich testen sollen.

Jedenfalls habe ich jetzt behalten, dass es die DIN 1333 ist.

Code: Alles auswählen

def kround(number, ndigits=None):
    """
    Kaufmännisches runden.
    
    Liefert einen int zurück, wenn
    ndigits == None
    
    Ansonsten gibt ndigits die
    Stellen hinter dem Komma an und
    ein Float wird zurück gegeben.
    
    
    Das Kaufmännische Runden (nicht negativer Zahlen) geschieht wie folgt:
    - Ist die Ziffer an der ersten wegfallenden
      Dezimalstelle eine 0, 1, 2, 3 oder 4,
      dann wird abgerundet.
    - Ist die Ziffer an der ersten wegfallenden
      Dezimalstelle eine 5, 6, 7, 8 oder 9,
      dann wird aufgerundet.
    
      13.3749 € ≈ 13.37 €
      13.3750 € ≈ 13.38 €

    >>> kround(13.3749, 2)
    13.37
    >>> kround(13.3750, 2)
    13.38
      
    Negative Zahlen werden nach ihrem Betrag gerundet,
    bei einer 5 also weg von null (engl: Away from Zero)

      -13.3749 € ≈ -13.37 €
      -13.3750 € ≈ -13.38 €
    
    >>> kround(-13.3749, 2)
    -13.37
    >>> kround(-13.3750, 2)
    -13.38
    
    >>> kround(99.9, -1)
    100.0
    
    >>> kround(123.123456789, 8)
    123.12345679
    
    >>> kround(123.123456789, 9)
    123.123456789
    
    >>> kround(13.50)
    14
    
    Diese Rundungsregel wird durch die Norm DIN 1333 beschrieben.  
    """
    sign = -1 if number < 0 else 1
    if ndigits is not None:
        exponent = 10 ** ndigits
    else:
        exponent = 1
    number *= exponent
    number, rest = divmod(abs(number), 1)
    if rest >= 0.5:
        number += 1
    number /= exponent
    number *= sign
    if ndigits is None:
        return int(number)
    else:
        return number
sourceserver.info - sourceserver.info/wiki/ - ausgestorbener Support für HL2-Server
Sirius3
User
Beiträge: 17741
Registriert: Sonntag 21. Oktober 2012, 17:20

etwas kompakter:

Code: Alles auswählen

def kround(number, ndigits=None):
    sign = -1 if number < 0 else 1
    exponent = 10 ** (ndigits or 0)
    number = int(number * 2 * exponent)
    number = (number + sign * (number % 2)) // 2
    return number if ndigits is None else number / exponent
Antworten