Hi, ich habe folgenes Problem: uint16_t erg=0, test=43; erg=(test*1000/1440); liefert: 29 Rückrechnung 29*1440/1000 =41,7 test kann was bis 55 sein. Wie bekomme ich das genauer. In diesem Beispiel wäre 30 als "Ergebnis" genauer. ->43,2 Wie runde ich mit wenig Aufwand, damit es für alle Zahlen der Reihe bis 55 besser passt? Danke. Maddin
Jens schrieb: > Wie runde ich mit wenig Aufwand, damit es für alle Zahlen der Reihe bis > 55 besser passt? Das ganze hat mit math. Runden überhaupt nichts zu tun. Du rechnest mit ganzen Zahlen und da es dort keine Nachkommastellen gibt werden diese quasi abgeschnitten und somit kann man da auch nichts mehr "runden". Schau dir mal das an: - https://www.mikrocontroller.net/articles/Festkommaarithmetik
Würdest du in floating point rechnen, wäre es (uint16_t)( (test*1000.0)/1440.0 + 0.5 ) für richtiges Runden. Zurück zur Ganzzahlarithmetik ziehst du die 0.5 vor die Division, also (test*1000u + 720u)/1440u
Einfach mit größeren Zahlen arbeiten. 43 * 1.000.000 / 1440 ergibt 29861 Das zurückgerechnet ergibt wieder genau 43.
43*1000/1440 ist nun mal 29,861.. Integer-Rechnung liefert dann 29, völlig korrekt. In Real würde man noch 0.5 addieren und dann die Vorkommastellen nehmen. Was du tun kannst, ist zu rechnen: test*10000/1440+5. Das gibt dann 303.6, dann noch durch 10 teilen. Oder, da dann der uint32_t nicht reicht: (test*1000/144+5)/10
Hallo, das was du brauchst ist ein Trick aus der Festkommaarithmetik 1000/1440 ist etwa gleich 711/1024 (Unterschied in der 1/10000 Stelle) und deshalb rechnets du besser erg = test*711/1024 die Multiplikation test*711 läuft normal die Division .../1024 durch rechtsschiften und mit Runden auf eine halbe Stelle ist es dann erg = (test*711 + 256)/1024 Vorsicht: mit uint16_t auf Zahlenüberlauf achten Gruss
Ich muss mich korrigieren erg = (test*711 + 512)/1024 512 ist die Hälfte von 1024
HildeK schrieb: > 43*1000/1440 ist nun mal 29,861.. M.e.a. rundet ((43*1000) + (1440/2))/1440 richtig
Stefan schrieb: > das was du brauchst ist ein Trick aus der Festkommaarithmetik > > 1000/1440 ist etwa gleich 711/1024 (Unterschied in der 1/10000 Stelle) > und deshalb rechnets du besser > > erg = test*711/1024 > > die Multiplikation test*711 läuft normal > die Division .../1024 durch rechtsschiften Den Trick predige ich, in der einen oder anderen Form, seit Jahr und Tag. Inzwischen hab ich's aufgegeben. Du fragst dich, was jetzt mit deinem Tipp passieren wird? Ich denke du willst das gar nicht wissen... Wenn du Glück hast, großes Glück, dann fragt einer nach, wie Du auf 711/1024 gekommen bist. Von mir jedenfalls ein :+1:
Uwe B. schrieb: > M.e.a. rundet ((43*1000) + (1440/2))/1440 richtig Ja. Runden = vorher halben Teiler zu addieren. Negative Zahlen sind ein eigenes Thema! Stefan schrieb: > 1000/1440 ist etwa gleich 711/1024 Das ist besser, hat für sich gesehen mit dem Problem nichts zu tun. Es ist eine Optimierung. Wenn es funktioniert aber zu viel Platz oder Laufzeit braucht. Auch hier fällt man mit negativen Zahlen auf die Nase.
Jens schrieb: > Danke. > Maddin ...und ward nicht mehr gesehen. 10/10 Ghost-TO-Punkte, leider nur 2/10 Troll-Punkte Zu Fractional-Arithmetic mit HildeK's Tipp hätte er sich schon mal wieder melden können. Viele Mikrocontroller unterstützen Fractional-Arithmetic im Befehlssatz, wie z.B. s16*s16-Multiplikation, die das Ergebnis um 15bits nach rechts geschoben wieder in ein 16bit-Register schmeißt. mfg mf
:
Bearbeitet durch User
jo schrieb: > Stefan schrieb: >> das was du brauchst ist ein Trick aus der Festkommaarithmetik >> >> 1000/1440 ist etwa gleich 711/1024 (Unterschied in der 1/10000 Stelle) >> und deshalb rechnets du besser >> >> erg = test*711/1024 >> >> die Multiplikation test*711 läuft normal >> die Division .../1024 durch rechtsschiften > > Den Trick predige ich, in der einen oder anderen Form, seit Jahr und > Tag. > Inzwischen hab ich's aufgegeben. > > Du fragst dich, was jetzt mit deinem Tipp passieren wird? Ich denke du > willst das gar nicht wissen... > > Wenn du Glück hast, großes Glück, dann fragt einer nach, wie Du auf > 711/1024 gekommen bist. Hallo, die Frage wollte ich eigentlich stellen, bin aber selbst draufgekommen. Verhältnisgleichung bzw. Normierung auf 1024. Nur fängt man sich damit nicht schon vorher einen Rundungsfehler ein bevor es überhaupt losgeht? Genau wären es ja 711,111.
Veit D. schrieb: > Nur fängt man sich damit nicht schon vorher einen Rundungsfehler ein > bevor es überhaupt losgeht? > Genau wären es ja 711,111. So ist das. Rundungsfehler fängst du dir selbst bei float oder double ein, weil du nur eine endliche Zahl von Bits zur Verfügung hast ;-)
Veit D. schrieb: > Nur fängt man sich damit nicht schon vorher einen Rundungsfehler ein > bevor es überhaupt losgeht? Bei nur wenigen Aufgaben ist das schlimm. Und noch seltener ist dieser Fehler größer als z.b. ein off-by-one beim Teiler oder Rundungsoffset. Wichtig ist nur die Priorität: Erst richtig (runden), dann lesbar. Und effizient nur, wenn man es sich erlauben kann oder muss. Wenn 2 zahlen im Datenblatt oder Schaltplan stehen, sind die einfacher zu verstehen als wenn da zwei ganz andere sind.
Uwe B. schrieb: > M.e.a. rundet ((43*1000) + (1440/2))/1440 richtig Ja, das ist die korrekte Antwort. Alles andere muss man nicht lesen
Veit D. schrieb: > Nur fängt man sich damit nicht schon vorher einen Rundungsfehler ein bevor > es überhaupt losgeht? Wenn dir der Rundungsfehler zu groß ist, musst du eine bessere Näherung für deine 1000/1440 verwenden. Bei Annäherung durch 711/1024 weicht dein Faktor um 0,016 Prozent ab, bei 5689/8192 wäre es nur eine Abweichung von 0,002 Prozent, also ein Faktor 10 besser. Dafür schränkt sich bei gegebenem Variablentyp dein Zahlenbereich entsprechend ein.
Wolfgang schrieb: > Bei Annäherung durch 711/1024 weicht dein Faktor um 0,016 Prozent ab, > bei 5689/8192 wäre es nur eine Abweichung von 0,002 Prozent, also ein > Faktor 10 besser. Dafür schränkt sich bei gegebenem Variablentyp dein > Zahlenbereich entsprechend ein. Hier ist ausdrücklich ein Bereich bis 55 vorgegeben. Ob man den in diesem Fall halten kann, hängt davon ab, wie groß auf der Plattform int ist.
Hallo, Jens schrieb: > ich habe folgenes Problem:... Es wurde in dieser Diskussion zwar schon diverse Male die Lösung beschrieben aber hier nochmal eine ausführliche Erklärung wie das mit dem Runden bei Ganzzahl-Arithmetik geht: Beitrag "Re: UART Baudrate clever runden" rhf
Bis x=55: (x*89+62)/128 1 0,69 1,00 2 1,39 1,00 3 2,08 2,00 4 2,78 3,00 5 3,47 3,00 6 4,17 4,00 7 4,86 5,00 8 5,56 6,00 9 6,25 6,00 10 6,94 7,00 11 7,64 8,00 12 8,33 8,00 13 9,03 9,00 14 9,72 10,00 15 10,42 10,00 16 11,11 11,00 17 11,81 12,00 18 12,50 13,00 19 13,19 13,00 20 13,89 14,00 21 14,58 15,00 22 15,28 15,00 23 15,97 16,00 24 16,67 17,00 25 17,36 17,00 26 18,06 18,00 27 18,75 19,00 28 19,44 19,00 29 20,14 20,00 30 20,83 21,00 31 21,53 22,00 32 22,22 22,00 33 22,92 23,00 34 23,61 24,00 35 24,31 24,00 36 25,00 25,00 37 25,69 26,00 38 26,39 26,00 39 27,08 27,00 40 27,78 28,00 41 28,47 28,00 42 29,17 29,00 43 29,86 30,00 44 30,56 31,00 45 31,25 31,00 46 31,94 32,00 47 32,64 33,00 48 33,33 33,00 49 34,03 34,00 50 34,72 35,00 51 35,42 35,00 52 36,11 36,00 53 36,81 37,00 54 37,50 38,00 55 38,19 38,00 WS
Jens schrieb: > Wie bekomme ich das genauer. Entweder du entschließt dich doch zur Rechnung in float oder du benutzt die Funktion div bzw. ldiv. Die liefern neben dem Quotienten auch den Rest, den du dann auswerten kannst, um ggf. das Ergebnis nach der Truncation auf Integer um 1 zu erhöhen. W.S.
Du hast einen Faktor von 1000/1440 = 0,694444... Ich würde mit 0,694444 x 65536 = 45511 multiplizieren. 16 Bit x 16 Bit -> 32 Bit Wirft man die unteren 16 Bit weg, rundet aber die verbleibenden 16 Bit um 1 auf, wenn das höchste weggeworfene Bit gesetzt ist, kommt man auf sehr gut gerundete Ergebnisse. Mit asm kein Problem. Aber auch mit C gut machbar. Gute Nacht!
(x<<6+x<<4+x<<3+x+62)>>7 Nix 16x16 und 32Bit und evtl. runden Bis 55!!
WS schrieb: > (x<<6+x<<4+x<<3+x+62)>>7 > Nix 16x16 und 32Bit und evtl. runden Allerdings auf einem AVR auch nicht sonderlich performant, mangels barrel shifter.
Für AVRs mit Multiplizierer:
1 | erg = (test * 178 + 124) / 256 |
Das ist die weiter oben von W.S. vorgeschlagene Lösung mit doppelt so großen Parametern, um den 7-Bit-Shift zu vermeiden. Das funktioniert für 0 ≤ tmp ≤ 76 und braucht nur 7 Taktzyklen.
:
Bearbeitet durch Moderator
Dividiert durch 0x100 und nicht 0xff…wo ist mein Kaffee, ich sehs grad nicht…
((((x<<2+x)<<1+x)<<3+x+62)<<1)>>8 2+1+3+1 shift left 4 add Ohne mul WS
Yalu X. schrieb: > Für AVRs mit Multiplizierer: >
1 | > erg = (test * 178 + 124) / 256 |
2 | >
|
Sind die 124 ein Fehler (128) oder hat es damit was auf sich?
Diese "Mul-Shift"-Vorgehensweise ist heute nicht mehr grundsätzlich zu empfehlen, da inzwischen öfters ein div direkt vom Controller unterstützt wird. Beim "Mul-Shift" müssen zum einen Überläufe verhindert werden und zum anderen könnte temporär ein größerer Datentyp nötig sein. Das alles wirkt sich negativ auf die Programmierzeit, Fehlergefahr und Laufzeit aus.
Wir haben alle mit den vom TO genannten Zahlen eine spezielle Lösung genannt, die für ihn funktioniert. Man sollte die Rechnung auch etwas allgemeiner betrachten. So wäre das korrekt gerundete Ergebnis der der Ganzzahldivision erg=a*b/c dann: erg = (a*b + c/2)/c Näherungen mit schnelleren Bitshifts sind nur bei festen Zahlen b und c zu finden und vermutlich nicht mal bei allen.
Hallo, 1000/1440*128=88,88888.. = 89-1/9 Also x*(89-1/9)+64 < x*89+64 Es kann also sein, dass (x×89+64)/128 als Ganzzahl im Argumentbereich zu gross ist. Deshalb versuche ich in der Rundung zu optimieren. Das Ergebnis ist exakt im Argumentbereich und optimal umsetzbar in Zeit und Recourceneinsatz. Auch ohne mul und 32bit optimal zu lösen. WS
HildeK schrieb: > Wir haben alle mit den vom TO genannten Zahlen eine spezielle Lösung > genannt, die für ihn funktioniert. Im Gegenteil: Die meisten haben das Problem des TO (die Rundung) ignoriert oder beiläufig eingeflochten und sich auf die (unnötige) Optimierung der Division gestürzt. Und manche haben gar das Float-Fass geöffnet. Und das, obwohl der TO im OP alle Informationen gegeben hat und es im Titel sehr präzise zusammengefasst hat: > richtig Runden nach Ganzzahl Operation Eine spezielle Lösung wäre die Wertetabelle. Bei 55 Werten von 8 Bit ein überschaubarer Aufwand mit perfekter (beliebiger) Genauigkeit.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.