|
Das deutsche QBasic- und FreeBASIC-Forum Für euch erreichbar unter qb-forum.de, fb-forum.de und freebasic-forum.de!
|
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen |
Autor |
Nachricht |
ALWIM
Anmeldungsdatum: 08.08.2006 Beiträge: 1047 Wohnort: Niederbayern
|
Verfasst am: 13.05.2024, 20:36 Titel: Wert einer Variable auf 2 Nachkommastellen runden |
|
|
Welche Möglichkeiten habe ich, den Wert einer Variable auf 2 Nachkommastellen zu runden?
Folgende Ausgangssituation: Ich habe für eine Person den Wert Punkte und den Wert Anzahl der gespielten Partien. Errechnen muss ich den Wert p mit einer bestimmten Formel und dann den Wert dp mittels einer Tabelle ermitteln.
Formel lautet: Code: | p = Punkte / Anzahl Partien |
Die Tabelle geht von 1.0 bis 0.00!
Link: https://tec.fide.com/wp-content/uploads/2024/04/C.07-2023-Tiebreak-exercises-V01-1.pdf
Auf Seite 37 befindet sich die Tabelle.
Tabelle:
Code: | p dp
1.0 800
.99 677
.98 589
.97 538
usw. |
Ich muss irgendwie den Wert dp ermitteln können. Das funktioniert teilweise nicht, da es sein kann, dass ich 0,214285714 als Ergebnis habe. Ich muss dieses Ergebnis quasi auf 2 Nachkommastellen runden. Ich bin schon am Überlegen, selber eine Funktion zu schreiben, die mir die Variable nach meinen Wünschen rundet. Mir fehlt momentan die zündende Idee...
Edit: Ich versuche gerade Feinwertung 19 (TPR) zu programmieren. _________________ SHELL SHUTDOWN -s -t 05 |
|
Nach oben |
|
|
nemored
Anmeldungsdatum: 22.02.2007 Beiträge: 4644 Wohnort: ~/
|
Verfasst am: 14.05.2024, 07:01 Titel: |
|
|
1) Der sicherste Weg ist, nicht zu runden, sondern nur die Ausgabe gerundet anzuzeigen:
Code: | #INCLUDE ONCE "vbcompat.bi"
DIM AS DOUBLE x = 0.214285714
PRINT FORMAT(x, "0.00") |
2) Das Runden auf zwei Stellen ist theoretisch schon möglich, allerdings werden Gleitkommazahlen im Binärsystem verarbeitet und es kann daher zu Problemen in der Dezimaldrstellung kommen.
Code: | DIM AS DOUBLE x = 0.214285714, y
y = INT(x*100) / 100
PRINT y |
3) Um sicher mit fester Nachkommazahl zu arbeiten, könnte man sich einen passenden Datentyp basteln. Die Idee: Zahlen werden als Integer gespeichert, und zwar als Hundertfaches des gesuchten Wertes. Für diesen Datentyp gibt es besttimmt schon mehrere Vorlagen.
Code: | DIM AS DOUBLE x = 0.214285714
DIM AS INTEGER y
y = x*100
PRINT y/100 |
Das Problem der Dezimaldarstellung binärer Gleitkommazahlen bleibt bestehen, aber Rechnungen innerhalb der neuen Zahlen können exakt ausgeführt werden. Zu beachten ist auch, dass die letzten beiden Vorschläge nur eine Nachkommastelle angeben, wenn die zweite 0 ist. Beide Darstellungsprobleme lassen sich wiederum mit FORMAT beheben. _________________ Deine Chance beträgt 1:1000. Also musst du folgendes tun: Vergiss die 1000 und konzentriere dich auf die 1. |
|
Nach oben |
|
|
SpionAtom
Anmeldungsdatum: 10.01.2005 Beiträge: 346
|
Verfasst am: 14.05.2024, 07:47 Titel: |
|
|
Wenn du weißt, dass das Ergebnis von 0.0 bis 1.0 geht, dann kannst du vielleicht bei allen Rechnungen auch auf Doubles verzichten.
In etwa so, wie wenn man nicht mit Euros, sondern mit Cents rechnet, umgeht man somit diese ganzen Fließkommaungenauigkeiten. _________________ Inzwischen gehöre ich auch zu den BlitzBasicern. Also verzeiht mir, wenn mir mal ein LOCATE 100, 100 oder dergleichen rausrutscht. |
|
Nach oben |
|
|
grindstone
Anmeldungsdatum: 03.10.2010 Beiträge: 1231 Wohnort: Ruhrpott
|
Verfasst am: 14.05.2024, 09:58 Titel: |
|
|
Hallo,
ich denke, die bisherigen Lösungsvorschläge gehen am eigentlichen Problem vorbei. Es geht -zumindest so wie ich es verstehe- nicht darum, überflüssige Nachkommastellen abzuschneiden oder zu vermeiden, sondern um eine mathematisch korrekte 4/5 - Rundung auf 2 Dezimalstellen genau.
Das ist eigentlich ganz einfach: Code: | Function rund(wert As Double) As Double
Return Int(wert * 100 + .5) / 100
End Function
Dim As Double p
p = 5 / 23
? p
? rund(p)
?
p = 5 / 20
? p
? rund(p)
?
p = 5 / 19
? p
? rund(p)
Sleep |
Zu diesem Thema gibt es auch ein ausführliches Tutorial.
Gruß
grindstone _________________ For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen! |
|
Nach oben |
|
|
hhr
Anmeldungsdatum: 15.07.2020 Beiträge: 104
|
Verfasst am: 14.05.2024, 10:38 Titel: |
|
|
Format hat eine Eigenheit, die man anscheinend aus Kompatibilität zu VBDos eingebaut hat.
Wenn mit den gerundeten Zahlen weitergerechnet werden soll, würde ich den Vorschlag von grindstone vorziehen.
Allerdings kann man dann 'Return Cint(wert * 100) / 100' in Erwägung ziehen. Cint rundet 5 zu gerade, was für den Mittelwert günstig ist.
Jedenfalls vergleicht das folgende Beispiel Format und sprintf:
Code: | #Include "crt.bi" 'sprintf
#Include "string.bi" 'Format
Randomize
Dim As Double n
Dim As String s
Dim As Zstring*20 zs
Do
n = Rnd
Print String(9,Chr(32));Str(n)
s = Format(n,"0.00")
Mid(s,Instr(s,",")) = "." 'Bei Bedarf Komma durch Dezimalpunkt ersetzen.
sprintf(zs,"%.2f",n)
Print "Format : ";s
Print "sprintf: ";zs
If s<>zs Then Print "Verschieden, beliebige Taste zum Fortfahren...":Sleep
Print String(30,"-")
'getkey
Loop
'
|
|
|
Nach oben |
|
|
ALWIM
Anmeldungsdatum: 08.08.2006 Beiträge: 1047 Wohnort: Niederbayern
|
Verfasst am: 14.05.2024, 14:36 Titel: |
|
|
Vielen herzlichen Dank für die Informationen!
nemored hat Folgendes geschrieben: | 1) Der sicherste Weg ist, nicht zu runden, sondern nur die Ausgabe gerundet anzuzeigen:
Code: | #INCLUDE ONCE "vbcompat.bi"
DIM AS DOUBLE x = 0.214285714
PRINT FORMAT(x, "0.00") |
2) Das Runden auf zwei Stellen ist theoretisch schon möglich, allerdings werden Gleitkommazahlen im Binärsystem verarbeitet und es kann daher zu Problemen in der Dezimaldrstellung kommen.
Code: | DIM AS DOUBLE x = 0.214285714, y
y = INT(x*100) / 100
PRINT y |
3) Um sicher mit fester Nachkommazahl zu arbeiten, könnte man sich einen passenden Datentyp basteln. Die Idee: Zahlen werden als Integer gespeichert, und zwar als Hundertfaches des gesuchten Wertes. Für diesen Datentyp gibt es besttimmt schon mehrere Vorlagen.
Code: | DIM AS DOUBLE x = 0.214285714
DIM AS INTEGER y
y = x*100
PRINT y/100 |
Das Problem der Dezimaldarstellung binärer Gleitkommazahlen bleibt bestehen, aber Rechnungen innerhalb der neuen Zahlen können exakt ausgeführt werden. Zu beachten ist auch, dass die letzten beiden Vorschläge nur eine Nachkommastelle angeben, wenn die zweite 0 ist. Beide Darstellungsprobleme lassen sich wiederum mit FORMAT beheben. |
Runden muss ich, da der Wert für die nächste Feinwertung benötigt wird!
Wenn ich nur 1 Nachkommastelle habe, ist das kein Problem. Ich darf lediglich nur maximal 2 Nachkommastellen haben, da ich sonst die Werte in der Tabelle nicht mehr vergleichen bzw. den Wert dp aus der Tabelle kriegen kann.
hhr hat Folgendes geschrieben: | Wenn mit den gerundeten Zahlen weitergerechnet werden soll, würde ich den Vorschlag von grindstone vorziehen. | Es wird nicht mehr weitergerechnet! Ich brauche lediglich den geteilten Wert, der auf 2 Nachkommastellen gerundet ist.
SpionAtom hat Folgendes geschrieben: | Wenn du weißt, dass das Ergebnis von 0.0 bis 1.0 geht, dann kannst du vielleicht bei allen Rechnungen auch auf Doubles verzichten.
| Wenn mir die richtigen Werte zum Schluss angezeigt werden, verzichte ich auf Doubles gerne. Bloß, wenn mir 0 angezeigt wird und ich auf 0.56 prüfen soll, habe ich ein Problem?
grindstone hat Folgendes geschrieben: | Hallo,
ich denke, die bisherigen Lösungsvorschläge gehen am eigentlichen Problem vorbei. Es geht -zumindest so wie ich es verstehe- nicht darum, überflüssige Nachkommastellen abzuschneiden oder zu vermeiden, sondern um eine mathematisch korrekte 4/5 - Rundung auf 2 Dezimalstellen genau.
Das ist eigentlich ganz einfach: Code: | Function rund(wert As Double) As Double
Return Int(wert * 100 + .5) / 100
End Function
Dim As Double p
p = 5 / 23
? p
? rund(p)
?
p = 5 / 20
? p
? rund(p)
?
p = 5 / 19
? p
? rund(p)
Sleep |
Zu diesem Thema gibt es auch ein ausführliches Tutorial.
Gruß
grindstone | Richtig! Es soll eine mathematisch korrekte Rundung auf 2 Nachkommastellen sein.
Mal schauen, was bei mir am besten funktioniert und korrekte Ergebnisse liefert. Alles nicht ganz einfach zum berechnen... _________________ SHELL SHUTDOWN -s -t 05 |
|
Nach oben |
|
|
Muttonhead
Anmeldungsdatum: 26.08.2008 Beiträge: 564 Wohnort: Jüterbog
|
Verfasst am: 14.05.2024, 16:56 Titel: |
|
|
es sieht nicht sehr schön aus
Code: | function gerundet(z as double) as double
dim as double dbl_tief,dbl_hoch
dbl_tief=fix(z*100)
dbl_hoch=dbl_tief + 1
dbl_tief /=100
dbl_hoch /=100
function= iif( (z-dbl_tief) < (dbl_hoch-z) , dbl_tief , dbl_hoch)
'if (z-dbl_tief) < (dbl_hoch-z) then function=dbl_tief else function=dbl_hoch
end function
print gerundet(1.234000000001)
print gerundet(1.234999999999)
print gerundet(1.235000000000)
print gerundet(1.235000000001) |
1.23
1.23
1.24
1.24
Mutton |
|
Nach oben |
|
|
ALWIM
Anmeldungsdatum: 08.08.2006 Beiträge: 1047 Wohnort: Niederbayern
|
Verfasst am: 14.05.2024, 18:15 Titel: |
|
|
Muttonhead hat Folgendes geschrieben: | es sieht nicht sehr schön aus
Code: | function gerundet(z as double) as double
dim as double dbl_tief,dbl_hoch
dbl_tief=fix(z*100)
dbl_hoch=dbl_tief + 1
dbl_tief /=100
dbl_hoch /=100
function= iif( (z-dbl_tief) < (dbl_hoch-z) , dbl_tief , dbl_hoch)
'if (z-dbl_tief) < (dbl_hoch-z) then function=dbl_tief else function=dbl_hoch
end function
print gerundet(1.234000000001)
print gerundet(1.234999999999)
print gerundet(1.235000000000)
print gerundet(1.235000000001) |
1.23
1.23
1.24
1.24
Mutton | Es sieht nicht schön aus. Das stimmt!
1.234999999999 sollte doch 1.24 sein?! Oder täusche ich mich jetzt? Muss ich nochmal nachschauen, wie genau gerundet wird.
Bei einem Quellcode den ich auf Souerceforge gefunden habe und in der Programmiersprache Python ist, wird der Befehl "Round" zum runden verwendet! _________________ SHELL SHUTDOWN -s -t 05
Zuletzt bearbeitet von ALWIM am 14.05.2024, 18:27, insgesamt 2-mal bearbeitet |
|
Nach oben |
|
|
hhr
Anmeldungsdatum: 15.07.2020 Beiträge: 104
|
Verfasst am: 14.05.2024, 18:18 Titel: |
|
|
Funktioniert aber ziemlich gut:
Code: | #Include "crt.bi" 'sprintf
Function gerundet(z As Double) As Double
Dim As Double dbl_tief,dbl_hoch
dbl_tief=Fix(z*100)
dbl_hoch=dbl_tief + 1
dbl_tief /=100
dbl_hoch /=100
Function= Iif( (z-dbl_tief) < (dbl_hoch-z) , dbl_tief , dbl_hoch)
'if (z-dbl_tief) < (dbl_hoch-z) then function=dbl_tief else function=dbl_hoch
End Function
Randomize
Dim As Double n
Dim As String s
Dim As Zstring*20 zs
Do
n = Rnd
Print String(11,Chr(32));Str(n)
s = Str(gerundet(n))
s = Left(s,4)
sprintf(zs,"%.2f",n)
zs = Rtrim(zs,"0")
zs = Rtrim(zs,".")
Print "gerundet : ";s
Print "sprintf : ";zs
If s<>zs Then Print "Verschieden, beliebige Taste zum Fortfahren...":Sleep
Print String(30,"-")
'Getkey
Loop
|
|
|
Nach oben |
|
|
dreael Administrator
Anmeldungsdatum: 10.09.2004 Beiträge: 2514 Wohnort: Hofen SH (Schweiz)
|
|
Nach oben |
|
|
hhr
Anmeldungsdatum: 15.07.2020 Beiträge: 104
|
Verfasst am: 14.05.2024, 20:36 Titel: |
|
|
@ dreael, interessante Seite.
@ ALWIM
zu 1.234999999999
4999999999 ist kleiner als 5000000000, deshalb wird abgerundet. |
|
Nach oben |
|
|
nemored
Anmeldungsdatum: 22.02.2007 Beiträge: 4644 Wohnort: ~/
|
Verfasst am: 14.05.2024, 22:17 Titel: |
|
|
Zitat: | Es soll eine mathematisch korrekte Rundung auf 2 Nachkommastellen sein. |
Dann ist CINT ein guter Kandidat. Ich vermute allerdings, dass du nicht die mathematische, sondern die kaufmännische Rundung willst - da kommt der Ansatz von grindstone ins Spiel.
Trotzdem: Wenn du mit den Werten nicht mehr weiterrechnest, nimm FORMAT (in Kombination von grindstones +.5). Sonst kann so etwas passieren:
Code: | DIM AS DOUBLE x, y
FOR i AS INTEGER = 0 to 99
x = RND
y = INT(x * 100 + .5) / 100
IF LEN(STR(y)) > 4 THEN PRINT x & " ist gerundet " & y
NEXT |
ergibt bei mir die Ausgabe
Code: | 0.6804801726248115 ist gerundet 0.6800000000000001
0.07139793620444834 ist gerundet 0.07000000000000001
0.6768258756492287 ist gerundet 0.6800000000000001
0.9341569624375552 ist gerundet 0.9300000000000001
0.8131022688467056 ist gerundet 0.8100000000000001
0.5576718533411622 ist gerundet 0.5600000000000001
0.9285403536632657 ist gerundet 0.9300000000000001 |
dreael beschreibt diesen Effekt auch in seinem Artikel. Eine binäre Gleitkommazahl ist zum Verwalten von Dezimalzahlen mit fester Nachkommazahl einfach nicht geeignet. _________________ Deine Chance beträgt 1:1000. Also musst du folgendes tun: Vergiss die 1000 und konzentriere dich auf die 1. |
|
Nach oben |
|
|
grindstone
Anmeldungsdatum: 03.10.2010 Beiträge: 1231 Wohnort: Ruhrpott
|
Verfasst am: 15.05.2024, 12:40 Titel: |
|
|
Mit SINGLE statt DOUBLE (das wäre in diesem Fall ja ausreichend) scheint es dieses Problem nicht zu geben.
Gruß
grindstone _________________ For ein halbes Jahr wuste ich nich mahl wie man Proggramira schreibt. Jetzt bin ich einen! |
|
Nach oben |
|
|
ALWIM
Anmeldungsdatum: 08.08.2006 Beiträge: 1047 Wohnort: Niederbayern
|
|
Nach oben |
|
|
Berkeley
Anmeldungsdatum: 13.05.2024 Beiträge: 46
|
Verfasst am: 17.05.2024, 15:56 Titel: |
|
|
Meine Empfehlung zum Runden wäre: INT(<Double-Wert>+0.500030517578125). Sollte man als ROUND()-Funktion einführen... Kann natürlich in einigen seltenen Fällen ein falsches Ergebnis liefern (*,499... wird aufgerundet), allerdings ist man Fehler von IEEE 754 sowieso gewöhnt.
Viel empfehlenswerter wär' die Erweiterung von FreeBASIC um einen DECIMAL-Typ.
Abgesehen davon kann man einfach nur darauf pochen, Integer zu verwenden - bei Geld zum Beispiel mit Cent rechnen, statt mit Euros. Oder auch mit hundertstel Cent je nach Anwendung, und bei der Ausgabe dann ein Komma reinbasteln. So funktioniert ja auch letztlich der decimal-Typ.
Formatierte Ausgabe von double/float("SINGLE") ergibt zwangsläufig immer irrwitzige Nachkommastellen, die muss man mit einer Format-Anweisung unterdrücken, welche aber wahrscheinlich die letzte Stelle nicht rundet(obwohl machbar). Und der Zahlensalat ist im Speicher immer noch da, nur ausgeblendet. "1.1+0.1" ergibt nunmal nicht gleich "1.2"... |
|
Nach oben |
|
|
|
|
Du kannst keine Beiträge in dieses Forum schreiben. Du kannst auf Beiträge in diesem Forum nicht antworten. Du kannst deine Beiträge in diesem Forum nicht bearbeiten. Du kannst deine Beiträge in diesem Forum nicht löschen. Du kannst an Umfragen in diesem Forum nicht mitmachen.
|
|