 |
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 |
stevie1401
Anmeldungsdatum: 04.07.2006 Beiträge: 133
|
Verfasst am: 27.11.2006, 22:27 Titel: Zahlen richtig runden |
|
|
Kann es sein, dass FB nicht richtig runden kann?
Ich habe mal folgendes probiert:
dim as double summe,mwst,netto
summe=10
netto=summe/1.16
netto=int(netto*100+0.5)/100
mwst=summe-netto
print "Summe "+str(summe)
print "Netto "+str(netto)
print "Mwst "+str(mwst)
Bei Summe=100 ist es genauso
Auch CINT hilft nichts.
Hat jemand eine Lösung? |
|
Nach oben |
|
 |
terminate
Anmeldungsdatum: 12.09.2006 Beiträge: 56
|
Verfasst am: 28.11.2006, 02:16 Titel: Re: Zahlen richtig runden |
|
|
stevie1401 hat Folgendes geschrieben: | Kann es sein, dass FB nicht richtig runden kann? |
Nein.
Jedenfalls nicht schlechter oder besser als andere Compiler.
Zitat: | Ich habe mal folgendes probiert:
...
Hat jemand eine Lösung? |
Das beobachtete Verhalten ist in der Natur von DOUBLE und SINGLE Variablen begründet:
http://de.wikipedia.org/wiki/Gleitkommazahl
Zum runden von Gleitkommazahlen wird eine separate Rundungsfunktion benötigt z.B. folgende, (das wär wohl eher was für YTWINKY):
(Quelle: http://www.freebasic.net/forum/viewtopic.php?p=14022#14022)
Code: |
declare Function RoundOff (x as single, k as integer) as single
dim as double summe,mwst,netto
summe=10
netto=summe/1.16
mwst=summe-netto
print "Summe "+str(summe)
print "Netto "+str(netto)
print "Mwst "+str(mwst)
print RoundOff(CSng(summe),3)
print RoundOff(CSng(netto),3)
print RoundOff(CSng(mwst),3)
sleep
Function RoundOff (x as single, k as integer) as single
dim as integer np
dim as double pn
dim as long l
If k > 6 Then k = 6
If Abs(x) > 1E-39 Then
np = k - 1 - Int(Log(Abs(x)) / Log(10))
pn = 10 ^ Cdbl(np)
l = pn * x
RoundOff = l / pn
Else
RoundOff = 0
End If
End Function |
Ebenfalls interessant:
Die ceil(), floor(), round() etc. Funktionen aus der math.bi.
(Einbinden mit: #include "crt/math.bi") |
|
Nach oben |
|
 |
stevie1401
Anmeldungsdatum: 04.07.2006 Beiträge: 133
|
Verfasst am: 28.11.2006, 11:46 Titel: |
|
|
Nein, dass funktioniert auch nur bedingt.
Auch die Mathe.bi scheint nicht richtig zu laufen.
Jetzt habe ich mir mal selbst etwas gebastelt.
Ist zwar mächtig von-hinten-durchs-Knie-ins-Auge, aber es scheint zu funktionieren
Code: |
Declare Function Stevie1401Round(Byval zahl As Double,Byval KommaStelle As Integer) As single
Screen 20,32,1,1
Dim As single OriginalZahl
'OriginalZahl MUSS Single sein!
OriginalZahl=32.6598
Print "OriginalZahl:"+Str(OriginalZahl)
'Umwandeln
Print
OriginalZahl=Stevie1401Round(OriginalZahl,2)
Print
Print "Zahl nach Stevie1401Round: "+Str(OriginalZahl)
Function Stevie1401Round(Byval zahl As Double,Byval KommaStelle As Integer) As Single
Dim komma,lz,vlz,gekuerzteZahl As Double,HinzuZaehlerZahl As Double,minusflg
Dim As String dummy, HinzuZaehlerStr
if zahl<0 then
zahl=zahl*-1
minusflg=1
end if
'Zahl als Sting umwandeln
dummy=Str(zahl)
'Nach Komma suchen (naja, eigentl. Punkt :-) )
komma=Instr(dummy,".")
If Len(dummy)>komma+KommaStelle And komma>0 Then
'Nachkommastellen ansagen
Print "Nachkommastellen:"+Str(KommaStelle)
'String auf Nachkommastellen+1 kürzen
'+1 deshalb, weil nach dieser beurteilt wird, ob aufgerundet werden soll
dummy=Left(dummy,komma+KommaStelle+1)
dummy=Trim(dummy)
Print "Abgeschnittener String:"+dummy
'Letzte Zahl ermitteln
lz=Val(Right(dummy,1))
Print "Letzte Zahl="+Str(lz)
'vorletzte Zahl ermitteln
vlz=Val(Mid(dummy,komma+KommaStelle,1))
Print "Vorletzte Zahl="+Str(vlz)
If lz>4 Then 'Wenn die letzte Zahl über 4, dann aufrunden
gekuerzteZahl=Val(Left(dummy,komma+KommaStelle))
Print "Gekuertze Zahl:"+Str(gekuerzteZahl)
HinzuZaehlerStr="0."+String$(Kommastelle-1,"0")+"1"
Print "HinzuZaehlerString="+HinzuZaehlerStr
HinzuZaehlerZahl=Val(HinzuZaehlerStr)
Print "HinzuZaehlerZahl:"+Str(HinzuZaehlerZahl)
' Print "Vorletzte Zahl aufgerundet="+str(vlz)
Print "Letzte Zahl ist >4, deshalb vorletzte Zahl aufrunden:"
Print "ZahlenString VOR Bearbeitung: "+Space(KommaStelle+2-Len(dummy))+dummy
zahl=zahl+HinzuZaehlerZahl
'String auf die richtige Länge kürzen
dummy=Str(zahl)
dummy=Left(dummy,komma+KommaStelle)
Print "ZahlenString NACH Bearbeitung: "+Space(KommaStelle+2-Len(dummy))+dummy
Else 'Wenn die letzte Zahl NICHT über 4, dann NICHT aufrunden
Print "Es wird NICHT aufgerundet."
'String auf die richtige Länge kürzen
dummy=Left(dummy,komma+Kommastelle)
End If
End If
if minusflg=1 then
zahl=val(dummy)
zahl=zahl*-1
dummy=str(zahl)
end if
Stevie1401Round=Val(dummy)
End Function
|
Das klappt natürlich nur mit POSITIVEN Zahlen.
Deshalb wandelt die Function bei Bedarf die Originalzahl erst einmal um, merkt es sich aber, um am Ende wieder Minus zu machen. |
|
Nach oben |
|
 |
terminate
Anmeldungsdatum: 12.09.2006 Beiträge: 56
|
Verfasst am: 28.11.2006, 15:40 Titel: |
|
|
stevie1401 hat Folgendes geschrieben: | Nein, dass funktioniert auch nur bedingt. |
Gut möglich, ich hab die Rundungsfunktion weder geschrieben noch getestet, nur Copy&Paste gemacht und die Qbasic Suffixes entfernt, (möglicherweise fehlerbehaftet), damit sich das ganze auch unter FB 0.17 CVS kompilieren lässt.
Zitat: |
Auch die Mathe.bi scheint nicht richtig zu laufen. |
Das glaube ich nicht, die math.bi ist nur eine Headerdatei, die auf die entsprechen, (Standard) C Funktionen zugreift, d.h. mit allem was hier an Ungereimtheiten oder möglichen Problemen auftritt muß sich auch jeder 'normale' C Programmierer auseinandersetzen.
Ich würde einfach mal behaupten Floatingpointvariablen sind ein bewährter und brauchbarer Kompromiss zwischen minimalem Speicherplatzverbrauch, maximal darstellbarer Zahlengröße und hinreichender Genauigkeit, für die meisten Anwendungen geeignet wo es nicht darum geht Zahlen in 'menschenlesbarer', genehmer, Form darzustellen. Jetzt mal im Ernst: 13.9999999999999 sieht natürlich Scheiße aus, wenn da eigentlich 14.0000000000 stehen müßte, aber der Fehler ist für die meisten Anwendungen vernachlässigbar klein, vor allem, wenn man bedenkt, dass man mit einer 'nur' 8 Byte großen Variable einen Zahlenraum von +/-4.490 656 458 412 465 E-324 bis +/-1.797 693 134 862 310 E+308 darstellen kann, wenn man dafür Integervariablen verwenden würde wäre man schnell bei etlichen hundert Bytes pro Variable, (oder noch mehr?). Wenn es z.B. um Grafikberechnungen in einem Spiel geht wird am Schluß sowieso zwangsläufig wieder auf maximal 'ein Pixel genau' gerundet, fällt also gar nicht auf.
Wenn ich aber tatsächlich in einem sehr kleinen Zahlenbereich, (z.B. max 10 Vorkammastellen und 10 Nachkommastellen), 'absolute' Genauigkeit benötigen würde, dann würde ich stattdessen einfach den Variablentyp ULONGINT verwenden und alles 'vor dem Komma' berechnen, d.h. ich wandle jede "Kommazahl" vor der Berechnung in eine ULONGINT Zahl um, indem ich das Komma definiert 'nach rechts verschiebe', (und nach der Berechnung, bzw. vor der Ausgabe, wieder zurück). Das ist nicht unüblich, eine ganze Menge Leute haben sich schon Fließkomma/Integer Libraries geschrieben um eine höhere Genauigkeit zu erzielen.
Zitat: |
Jetzt habe ich mir mal selbst etwas gebastelt.
Ist zwar mächtig von-hinten-durchs-Knie-ins-Auge, aber es scheint zu funktionieren
|
Sieht aufwändig aus, aber ich bin hier auch nicht der Mathematikspezialist, wenns funktioniert und ausreichend schnell ist, gut. Da würde ich jetzt gerne mal einen Lösungsansatz von YTWINKY sehen, (falls du Zeit hast .) |
|
Nach oben |
|
 |
ytwinky

Anmeldungsdatum: 28.05.2005 Beiträge: 2624 Wohnort: Machteburch
|
Verfasst am: 28.11.2006, 17:59 Titel: |
|
|
Nu schrei doch nicht so laut, ich bin zwar kurzsichtig, aber nicht schwerhörig
Für genaue Berechnungen ist Double echt super, wenn an die Zeit dafür hat..
Für die Ausgabe von doppelt genauen Zahlen siehe:
http://forum.qbasic.at/viewtopic.php?p=20694&highlight=format#20694
und Befehlsreferenz etc pp usw.
Ich hatte gestern abend schon ein Beispiel fertig, war dann aber zu müde..
..so müde, daß ich es auch nicht mehr auf meinen Stick kopiert hab..
mußte warten, bis ich wieder zu Hause bin ~20:00
Bis später
Gruß
ytwinky _________________
v1ctor hat Folgendes geschrieben: | Yeah, i like INPUT$(n) as much as PRINT USING.. | ..also ungefähr so, wie ich GOTO.. |
|
Nach oben |
|
 |
stevie1401
Anmeldungsdatum: 04.07.2006 Beiträge: 133
|
Verfasst am: 28.11.2006, 19:15 Titel: |
|
|
Tja, was in aller Welt nützt mir das schönste runden!
Guckt euch DAS einmal an:
dim as single a,b
a=68.50
b=59.05
print a-b
Ergebnis: 9.450001
QBASIC spuckt als Ergebnis feine 9.45 aus.
Ebenso das GFA-Basic
Und DAS soll KEIN BUG sein??
Wie soll man denn so rechnen? |
|
Nach oben |
|
 |
Mao
Anmeldungsdatum: 25.09.2005 Beiträge: 4409 Wohnort: /dev/hda1
|
Verfasst am: 28.11.2006, 20:43 Titel: |
|
|
Das ist eigentlich der absolute Wert.
Hm, verwendest du irgendwo zwischen drin die ABS-Funktion?
Ich kann dir nur erklären, warum was anderes rauskommt mit Double als Datentyp, anstatt mit Single - 'ne Gleitpunktoperation ist nie genau.
Aber warum da das Vorzeichen weggelassen wird, kA.  _________________ Eine handvoll Glück reicht nie für zwei.
--
 |
|
Nach oben |
|
 |
Michael712 aka anfänger, programmierer
Anmeldungsdatum: 26.03.2005 Beiträge: 1593
|
Verfasst am: 28.11.2006, 20:47 Titel: |
|
|
Äh, Mao?
Wieso soll da ein Vorzeichen sein?
a>b, daraus folgt, das a-b größer als 0 sein muss
Und das heißt, kein Vorzeichen. Oder erwartest du ein "+ 9.450001"?
Edit:
Code: | #include <stdio.h>
int main () {
float a;
float b;
float c;
a=68.50;
b=59.05;
c=a-b;
printf ("Ergebnis: %f ",c);
};
|
In C ist es genau so.
Also ist so die beste möglichkeit(gefunden auf freebasic.net, nach 0.17 "portiert"):
Code: | Declare Function RoundOff(x As Single, k AS Integer) As Single
Dim As Single a,b,c
a=68.5
b=59.05
c=a-b
print c
Print RoundOff(c, 0)
Print RoundOff(c, 1)
Print RoundOff(c, 2)
Print RoundOff(c, 3)
Print RoundOff(c, 4)
Print RoundOff(c, 5)
Print RoundOff(c, 6)
Function RoundOff(x As Single, k As Integer) As Single
Dim pn As Double
Dim np AS Integer
Dim l As LongInt
If k > 6 Then k = 6
If Abs(x) > 1E-39 Then
np = k - 1 - Int(Log(Abs(x)) / Log(10#))
pn = 10 ^ Cdbl(np)
l = pn * x
Return l / pn
Else
Return 0
End If
End Function |
_________________
Code: | #include "signatur.bi" |
|
|
Nach oben |
|
 |
ytwinky

Anmeldungsdatum: 28.05.2005 Beiträge: 2624 Wohnort: Machteburch
|
Verfasst am: 28.11.2006, 21:00 Titel: |
|
|
So, hier nun das versprochene Beispiel: Code: | #include "vbcompat.bi"
dim as double summe, mwst, netto, Test, d
Dim As String Fmt="#####.00", dt
summe=10
netto=summe/1.16
'netto=int(netto*100+0.5)/100
mwst=summe-netto
print "Summe "+str(summe)
print "Netto "+str(netto)
print "Mwst "+str(mwst)
'Bei Summe=100 ist es genauso
'Auch CINT hilft nichts.
?"Summe " &Format(summe, Fmt) &" Euro"
?"Netto " &Format(netto, Fmt) &" Euro"
?"Mwst " &Format(mwst, Fmt) &" Euro"
Test=mwst+netto
?"Test " &Format(Test, Fmt) &" Euro"
?Summe-Test
?"Soll=0=" &Format(Summe-Test, Fmt)
?
?"Problem"
?"Netto " &Format(netto, Fmt)
dt=Format(netto, Fmt)
dt=Left(dt, Instr(dt, ",")-1) &"." &Mid(dt, Instr(dt, ",")+1)
d=Val(dt)
?dt
?d
?"Format=" &Format(d, Fmt)
?"wg. ',' im String"
?"btw.: +Str(Irgendwas) ist dasselbe wie &Irgendwas"
Sleep | ..das mit dem Runden ist so eine Sache, wie das letzte Beispiel zeigt
Wenn es einfacher wäre, gäbe es sicherlich schon eine allgemeine Runden-Funktion..
Gruß
ytwinky _________________
v1ctor hat Folgendes geschrieben: | Yeah, i like INPUT$(n) as much as PRINT USING.. | ..also ungefähr so, wie ich GOTO..
Zuletzt bearbeitet von ytwinky am 28.11.2006, 21:03, insgesamt 2-mal bearbeitet |
|
Nach oben |
|
 |
Mao
Anmeldungsdatum: 25.09.2005 Beiträge: 4409 Wohnort: /dev/hda1
|
Verfasst am: 28.11.2006, 21:00 Titel: |
|
|
Scheiße, ich glaub, jetzt fängts an...
Sry, hab irgendwas mit b-a gemacht.  _________________ Eine handvoll Glück reicht nie für zwei.
--
 |
|
Nach oben |
|
 |
dreael Administrator

Anmeldungsdatum: 10.09.2004 Beiträge: 2529 Wohnort: Hofen SH (Schweiz)
|
|
Nach oben |
|
 |
Dominik
Anmeldungsdatum: 22.12.2004 Beiträge: 172
|
Verfasst am: 28.11.2006, 23:36 Titel: |
|
|
Wenn ich das richtig sehe, dann arbeitest du mit einfacher Genauigkeit. Wenn dir diese Genauigkeit reicht, dann rechne doch mit doppelter Genauigkeit und konvertiere, wenn du fertig bist, das Ergebnis nach Single:
Code: |
Dim A As Double = 68.50
Dim B As Double = 59.05
Print CSng(A - B)
Sleep
|
Ausgabe: 9.45 |
|
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.
|
|