Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht Das deutsche QBasic- und FreeBASIC-Forum
Für euch erreichbar unter qb-forum.de, fb-forum.de und freebasic-forum.de!
 
FAQFAQ   SuchenSuchen   MitgliederlisteMitgliederliste   BenutzergruppenBenutzergruppen  RegistrierenRegistrieren
ProfilProfil   Einloggen, um private Nachrichten zu lesenEinloggen, um private Nachrichten zu lesen   LoginLogin
Zur Begleitseite des Forums / Chat / Impressum
Aktueller Forenpartner:

Wert einer Variable auf 2 Nachkommastellen runden

 
Neues Thema eröffnen   Neue Antwort erstellen    Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht -> Allgemeine Fragen zu FreeBASIC.
Vorheriges Thema anzeigen :: Nächstes Thema anzeigen  
Autor Nachricht
ALWIM



Anmeldungsdatum: 08.08.2006
Beiträge: 1047
Wohnort: Niederbayern

BeitragVerfasst am: 13.05.2024, 20:36    Titel: Wert einer Variable auf 2 Nachkommastellen runden Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
nemored



Anmeldungsdatum: 22.02.2007
Beiträge: 4644
Wohnort: ~/

BeitragVerfasst am: 14.05.2024, 07:01    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
SpionAtom



Anmeldungsdatum: 10.01.2005
Beiträge: 346

BeitragVerfasst am: 14.05.2024, 07:47    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
grindstone



Anmeldungsdatum: 03.10.2010
Beiträge: 1231
Wohnort: Ruhrpott

BeitragVerfasst am: 14.05.2024, 09:58    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
hhr



Anmeldungsdatum: 15.07.2020
Beiträge: 104

BeitragVerfasst am: 14.05.2024, 10:38    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
ALWIM



Anmeldungsdatum: 08.08.2006
Beiträge: 1047
Wohnort: Niederbayern

BeitragVerfasst am: 14.05.2024, 14:36    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
Muttonhead



Anmeldungsdatum: 26.08.2008
Beiträge: 564
Wohnort: Jüterbog

BeitragVerfasst am: 14.05.2024, 16:56    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
ALWIM



Anmeldungsdatum: 08.08.2006
Beiträge: 1047
Wohnort: Niederbayern

BeitragVerfasst am: 14.05.2024, 18:15    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
hhr



Anmeldungsdatum: 15.07.2020
Beiträge: 104

BeitragVerfasst am: 14.05.2024, 18:18    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
dreael
Administrator


Anmeldungsdatum: 10.09.2004
Beiträge: 2514
Wohnort: Hofen SH (Schweiz)

BeitragVerfasst am: 14.05.2024, 19:45    Titel: Antworten mit Zitat

Schon länger her, aber hier durchaus relevant aus meiner Sammlung:

https://www.dreael.ch/Deutsch/BASIC-Knowhow-Ecke/Gleitkommazahlen.html
_________________
Teste die PC-Sicherheit mit www.sec-check.net
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden Website dieses Benutzers besuchen
hhr



Anmeldungsdatum: 15.07.2020
Beiträge: 104

BeitragVerfasst am: 14.05.2024, 20:36    Titel: Antworten mit Zitat

@ dreael, interessante Seite.

@ ALWIM
zu 1.234999999999
4999999999 ist kleiner als 5000000000, deshalb wird abgerundet.
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
nemored



Anmeldungsdatum: 22.02.2007
Beiträge: 4644
Wohnort: ~/

BeitragVerfasst am: 14.05.2024, 22:17    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
grindstone



Anmeldungsdatum: 03.10.2010
Beiträge: 1231
Wohnort: Ruhrpott

BeitragVerfasst am: 15.05.2024, 12:40    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden E-Mail senden
ALWIM



Anmeldungsdatum: 08.08.2006
Beiträge: 1047
Wohnort: Niederbayern

BeitragVerfasst am: 15.05.2024, 21:14    Titel: Antworten mit Zitat

https://github.com/OttoMilvang/TieBreakServer/blob/main/rating.py

Weitere Wortmeldungen von mir zu dem Thema, gibt es später...
_________________
SHELL SHUTDOWN -s -t 05
Nach oben
Benutzer-Profile anzeigen Private Nachricht senden
Berkeley



Anmeldungsdatum: 13.05.2024
Beiträge: 46

BeitragVerfasst am: 17.05.2024, 15:56    Titel: Antworten mit Zitat

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
Benutzer-Profile anzeigen Private Nachricht senden
Beiträge der letzten Zeit anzeigen:   
Neues Thema eröffnen   Neue Antwort erstellen    Das deutsche QBasic- und FreeBASIC-Forum Foren-Übersicht -> Allgemeine Fragen zu FreeBASIC. Alle Zeiten sind GMT + 1 Stunde
Seite 1 von 1

 
Gehe zu:  
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.

 Impressum :: Datenschutz