Frage deutsch
~~~~~~~~~~~~~~~~~
Wie runde ich Zahlen kaufmännisch/wissenschaftlich korrekt?
Question English
~~~~~~~~~~~~~~~~~
How to do correct commercial/scientific rounding of numeric values?
Antwort 1
~~~~~~~~~~~~~~~~~
[ von Thomas Antoni, 5.11.2002 - 26.2.2006 ]
Rundung von Zahlenwerten
*** Inhalt
0) Übersicht (für Eilige)
1) Einführung - Die Rundungsarten
2) Definition der kaufmännischen Rundung
3) Kaufmännische Rundung in QBasic
4) Definition der Wissenschaftlichen Rundung
5) Wissenschaftliche Rundung in QBasic
6) Kommastellen abschneiden
7) Immer aufrunden
8) Immer abrunden
9) Wie reduziere ich die Rundungsfehler der internen
Gleitpunktarithmetik?
10) Spezielle Rundungen, z.B. auf Vielfache von 0,25
*** 0) Übersicht (für Eilige)
Für die ganz Eiligen unter Euch kann ich das Thema "Rundung von Zahlen in QB"
sehr kurz abhandeln: Willst Du z.B. eine Kommazahl kaufmännisch korrekt auf zwei
Nachkommastellen gerundet anzeigen, so verwendest Du den INT- oder den PRINT
USING -Befehl gemäß dem folgenden Beispiel:
'Rundung mit 2 Nachkommastellen
INPUT "Gib eine Zahl mit Dezimalpunkt ein "; Zahl#
PRINT INT (Zahl# * 10^2 + 0.5) / 10^2 'gerundete Zahl anzeigen
PRINT USING "#######.##"; zahl# 'gerundete Zahl anzeigen
Anmerkungen
• Du solltest für die zu rundende Zahl
wegen der höheren Genauigkeit immer das doppelt lange Gleitpunktformat verwenden
(mit dem Typ-Bezeichner "#").
• Willst Du auf n statt auf zwei
Nachkommastellen runden, dann schreibst Du im 2. Befehl 10^n statt 10^2 und im
3. Befehl schreibst Du n statt 2 Gartenzaun-Zeichen "#" nach dem
Dezimalpunkt.
• Zu PRINT USING findest Du erschöpfende
Informationen im FAQ-Eintrag "Fragen zu speziellen QBasic-Befehlen -> Wie funktioniert PRINT
USING?"
• Extrem hohe Erwartungen an die
Genauigkeit kannst Du wegen der prinzipbedingten Fehler bei der
Gleitpunkt-Zahlendarstellung nicht haben; siehe den FAQ-Eintrag "Warum kommt es bei Gleitpunktoperationen zu
Rundungsfehlern?" . Bei sehr hohen Ansprüchen
an die Genauigkeit verwendest Du besser PRINT USING statt
INT.
• Für die "wissenschaftliche" oder
"mathematische Rundung" verwendest Du statt INT die Befehle CLNG() oder CINT().
Dann wird eine 5 nur dann aufgerundet, wenn links davor eine ungerade Ziffer
steht (für Statistik-Anwendungen usw.). Bei der oben geschilderten "normalen"
Kaufmännischen Rundung wird dagegen eine 5 immer aufgerundet.
Beispiele
'--- Nomale (Kaufmaennische) Rundung auf 2 Nachkommastellen
PRINT INT(3.425# * 100 + .5) / 100 '=====> Anzeige = 3.43
PRINT USING "#######.##"; 3.425 '========> Anzeige = 3.43
'
'--- Normale (Kaufmaennische) Rundung auf 3 Nachkommastellen
PRINT INT(12.8746# * 1000 + .5) / 1000 '=> Anzeige = 12.875
PRINT USING "#######.###"; 12.8746 '=====> Anzeige = 12.875
'
'--- Rundung auf ganze 100er
PRINT INT(12456 * .01 + .5) / .01 '======> Anzeige = 12500
'
'--- Unterschied zwischen Kaufmaennischer Rundung
'--- und Wissenschaftlicher Rundung
'--- (Rundung auf eine Nachkommastelle)
PRINT "286.25 kaufmaennisch auf 2 Nachkommastellen gerundet =";
PRINT CLNG(286.25# * 10) / 10 '======> Anzeige = 286.3
'
PRINT "286.25 wissenschftal. auf 2 Nachkommastellen gerundet =";
PRINT INT(286.25# * 10 + .5) / 10 '======> Anzeige = 286.2
Eilige Leute können hier aufhören, zu lesen. Das Thema Rundung hat jedoch
noch viele weitere hochinteressante Facetten, die einige von Euch vielleicht
interessieren und die ich in den folgenden Abschnitten mit allen Details
diskutieren möchte.
*** 1) Einführung - Die Rundungsarten
"Wie runde ich richtig?" ist seit jeher ein Dauerthema in allen QBasic-Foren.
Über die Problematik der Rundung könnte man ein ganzes Buch schreiben. Das Thema
ist viel facettenreicher als man auf den ersten Blick denkt. Ich habe noch kein
Tutoral oder Buch gefunden, in dem das Thema "Rundung" umfassend behandelt
worden wäre.
Der vorliegende Beitrag soll nun diese Lücke schließen.
Im Wesentlichen gibt es die folgenden Rundungsvarianten, die ich alle in
diesem Beitrag beleuchten werde:
- kaufmännische Rundung
- wissenschaftliche Rundung
- Kommastellen abschneiden
- immer aufrunden
- immer abrunden
Diese Probleme sind dabei zu beachten:
• Behandlung negativer
Zahlen
• Auswirkung der bei Gleitpunktzahlen
unvermeidbaren Rechenungenauigkeiten (siehe auch den FAQ-Eintrag "Warum kommt es bei Gleitpunktoperationen zu
Rundungsfehlern?" . Diese Ungenauigkeiten
können bei Verwendung der Gleitpunktarithmetik grundsätzlich immer dazu führen,
dass in Grenzfällen falsch gerundet wird. Wer das unter allen Umständen
vermeiden will, sollte wo immer möglich Integer-Arithmetik verwenden und z.B.
bei kaufmännischen Problemen alle Berechnungen mit CENT- statt mit EURO-Beträgen
durchführen und die Endergebisse "von Hand" runden. Oder Du verwendest
QuickBASIC 7.1/PDS, das für diesen Zweck den Datentyp "Currency" unterstützt
(Währungs-Typ mit Typ-Kennzeichen "@"). Auch -> PowerBASIC mit seinem
-> BCD-Gleitpunkt-Datentyp ist eine interessante Alternative
(Typkennzeichen "@@").
In QBasic stehen für das Runden die folgenden Befehle zur Verfügung:
- FIX => schneidet die Nachkommastellen
ab
- INT => gibt die größte Ganzzahl
zurück, die kleiner oder gleich dem numerischen Ausdruck ist.
- CINT => rundet einen numerischen
Ausdruck zu einer 2 Byte langen Ganzzahl (Datenformat INTEGER, Zahlenbereich
-32768 bis 32767)
- CLNG => rundet einen numerischen
Ausdruck zu einer 4 Byte langen Ganzzahl (Datenformat LONG, Zahlenbereich bis
ca. -2 Milliarden bis +2 Milliarden)
- (L)PRINT USING "FormatMaske" ==> gibt
formatierte Daten auf den Bildschirm, einen Drucker oder in eine Datei aus.
Dabei ist bei Zahlen die Anzahl der Vor- und Nachkommastellen
wählbar
*** 2) Definition der Kaufmännischen Rundung
Bei der Kaufmännischen Rundung werden Zahlen, die genau in der Mitte zwischen
zwei von den Kommastellen her noch darstellbaren Zahlen liegen, immer
aufgerundet. So wird z.B. 2.445 zu 2.45 aufgerundet. Man kann es auch so
ausdrücken: Eine zu rundende "5" wird bei der Kaufmännischen Rundung immer nach
oben aufgerundet.
Bei negativen Zahlen wird für die Rundung das Vorzeichen nicht beachtet.
-2.445 wird also zu -2.45 abgerundet ("abgerundet" weil -2.45 ja streng
arithmetisch gesehen kleiner ist als -2.44).
*** 3) Kaufmännische Rundung in QBasic
Zur Realisisierung der Kaufmännischen Rundung gibt es zwei Methoden: Die
erste verwendet den Befehl INT und die zweite PRINT USING.
Die Methode mit INT wird in den QB-Foren und Büchern
häufig empfohlen, obwohl sie ihre "Macken" hat. Sie funktioniert wie folgt, wenn
man auf "Stellen%" Nachkommastellen runden will:
INPUT "Anzahl der Kommastellen, auf die gerundet werden soll"; Stellen%
INPUT "Zu rundende doppelt lange Gleitpunktzahl"; Zahl#
Zahlgerundet# = INT(Zahl# * 10 ^ Stellen% + .5) / 10 ^ Stellen%
PRINT "Zahl nach der Rundung: "; Zahlgerundet#
Weniger bekannt ist die Rundung mit Hilfe des Befehls PRINT USING.
Diese Methode ist nur verwendbar in Kombination mit einer Bildschirmausgabe oder
dem Schreiben in eine Datei oder auf einen Drucker. Die Syntax dieses Befehls
lautet so:
PRINT USING "######.###" ; variable
Dabei stehen die "#" für die anzuzeigenden Zeichen vor bzw. hinter dem
Dezimalpunkt. PRINT USING arbeitet präziser als INT, wie ich weiter unten
beweisen werde.
Mein folgendes Programm RUNDUNG1.BAS demonstriert beide Methoden, die Rundung
mit INT und die Rundung mit PRINT USING:
'************************************************************
' RUNDUNG1.BAS - Kaufmaennische Rundung in QBasic
' =====================================================
' Kaufmaennische Rundung von Dezimalzahlen auf 2
' Nachkommastellen. Bei Zahlen, die genau in der Mitte
' zwischen zwei darstellbaren Werten liegen, wird auf die
' naechstgroessere Zahl gerundet.
'
' Hierzu stellt QBasic die Befehle INT und PRINT USING
' zur Verfuegung. Dieses Programm demonstriert beide
' Befehle, wobei PRINT USING in vielen Fällen exakter
' arbeitet (z.B. beim Runden der Zahl 2.445 zu 2.45)
'
' Von Thomas Antoni, 5.5.2003 - 7.5.2003
'************************************************************
CLS
DO
PRINT "Gibt eine pos. oder neg. Zahl mit Dezimalpunkt ein"
INPUT z#
PRINT INT(z# * 100 + .5) / 100
LOCATE , 10: PRINT "...gerundet mit Methode INT"
PRINT USING "######.##"; z#
LOCATE , 10: PRINT "...gerundet mit Methode USING"
PRINT "Abbrechen...{[Esc] Wiederholen...[beliebige Taste]"
DO: taste$ = INKEY$: LOOP WHILE taste$ = ""
LOOP UNTIL taste$ = CHR$(27)
END
Das obige Programm steht im Verzeichnis Progs\ zur Verfügung sowie online
unter www.antonis.de/faq/progs/rundung1.bas .
Wie man durch Eingabe der Zahl 2.445 leicht feststellen kann, ist die Methode
mit USING vorzuziehen, weil sie weniger "anfällig" gegenüber Rundungsfehlern der
internen Gleitpunktverarbeitung ist:
Methode mit INT ==> 2.445 wird zu 2.44 (falsch)
Methode mit USING ==> 2.445 wird zu 2.45 (korrekte kaufmänn. Rundung)
*** 4) Definition der wissenschaftlichen Rundung
Die wissenschaftliche Rundung funktioniert wie folgt:
2,444 wird zu 2,44
2,446 wird zu 2,45
***
2,445 wird zu 2,44
2,455 wird zu 2,46
Die lezten beiden Zeilen zeigen die Feinheiten der Wissenschaftlichen
Rundung:Die 5 wird "abgeschnitten", wenn links davon eine gerade Ziffer steht,
sie wird "hinzugenommen", wenn links davon eine ungerade Ziffer steht.
Man kann es auch so ausdrücken: Eine zu rundende "5" wird bei der
Wissenschaftlichen Rundung nicht immer automatisch aufgerundet, sondern auf die
nächstliegende gerade Zahl gerundet.
Bei negativen Zahlen wird übrigens ebenso verfahren, und das Vorzeichen
bleibt für die Rundung unberücksichtigt:
-2,444 wird zu -2,44
-2,446 wird zu -2,45
***
-2,445 wird zu -2,44 (im Gegensatz zur positiven Zahl wird auf-, nicht
abgerundet)
-2,455 wird zu -2,46 (im Gegensatz zur positiven Zahl wird ab-, nicht
aufgerundet)
Diese Art der Rundung ist in einer IEEE-Norm verbindlich festgelegt. Das
Verfahren führt bei vielen Rundungen zu einem genaueren Ergebnis als die
Kaufmännische Rundung und wird immer angewandt, wenn Abweichungen aus vielen
Messungen möglichst niedrig sein müssen, z.B. in der Statistik und im
Vermessungswesen.
Die Wissenschaftliche Rundung verhindert Verfälschungen bei der statistischen
Behandlung größerer Datenmengen, z.B. Messreihen.
Stelle Dir eine Messreihe mit Werten vor, deren erste Nachkommastelle (wenn
wir auf ganze Zahlen runden) überwiegend '.5' lautet. Die Werte würden mit
kaufmaennischer Rundung alle aufgerundet. Alle statistischen Betrachtungen diese
Wertemenge wären daraufhin verfälscht.
Praktisches Beispiel:
...... Eingangsgrößen: 12.5 13.5 11.5 10.5 14.5 15.5
...... kaufm. gerundet: 13 14 12 11 15 16
...... wissens. gerundet: 12 14 12 10 14 16
Als Mittelwerte ergeben sich:
...... mit Eingangsgrößen: 13 (ohne die einzelnen Werte zu runden)
...... kaufm. gerundet: 14 (13.5 gerundet)
...... wissen. gerundet: 13 (korrekt)
*** 5) Wissenschaftliche Rundung in QBasic
QBasic stellt für die Wissenschaftliche Rundung die komfortablen
IEEE-kompatiblen Befehle CINT und CLNG zur Verfügung. Diese runden die
Vorkommastellen auf eine ganze Zahl gemäß den Regeln für die Wissenschaftliche
Rundung. Soll auf n Nachkommastellen gerundet werden, so ist die Zahl vor der
Rundung mit 10^n zu multiplizieren und hinterher wieder durch 10^n zu
dividieren.
CINT liefert einen INTEGER Datentyp zurück (15 Bit + Vorzeichen) und eignet
sich somit nur für Zahlen bis 32767. Für größere Zahlen verwendet man CLNG.
Mein untenstehendes Programm RUNDUNG2.BAS zeigt das Prinzip:
'************************************************************
' RUNDUNG2.BAS - Wissenschaftliche Rundung in QBasic
' =====================================================
' Wissenschaftliche Rundung von Dezimalzahlen auf 2
' Nachkommastellen. Bei Werten, die genau in der Mitte
' zwischen zwei darstellbaren Werten liegen, wird auf die
' naechste gerade Zahl gerundet.
'
' Hierzu stellt QBasic die Befehle CINT und CLNG zur
' Verfuegung.
'
' Von Thomas Antoni, 5.5.2003 - 7.5.2003
'************************************************************
CLS
DO
PRINT "Gibt eine pos. oder neg. Zahl mit Dezimalpunkt ein"
INPUT z#
PRINT CLNG(z# * 100) / 100
PRINT "Abbrechen...{[Esc] Wiederholen...[beliebige Taste]"
DO: taste$ = INKEY$: LOOP WHILE taste$ = ""
LOOP UNTIL taste$ = CHR$(27)
END
Das obige Programm steht im Verzeichnis Progs\ zur Verfügung sowie online
unter www.antonis.de/faq/progs/rundung2.bas .
Willst Du nicht auf 2, sondern auf eine andere Anzahl von
Nachkommastellen
runden, so ersetzt Du einfach zweimal die "100" im obigen Programm durch
"10^Nachkommastellen", z.B. durch "199" bei Rundung auf 3
Nachkommastellen.
*** 6) Kommastellen abschneiden
Zum Abschneiden sämtlicher Kommastellen einer Gleitpunktzahl stehen in QBasic
die Befehle INT und FIX zur Verfügung. INT funktioniert nach dem folgenden
Strickmuster:
INPUT "Zahl mit Nachkommastellen: "; Zahl#
PRINT "Zahl mit abgeschnittenen Kommastellen: "; INT(Zahl#)
Bei negativen Zahlen rundet INT auf die nächstkleinere ("stärker negative")
Zahl:
+123.345 wird zu 123
-123.345 wird zu -124
Wen das stört, der verwendet den Befehl FIX wie folgt:
INPUT "Zahl mit Nachkommastellen: "; Zahl#
Zahlabgeschnitten& = FIX(Zahl#)
PRINT "Zahl mit abgeschnittenen Kommastellen: ";
Zahlabgeschnitten&
Dabei wird -123.345 zu -123
Durch einen Trick kann man auch hierfür INT verwenden, wie die folgende
Befehlssequenz zeigt:
INPUT "Zahl mit Nachkommastellen: "; Zahl#
Zahlabgeschnitten& = SGN(Zahl#) * INT(ABS(Zahl#))
PRINT "Zahl mit abgeschnittenen Kommastellen: ";
Zahlabgeschnitten&
*** 7) Immer aufrunden
Wenn bei Nachkommastellen grundsätzlich aufgerundet werden soll, dann geht
das nach dem folgenden Rezept:
INPUT "Zahl mit Nachkommastellen: "; Zahl#
PRINT "Aufgerundete Zahl"; -INT(-Zahl#)
Negative Zahlen werden dabei zur nächsten "weniger negativen" ganzen Zahl
gerundet, also
+123.45 wird zu 124
-123.45 wird zu -123
*** 8) Immer abrunden
Soll bei Vorhandensein von Nachkommastellen immer abgerundet werden, so
verwendet man den INT-Befehl gemäß dem folgenden Schema:
INPUT "Zahl mit Nachkommastellen: "; Zahl#
PRINT "Aufgerundete Zahl"; -INT(-Zahl#)
Negative Zahlen werden dabei zur nächsten "negativeren" ganzen Zahl gerundet,
also
+123.45 wird zu 123
-123.45 wird zu -124
*** 9) Wie reduziere ich die Rundungsfehler der internen
Gleitpunktarithmetik?
Die QBasic-Foren sind voll von Klagen verzweifelter Programmierer über die
angeblich höchst ungenaue Gleitpunktarithmetik bei QBasic 1.1. Diese Probleme
treten auch bei doppelt langen Gleitpunktzahlen vom Typ DOUBLE auf. Siehe auch
den FAQ-Eintrag "Warum kommt es
bei Gleitpunktoperationen zu Ungenauigkeiten?" .
Durch den folgenden, ganz einfachen, wenig dokumentierten Trick lassen sich
viele dieser Probleme aus dem Wege räumen:
In vielen Fällen lässt sich die Genauigkeit beim Rechnen mit konstanten
Zahlenwerten verbessern, indem man für den Zahlenwert durch einen angehängtes
"#" die Darstellung als doppelt lange Gleitpunktzahlen erzwingt. Probiere z.B.
einmal das folgende Programm aus:
a# = 120000000 - (150000000 * .6)
PRINT "Ungenaues Ergebnis = "; a#
a# = 120000000# - (150000000# * .6#)
PRINT "genaues Ergebnis . = "; a#
Die erste Operation liefert das relativ ungenaue Ergebnis 29999996.42372131,
während die zweite Operation mit den angehängten "Gartenzäunen" # das erwartete
Ergebnis 30000000 liefert.
*** 10) Spezielle Rundungen, z.B. auf Vielfache von 0,25
Problem: ich brauche eine spezielle Rundung
Ich brauche dringend einen Zahlenwert auf Vielfache von 0,25 genau zu
runden.
Beispiel:
5.03 -> 5.00
5.23 -> 5.25
5.48 -> 5.50
Lösung
Das geht mit der folgenden Methode:
DO
INPUT z
PRINT INT(z * 4 + .5) / 4
LOOP
Antwort 2
~~~~~~~~~~~~~~~~~
[ von helium ( m.beckah*gmx.de ) im QB-Forum,3.10.02
]
1) Runden:
a = 1.5378
b = cint(a) 'gerundeter Wert
2) Abschneiden
a = 1.5378
b = int(a)
3) gegen 0 Runden (bei pos. Zahlen wie int)
a = 1.5378
b = fix(a)
Antwort 3
~~~~~~~~~~~~~~~~~
[ von Peter Vollenweider ( peter.vollenweider*gmx.ch ) im QB-Forum, 3.10.02 ]
Runden, aufrunden oder abrunden? Hier sind die Befehle...
*** INT(a)
rundet a ab,
*** CINT(a)
rundet auf oder ab - nur für Zahlen zwischen -32767 und +32767, für
grössere Zahlen CLNG(a) verwenden),
*** -INT(-a)
rundet a auf. Man muss etwas vorsichtig sein mit dem Runden von
Fliesskommazahlen (also 'unganze' Zahlen), denn manchmal gibt es kleine
Rechnungsfehler: statt 5 ergibt eine Rechnung z.B. 4.9999999999. 4.9999999999
wird mit INT aber auf 4 abgerundet. Wenn also Fehler vorkommen sollte man
zuerst
vielleicht z.B. 0.0000000001 dazuzählen.
*** INT(x + .5)
Statt CINT(x) kann man ausserdem auch INT(x + .5) schreiben.
*** Beispiele:
Wenn a=1.4 ist:
INT(a)=1
CINT(a)=1
-INT(-a)=2
Antwort 4
~~~~~~~~~~~~~~~~~
[ Von Schlotzz im QB-Forum ]
Um einen Wert zu runden, kann man die CINT() Funktion verwenden. Diese
Funktion rundet die Zahl auf eine Ganzzahl.
z.B.
CINT(103.115) -> 103
CINT(104.547) -> 105
Um aber zwei Nachkommastellen beizubehalten, verwendet man einen kleinen
Trick; man verschiebt vorher das Komma.
z.B.
103.115 * 100 -> 10311.5
104.547 * 100 -> 10454.7
Diese Zahl wird nun mit CINT() gerundet.
z.B.
CINT(10311.5) -> 10312
CINT(10454.7) -> 10455
Nun haben wir aber mit dem Faktor 100 eine zu große Zahl; also teilen wir sie
wieder durch 100.
z.B.
10312 / 100 -> 103.12
10455 / 100 -> 104.55
Zusammenfassend nochmal hier ein Programmlisting:
ListenPreis# = 103.115
Stellen% = 2
Faktor% = 10 ^ Stellen%
NeuerPreis# = CINT(ListenPreis# * Faktor%) / Faktor%
PRINT ListenPreis#; " gerundet ist";NeuerPreis#
Ich hoffe, Du kannst der Logik folgen...
Answer 5
~~~~~~~~~~~~~~~
How does the commercial and scientific rounding work?
*** Commercial rounding
The commercial rounding rounds a given number to the nearest representable
number. As an additional rule, .5 is always rounded to the larger value.
*** Scientific rounding
• If the digit to be dropped is less that 5, the preceding figure is not
altered.
• If the digit to be dropped is greater than 5, the preceding figure is
increased by 1.
• If the digit to be dropped is 5, the preceding figure is increased by 1
if it is an odd number and the preceding figure is not altered if it is
an even number.
[ The QBasic-MonsterFAQ --- Start Page: www.antonis.de/faq ]