Hallo Ihr! ich hab mit einem ATMega32 diverse Berechnungen zu erledigen und zum Schluss sollte dann eine uint16_t Zahl rauskommen habe drei Formeln: 1. teilergebnis1= (uint16_t*int16_t/(2^24))+(uint16_t/(2^10)) 2. teilergebnis2= (1/(2^14))*(int16_t + (int16_t*int16_t/(2^17)) + ((int16_t*(int16_t)²)/2^34) 3. teilergebnis3= noch komplizierter Endergebnis ist dann teilergebnis2*unit16_t + teilergebnis3 ich sitzt jetzt schon seit 4 Tagen daran und hab fast keine Haare mehr weil ich sie mir alle rausgerissen habe... mir ist klar dass wenn man uint*int rechnet uint wieder als Ergebnis rauskommt, jedenfalls mit gcc. Meine Frage: um bei den Multiplikationen von zwei oder mehreren 16bit integern, deren Ergebnis 32bit oder gar 64bit sind kein Fehler zu machen ist welche Methode die sichere? Variablen gleich mit 64bit deklarieren? oder während der Berechnung castn? oder eine ganz andere Methode? ich habe die beiden Methoden zum erbrechen getestet und komme bei jeder methode auf ein anderes Ergebnis... ich hab die ganze sache mal mit stift und blatt papier nach gerechnet und auf das ergebnis auf dem papier komme ich mit beiden methoden nicht.. zusätzlich kommt ja noch das vorzeichen Problem dazu... wie löse ich das am besten? vllt alle variablen ersteinmal uint machen und dann bei der Berechnung berücksichtigen? oder erst beim endergebnis? Danke schonmal für euren support/feedback :D
Fatih T. schrieb: > mir ist klar dass wenn man uint*int rechnet uint wieder als Ergebnis > rauskommt, jedenfalls mit gcc. Wie bist du darauf gekommen? > wie löse ich das am besten? Du musst dir selber erst mal klar werden, wo Überläufe passieren können, und wie du damit umgehen kannst. Auf jeden Fall wirst du zwischendurch herunterskaliern müssen, wenn du mit 32 Bit auskommen willst. Denn bei 2^32/2^34 bleibt nicht viel übrig...
uint*int=uint.... hat man mir so beigebracht.. es ist äquivalent zu double*int=int... also es wird der typ genommen der mit kleinerer bit anzahl und genauigkeit und vorzeichen.. oder hab ich da was falsch gelernt? ja da hast du recht, da bleibt nicht viel übrig und zum float typ will ich nicht casten... mein problem ist wie ich an die sache überhaupt mal rangehe... gleich von anfang an 32 oda 64 bit variablen oder während der laufzeit richtiges casten... oder vllt doch mit float arbeiten?
Fatih T. schrieb: > um bei den Multiplikationen von zwei oder mehreren 16bit integern, deren > Ergebnis 32bit oder gar 64bit sind kein Fehler zu machen ist welche > Methode die sichere? Den Code durchgehen und die Reihenfolge feststellen, in der die Operationen ausgeführt werden. Danach die Wertebereiche der Variablen abklären und die Sequenzen von oben nach unten durchgehen, ob es zu einem Overflow kommen kann. > Variablen gleich mit 64bit deklarieren? Das ist die Rundumschlagmethode :-) Äquivalent mit: Auf einem Kreuzfahrtschiff ist Tag und Nacht eine Schwimmweste zu tragen. > oder > während der Berechnung castn? Falls es notwendig ist: ja > ich habe die beiden Methoden zum erbrechen getestet und komme bei jeder > methode auf ein anderes Ergebnis... Dann machst du was falsch.
Fatih T. schrieb: > uint*int=uint.... hat man mir so beigebracht.. wenn ich mal eine Definition für uint zu Grunde lege. Ja, stimmt. Solange die Bitzahlen gleich sind und unsigned und signed aufeinander treffen, gewinnt unsigned. > es ist äquivalent zu > double*int=int... Äh. Nein. Bei double und int gewinnt double > also es wird der typ genommen der mit kleinerer bit > anzahl und genauigkeit und vorzeichen.. Denk über diesen Satz nochmal nach. Ist der logisch, macht das Sinn, wenn man es so definiert?
Fatih T. schrieb: > gleich von anfang an 32 oda 64 bit variablen oder während der > laufzeit richtiges casten... oder vllt doch mit float arbeiten? Das hängt auch von Deinen Anforderungen an die Genauigkeit ab. Float hat zwar einen größeren Dynamikbereich, aber weniger Stellen als int32_t. In jedem Fall solltest Du Dir die Reihenfolge der Berechnungen überlegen. Bei Verwendung der Ganzzahl-Arithmetik würde ich zum Beispiel die Division erst zum Schluß machen. Beispiel:
1 | 1. teilergebnis1= (uint16_t*int16_t/(2^24))+(uint16_t/(2^10)) |
würde ich umformen in:
1 | teilergbnis1 = ((uint16_t*int16_t) + (uint16_t*2^14) )/(2^24) |
Wie werden Deine Teilergenisse weiter verwendet? Kannst Du Dir erlauben den Rest abzuschneiden? Oder solltest Du besser runden:
1 | teilergbnis1 = ((uint16_t*int16_t) + (uint16_t*2^14) + (2^23) )/(2^24) |
Anmerkung: Ich habe hier nur die prinzipielle Reihenfolge dargestellt, die Größen der Variablen habe ich nicht angefaßt, das ist ein anderes Thema.
mir was fast sicher dass ich was falsch mache :) oke die reihenfolge feststellen und den wertebereiche festlegen hört sich nicht schlecht an, aber wird es denn nicht schnell unübersichtlich? lieber ne schwimmweste tragen als zu ertrigen :).. aber ich könnte doch nur eine 64bit variable als teilergebnis nehmen und nacheinander die variable mit den jeweiligen operator hineinschieben, also zb so: uint64_t teilerg1; uint16_t c1; uint16_t c2; teilerg1=(uint64_t)c1; teilerg1*=(uint64_t)c2; teilerg1+=(uint64_t)69; teilerg1>>=24; //teilerg1/=c^24; . . . usw?
@volkmar also zum Schluss muss rauskommen eine zahl wischen 300,00 bis 1100,00 .. die genauigkeit auf zweinachkommastellen muss ich einhalten... oda es geht auch wenn der wertebereich zwischen 30000 bis 110000 geht, dann weis ich ja dass das endergebnis um den faktor 100 zu groß ist... aba des ist dann kein problem mehr beim weiteren programm verlauf.
Fatih T. schrieb: > uint64_t teilerg1; > > uint16_t c1; > uint16_t c2; > > teilerg1=(uint64_t)c1; > teilerg1*=(uint64_t)c2; > teilerg1+=(uint64_t)69; wo kommen jetzt schon wieder die 69 her? > teilerg1>>=24; //teilerg1/=c^24; Ich hab ja nicht gesagt, dass du dein Programm so umformulieren musst. Du musst dir nur darüber klar werden, dass die Operationen 'im Prinzip' in dieser Reihenfolge ablaufen. (Einschub: Du bist dir darüber im Klaren, dass ^ in C NICHT die Exponentenoperation ist? 2^24 ist nicht "2 hoch 24"!) Das ist dein Ausdruck teilergebnis1= (c1*c2/(1<<24))+(uint16_t/(1<<10)) Was wird gerechnet c1 ist ein uint16_t c2 ist ein uint16_t also wird 16*16 Bit multiplizert und das Ergebnis ist wieder ein uint16_t. Welchen Wert kann c1 maximal annehmen? Da du nichts dazu sagst, nehme ich mal was an: 2500 Welchen Wert kann c2 maximal annehmen? Du hast wieder nichts gesagt, also erfinde ich wieder: 5000 2500 * 5000 macht am Taschenrechner 12500000. Zu groß für uint16_t. Also muss man den Compiler zwingen, die Multiplikation in 32 Bit zu machen: -> Eines der Argument hochcasten (uint32_t)c1 * c2 jetzt wird 32 * 16 Bit multiplizert. Dazu muss der Compiler den andern Operanden ebenfalls auf 32 Bit bringen und das Ergebnis ist wird ein uint32_t sein. Das arithmetische Ergebnis, 12500000, passt in einen uint32_t. Der Teil ist also erledigt, da passiert nichts mehr. Wie gehts weiter? ((uint32_t)c1 * c2 / (1<<24) Das uint32_t Ergebnis wird durch 1<<24 dividiert. Soweit ok. Da kann nichts passieren. Ausser das ein paar Stellen wegfallen. Immerhin so viele, dass von den 12500000 nichts mehr übrig bleibt :-) Dann wird c4 / (1<<10) gerechnet. c4 ist wieder ein uint16_t. Den durch 1<<10 zu dividieren gibt wieder keinen Overflow damit bleibt erster_Teilergebnis + zweites_Teilergebnis der erste Teil war ein uint32_t und wir wissen, der kann nicht größer als x sein, das zweite Teilergebnis ist ein uint16_t und kann nicht größer als y sein. (Ich muss hier x und y schreiben, weil du nichts zu den Zahlen gesagt hast). uint32_t + uint16_t wird daher als uint32_t gerechnet. Die große Frage ist jetzt: Wird dieses Ergebnis überlaufen? Setz deine maximalen Zahlen ein und finde es raus. Und so gehst du deine Berechnungen durch. Und wenn du fertig bist, hast du die Stellen identifiziert an denen Überläufe passieren können und wo du hochcasten musst.
@karl heinz die 69 hab ich jetzt mal erfunden um die art und weise nur zu verdeutlichen was ich mit "nacheinander mit operator hineinschieben" meine. alle unsigned Variablen können werte von 0 bis 2^16 und alle signed Variablen von -2^15 bis +2^15.. des macht die ganze sache ja so böse.. ja ich weis dass man in C nicht 2^x schreiben kann :D .. also da ich so wenig wie möglich von der genauigkeit abgeben darf mache ich es warscheinlich so wie der Volkmar es gesagt hat.. 1. ich erweitere alle brüche so dass kein bruch mehr übrig bleibt 2. führe die multiplikationen und die additionen durch 3. teile das zwischenergebnis durch die 2^x mit der ich zuvor alle brüche erweitert hab 4. und mach zum Schluss noch vorzeichenkorrektur bin mal gespannt ob des hinhaut jetzt endlich mal
nachtrag:
> teilergebnis1= (c1*c2/(1<<24))+(uint16_t/(1<<10));
hier muss nach rechts geschoben werden, da geteilt wird
also richtig ises so:
teilergebnis1= (c1*c2/(1>>24))+(uint16_t/(1>>10));
Fatih T. schrieb: > nachtrag: > >> teilergebnis1= (c1*c2/(1<<24))+(uint16_t/(1<<10)); > hier muss nach rechts geschoben werden, da geteilt wird > > also richtig ises so: > > teilergebnis1= (c1*c2/(1>>24))+(uint16_t/(1>>10)); No. 1>>24 ergibt 0. richtig wäre (c1*c2)>>24 und das alles wäre so viel einfacher, wenn du konkreten Code zeigen würdest.
ja es wäre sehr viel einfacher wenn ich mein code reinstelle da hast du recht, aba ich bin nicht sicher ob ich es von meiner firma aus her reinstellen darf. Das risiko, deswegen gefeuert zu werden geh ich lieber nicht ein. nein auch nicht ganz richtig ^^ ganz richtig: ((uint32_t)c1*(uint32_t))c2>>24 :D
wieder falsch... also jetzt nochmal! ((uint32_t)c1*(uint32_t)c2)>>24 die Kalmmer war falsch
Fatih T. schrieb: > ja es wäre sehr viel einfacher wenn ich mein code reinstelle da hast du > recht, aba ich bin nicht sicher ob ich es von meiner firma aus her > reinstellen darf. Das risiko, deswegen gefeuert zu werden geh ich lieber > nicht ein. 2 Zeilen Berechnung? Entschuldige: Aber das sind keine Firmengeheimnisse. Demnächst darfst du hier nicht mehr posten weil du nicht weißt ob du den Buchstaben 'M' benutzen darfst. > nein auch nicht ganz richtig ^^ Ich tipp mir da jetzt nicht mehr die Finger wund. Du weiß, dass mindestens einer der Teilnehmer ein uint32_t sein muss. Also schreib ich da nicht mehr, wenn wir sowieso nicht über konkreten Code reden.
Fatih T. schrieb: > also zum Schluss muss rauskommen eine zahl wischen 300,00 bis 1100,00 .. > die genauigkeit auf zweinachkommastellen muss ich einhalten... oda es > geht auch wenn der wertebereich zwischen 30000 bis 110000 geht, Wenn Du sowieso ein Ergebnis mit 2 Nachkommastellen hast, dann kommst Du mit uint16_t für die Teilergebnisse eh nicht hin. Also entweder mit float oder wenn Du den Faktor 100 (oder eventuell auch 128 wegen der Teiler) verwendest mit uint32_t.
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.