Hallo liebes Forum,
ich habe hier eine Anfängerfrage zum Inline Assembler, welchen ich in
ein Programm für meinen ATMEGA2560 benutzen muss, hoffe trotzdem auf
Eure Hilfe.
Das Problem:
Für eine umfangreichere Berechnung muss zwingend ein Term berechnet
werden, welcher aus 9-stelligen Zahlen besteht: z.B.
oder im code aktuell umgestellt zu:
1
floatfq=(((float)Fu)-((float)Fl))/((float)(Fu));
Da Ergebnis und Operanden Float sind, werden bei der Berechnung nicht
ausreichend signifikante Stellen verwendet. Dadurch wird das Ergebnis
ungenau. Es wird statt 599999912 z.B. nur mit 59999991 gerechnet wegen
der 8 signifikanten Stellen bei float.
Bisherige Lösungsversuche:
alle Versuche waren bislang erfolglos, ebenfalls der Versuch die
avr_f64.c Bibliothek von Detlef_a dazu zu verwenden.
Aktuelle Hoffnung:
Der Beitrag aus diesem Forum
[[http://www.mikrocontroller.net/articles/AVR_Arithmetik#32_Bit_.2F_32_Bit]]
mit dem Assembler code zur Division von 2 32Bit Werten scheint mir eine
Lösung meines Problems zu sein, wobei das für mich eine fremde Welt ist.
Was ich einfach nicht schaffe - mangels Wissen zu den Assembler
Geschichten - ist es diesen Code bei mir einfach im laufenden C-Code
einzubinden und auszuführen.
Ich habe die Variablen z.B.: Fl=599999912 und Fu=6000000000 irgendwie zu
übergeben und möchte fq als Ergebnis der Division zurück erhalten.
In den Projecteinstellungen meines AtmelStudio7 steht unter
Toolchain/AVR/GNU Assembler als Command: avr-gcc.
Im Code habe ich durch basteln nun diese Variante des o.g. Arithmetic
Beispiels und kann den Code auch fehlerfrei übersetzen, nur tut er
natürlich nicht was er soll.
Weshalb verwendest du nicht einfach 32-Bit Typen in C?
Allerdings umschleicht mich das Gefühl, dass bei deiner Rechnerei, egal
welcher davon, bei Ganzzahlen nicht das rauskommt, was du haben willst.
Bei der Assemblerprogrammierung kann ich Dir nicht weiterhelfen.
Hier gibt es die BCD-Bibliothek "decNumber" mit der man die
Grundrechenarten mit bis zu 34 Dezimalstellen bekommt:
http://speleotrove.com/decimal/
Das ist reines C und die Beispielprogramme sollten Dein Problem
abdecken.
Eine Division von 2 9 stelligen Zahlen bedingt kein Float, speziell
nicht wenn man's selbst macht.
Normale compiler koennen nur 32bit float, welches eh nur 6 signifikannte
Stellen kann. Was hier verwendet werden muesste waere allenfalls 64bit
float.
Aber eigentlich genuegt fixed point, bedeutet die Verwaltung des
dezimalpunktes macht man selbst.
Ohne den Code angeschaut zu haben, ja man machts mit schieben und
subtrahieren. Das waere dann fuer die mantisse
Hallo A.K.
bis zu der problematischen Stelle mache ich genau das. Ich habe alles
auf 32Bit in gewandelt. Eigentlich sind die Werte z.B. 6000000.12 oder
5999123.45.
Da nun aber die Division ansteht und sich die Änderungen von Berechnung
zu Berechnung im Nachkommabereich, also den letzten beiden Stellen
abspielen geht das nicht mehr. Das Ergebnis ist ja kleiner 1
599999999 / 600000000 = 0.99999999833
als int32 also dann 0.
A. K. schrieb:> Weshalb verwendest du nicht einfach 32-Bit Typen in C?>> Allerdings umschleicht mich das Gefühl, dass bei deiner Rechnerei, egal> welcher davon, bei Ganzzahlen nicht das rauskommt, was du haben willst.
Da sind wir schon 2.
Allgemein ist es meistens zielführender die Berechnung umzustellen. Denn
mit dem Ausdruck von da oben wird ja noch weiter gerechnet, der ist ja
nicht einfach reiner Selbstzweck. D.h. der kommt noch irgendwo vor. Dort
wird dieser Ausdruck mathematisch eingesetzt und dann kommt erst mal der
Stift zum Einsatz um den Ausdruck zu vereinfachen und umzuformen und zu
kürzen. Und dann sieht man weiter.
chrisrabe schrieb:> Hallo A.K.>> bis zu der problematischen Stelle mache ich genau das. Ich habe alles> auf 32Bit in gewandelt. Eigentlich sind die Werte z.B. 6000000.12 oder> 5999123.45.>> Da nun aber die Division ansteht und sich die Änderungen von Berechnung> zu Berechnung im Nachkommabereich, also den letzten beiden Stellen> abspielen geht das nicht mehr. Das Ergebnis ist ja kleiner 1>> 599999999 / 600000000 = 0.99999999833>> als int32 also dann 0.
Aber nicht dann, wenn du nicht durch 600000000 sondern nur durch zum
Beispiel 600 dividierst und dir merkst, dass der Kommapunkt iegentlich
um 6 Stellen weiter rechts sein müsste.
Eine Methode waere auch den AVR Simulator zu verwenden. Da kann man bei
jeder Operation zuschauen, was geschieht. Damit kann man auch
herausfinden wie eigene funktionen aufgerufen werden.
> Ich habe die Variablen z.B.: Fl=599999912 und Fu=6000000000 irgendwie zu> übergeben und möchte fq als Ergebnis der Division zurück erhalten.
Die 6000000000 aus dem Beispiel braucht mehr als 32 Bit zur Darstellung.
---
Wie genau muss das Ergebnis denn sein?
@ KarlHeinz
Das mit dem Umstellen habe ich nun zwei Tage lang in allen möglichen
Varianten versucht.
Klar, der Wert wird später weiterverwendet in einer atan Funktion.
Die eigentliche Änderung zwischen den Werten kommt aber nich
mathematisch heraus. Die Wiederholungen der Berechnung (alle Sekunde)
sind ja z.B:
1-599999999/600000000 = 1 - 0.99999999833 = 0.00000000167
1-599999998/600000000 = 1 - 0.99999999666 = 0.00000000333
1-599999997/600000000 = ...
wie kann ich denn noch umstellen (ohne float), so dass ich diese
Ergebnisse auch erhalte?
// Die 6000000000 aus dem Beispiel braucht mehr als 32 Bit zur
Darstellung.
sorry, stimmt. Das ist ein Tippfehler.
Es handelt sich bei den Werten um Quarz Frequenzen. Der Quarz hat 6MHz
und die Messung ist auf 2 Kommastellen genau.
chrisrabe schrieb:> @ KarlHeinz>> Das mit dem Umstellen habe ich nun zwei Tage lang in allen möglichen> Varianten versucht.>> Klar, der Wert wird später weiterverwendet in einer atan Funktion.
Was genau rechnest du da eigentlich?
Das klingt jetzt immer mehr sehr abenteuerlich. Mit 4 Byte float wirst
du da ziemlich sicher nicht glücklich werden, egal ob diese Division
jetzt klappt oder nicht. 4 Byte float haben so 5 bis 6 signifikante
Ziffern. Aber auch nur dann, wenn man nicht mit ihnen gross rumrechnet.
Rechnet man nur ein bischen werden aus den 5 bis 6 ganz schnell 4 bis 5.
Rechnet man dann noch ein bisschen mehr, dann sind es plötzlich nur noch
3 bis 4. Je komplexer und langwieriger die Berechnungen werden, desto
unbrauchbarer ist das Ergebnis.
Karl H. schrieb:> chrisrabe schrieb:>> Hallo A.K.>>>> bis zu der problematischen Stelle mache ich genau das. Ich habe alles>> auf 32Bit in gewandelt. Eigentlich sind die Werte z.B. 6000000.12 oder>> 5999123.45.>>>> Da nun aber die Division ansteht und sich die Änderungen von Berechnung>> zu Berechnung im Nachkommabereich, also den letzten beiden Stellen>> abspielen geht das nicht mehr. Das Ergebnis ist ja kleiner 1>>>> 599999999 / 600000000 = 0.99999999833>>>> als int32 also dann 0.>> Aber nicht dann, wenn du nicht durch 600000000 sondern nur durch zum> Beispiel 600 dividierst und dir merkst, dass der Kommapunkt iegentlich> um 6 Stellen weiter rechts sein müsste.
Das habe ich in genau dieser Form noch nicht versucht! Probiere ich mal
aus!!
Versucht hatte ich bereits die 5Mio während der Berechnung zu
eliminieren und später wieder mit einzubringen - was nicht klappte
chrisrabe schrieb:> Es handelt sich bei den Werten um Quarz Frequenzen. Der Quarz hat 6MHz> und die Messung ist auf 2 Kommastellen genau.
selbst wenn auf deinem Quarz 6Mhz draufsteht, so macht der trotzdem
keine 6000000 Schwingungen pro Sekunde. Der macht vielleicht 6000087
oder 6000113.5 oder 59999962 oder ....
Es ist daher völlig sinnlos sich da auf massenhaft Kommastellen zu
versteifen. Die stimmen sowieso nicht.
Oder hast du deinen Quarz mit Ziehkondensatoren und Frequenzzähler auf
exakt 6.000 Mhz gezogen und betreibst ihn in einer Klimakammer?
Karl H. schrieb:> Was genau rechnest du da eigentlich?
Anhand der (sehr kleinen) Frequenzänderung des Quarzes kann ich auf das
Temperaturverhalten in einem Gefäß schließen.
Das Ergebnis der Berechnung von oben fq wird weiterverwendet in der
Form:
atan(x*tan(pi*fq))
Das ich mit float nicht weiterkomme musste ich bereits selber lernen :-)
Händisch einen der gewünschten Werde in fq zu platzieren geht und führt
auch zu guten Ergebnissen, nur das Berechnen von FQ klappt halt nicht.
Karl H. schrieb:> chrisrabe schrieb:>>> Es handelt sich bei den Werten um Quarz Frequenzen. Der Quarz hat 6MHz>> und die Messung ist auf 2 Kommastellen genau.>> selbst wenn auf deinem Quarz 6Mhz draufsteht, so macht der trotzdem> keine 6000000 Schwingungen pro Sekunde. Der macht vielleicht 6000087> oder 6000113.5 oder 59999962 oder ....>> Es ist daher völlig sinnlos sich da auf massenhaft Kommastellen zu> versteifen. Die stimmen sowieso nicht.> Oder hast du deinen Quarz mit Ziehkondensatoren und Frequenzzähler auf> exakt 6.000 Mhz gezogen und betreibst ihn in einer Klimakammer?
Das ist richtig, weshalb ja auch nicht der Absolutwert zum Ergebnis
führt, sondern die Änderung.
Ich habe mal für einen reziproken Frequenzzähler in AVR-Assembler eine
Kehrwertbildung gerechnet, 64/32 Bit aus der MATH32-Bibliothek von Andre
Birua
http://avr-asm.tripod.com/math32x.html
(da ploppt eine aufdringliche Seite dazwischen)
die 64 Bit waren nötig, damit sich 32 Vorkommastellen ergeben.
Das Ganze geht auch ohne Division, da die 600... (Die restlichen Nullen
spare ich mir jetzt) ja konstant ist.
Also muss man nur die Abweichung * 0.00000000167 nehmen oder * 167 (dann
gehen auch Ganzzahlen). Den ATN kann man dann über
Tabellen-Interpolation bewerkstelligen.
Jörg
Joerg W. schrieb:> Das Ganze geht auch ohne Division, da die 600... (Die restlichen Nullen> spare ich mir jetzt) ja konstant ist.
ok, hier habe ich leider falsch beschrieben. Die 6Mio ist nicht
konstant, sondern wird nach jeter Iteration durch den zuletzt gemessenen
Frequenzwert ersetzt. Es kommen also immer in Betracht:
Joerg W. schrieb:> Das Ganze geht auch ohne Division, da die 600... (Die restlichen> Nullen> spare ich mir jetzt) ja konstant ist.>> Also muss man nur die Abweichung * 0.00000000167 nehmen oder * 167 (dann> gehen auch Ganzzahlen). Den ATN kann man dann über> Tabellen-Interpolation bewerkstelligen.>> Jörg
Ich würde das ebenfalls so lösen. Wollte die Antwort gerade schreiben,
dann hab ich zum Glück nochmal aktualisiert :)
Je nachdem, mit wievielen Nachkommastellen (*167, *1667, *16667, ..) man
rechnet dürfte das sogar genauer sein. Der Fehler der entsteht wird halt
Multipliziert. Also je größer die Differenz zu 600000000, desto größer
der absolute Fehler.
Hallo chrisrabe,
wie wäre es mit folgender Vorgehensweise?
1=6000000/6000000 (6MHz in Hz)
1- 5999999/6000000 = (6000000-5999999)/6000000 = 1/6000000 = 0,1/600000
= 0,01/60000 = 0,001/6000 = 0,0001/600 = 0,00001/60 = 0,000001/6
was ist 1/6 um 6 Kommastellen verschoben?
Gruß
Markus
Kann es sein, dass du bei der Auswahl des Controllers eine etwas
unglückliche Entscheidung getroffen hast? Plattformen mit 64-Bit
Fliesskommarechnung machen manche Dinge doch etwas einfacher. Weil man
nicht tagelang an Problem rumrühren muss, die man dann nicht hat.
Andreas K. schrieb:> Ich würde das ebenfalls so lösen. Wollte die Antwort gerade schreiben,> dann hab ich zum Glück nochmal aktualisiert :)
Also wie gesagt, es ist leider keine Konstante. Die Form in meiner
ursprünglichen Frag kann man ja so darstellen:
1
fq=1-(Fl/Fu)
Letztenendes ist die Frequenzänderung, welche im 0.01Hz Bereich
stattfindet für des spätere Ergebnis verantwortlich. Das ganze kann
sowohl im Startbereich von ca. 6MHz aber auch im Bereich bis zu 4Mhz
erfolgen.
Ein Term, der das (Fl/Fq) bei gleichem Ergebnis jedoch im 32Bit
Zahlenbereich ersetzt ginge natürlich auch. Sofern meine Mathekenntnisse
noch ausreichen geht das aber nicht.
A. K. schrieb:> Kann es sein, dass du bei der Auswahl des Controllers eine etwas> unglückliche Entscheidung getroffen hast? Plattformen mit 64-Bit> Fliesskommarechnung machen manche Dinge doch etwas einfacher. Weil man> nicht tagelang an Problem rumrühren muss, die man dann nicht hat.
Ja, definitiv :-( - Ich muss das aber jetzt auf dieser Hardware lösen.
Markus W. schrieb:> wie wäre es mit folgender Vorgehensweise?>> 1=6000000/6000000 (6MHz in Hz)>> 1- 5999999/6000000 = (6000000-5999999)/6000000 = 1/6000000 = 0,1/600000> = 0,01/60000 = 0,001/6000 = 0,0001/600 = 0,00001/60 = 0,000001/6
Hallo Markus,
Mathematisch stimme ich da zu (2 Stellen fehlen), aber wie gesagt ist
das ganze dynamisch und beide Werte ändern sich mit jedem Durchlauf in
den beiden Nachkommastellen.
chrisrabe schrieb:> Mathematisch stimme ich da zu (2 Stellen fehlen), aber wie gesagt ist> das ganze dynamisch und beide Werte ändern sich mit jedem Durchlauf in> den beiden Nachkommastellen.
Von welchem Frequenz- und Temperatur-Bereich reden wir denn da? Auch
wenn das dynamisch sein sollte, könnte man prüfen, ob man anhand der
Frequenz-Änderungen über Tabellen an die Temperaturänderung herankommt.
Die Tabellen könnte man dann supergenau auf dem PC erstellen und eine
Berechnung auf dem µC entfällt.
Das Argument "dynamisch" lasse ich erstmal nicht gelten. Man kann
nämlich für jede Start-Situation (f=x) eine eigene Tabelle anlegen.
Daher auch meine Frage, wie groß die Wertebereiche überhaupt sind.
Hallo chrisrabe,
Sofern der Nenner eine Anzahl von Nullen am Ende hat,
kann man die Division um die Anzahl der Nullen, wie
beschrieben, in die Vorkommastellen des Zählers transferieren
und damit die Rechnung vereinfachen.
Wobei der Aufwand kleiner ist, wenn die Differenz zwischen
Zähler und Nenner klein ist (ein-/zweistellig..)
Kann man den Nenner nicht so einfach reduzieren, wird es natürlich
etwas aufwändiger.
Gruß
Markus
Frank M. schrieb:> Von welchem Frequenz- und Temperatur-Bereich reden wir denn da?
Der Frequenzbereich liegt bei 6Mhz...4MHz.
Die Idee an sich ist nicht schlecht, jedoch gibt es hier eine
zusätzliche Unbekannte - Die Gasfüllung im Behälter - welche das
Verhältnis zwischen Temp. und Freq. beeinflusst.
Letztendlich soll genau diese Abhängigkeit die Du verwenden möchtest
hier gemessen werden um das Medium (im Beispiel das Gas) zu definieren.
Sozusagen wäre das Ergebnis meiner Messung Deine Tabelle.
Markus W. schrieb:> Hallo chrisrabe,>> Sofern der Nenner eine Anzahl von Nullen am Ende hat,> kann man die Division um die Anzahl der Nullen, wie> beschrieben, in die Vorkommastellen des Zählers transferieren> und damit die Rechnung vereinfachen.> Wobei der Aufwand kleiner ist, wenn die Differenz zwischen> Zähler und Nenner klein ist (ein-/zweistellig..)>> Kann man den Nenner nicht so einfach reduzieren, wird es natürlich> etwas aufwändiger.>> Gruß> Markus
ja, ist halt leider nicht immer x00000.., sondern wenn überhaupt, dann
nur in der ersten Sekunde.
Wegen der Assemblerdivision würde ich mich an Moby wenden. Das schüttelt
der nur so wie ein Blitz in 3 Zeilen Code aus dem Ärmel. Genauso wie IoT
komplett in Assembler. (Den Tcp/Ip Stack in Assembler hat er uns aber
noch nicht gezeigt)
Hallo Chris
>>>>>
alle Versuche waren bislang erfolglos, ebenfalls der Versuch die
avr_f64.c Bibliothek von Detlef_a dazu zu verwenden.
<<<<<<
freut mich, dass jemand das benutzt.
Geht, aber man muß aufpassen.
Untenstehender Code bringt das richtige Ergebnis für Dein Beispiel,
146.66667e-9 . Es ist notwendig, die Differenz fu-fh in int32_t zu
rechnen. Wenn man diese Differenz in float64_t rechnet kommt statt 88
die Differenz 64 raus, weil bei der Umwandlung der beiden integer32_t zu
float32_t die Genauigkeit flöten geht.
So looft dette aba.
Cheers
Detlef
float64_t Fu,Fh;
float32_t Ff;
int32_t fu=600000000;
int32_t fh=599999912;
Fh=f_sd((float32_t)(fu-fh));
Fu=f_sd((float32_t)fu);
Fh=f_div(Fh,Fu);
Ff=f_ds(Fh);
printf("%e",Ff);
return;
Ich kann mir jetzt nich vorstellen, dass man mittels Temperierung einen
Bereich von 4-6 MHz mit einem Quarz abdecken kann.
Aber wenn die Abweichungen im Vergleich zum Absolutwert sehr klein sind,
kann man (abhängig von der geforderten Genauigkeit) die vom Divisor
abhängige Hyperbel in diesem Bereich als linear annehmen.
1-599999999/600000000 = 1 - 0.99999999833 = 0.00000000167
1-599999910/599999911 = 1 - 0.99999999833 = 0.00000000167
Also gilt in diesem Bereich genähert:
1-A/B = 0.00000000167 * (B-A)
Also müsste man die Randbedingungen besser beschreiben um zu sehen, mit
welcher Lösung man am schnellsten zum Ziel kommt.
Jörg
Detlef_A schrieb:> Hallo Chris>>>>>>>> alle Versuche waren bislang erfolglos, ebenfalls der Versuch die> avr_f64.c Bibliothek von Detlef_a dazu zu verwenden.> <<<<<<>> freut mich, dass jemand das benutzt.>> Geht, aber man muß aufpassen.>> Untenstehender Code bringt das richtige Ergebnis für Dein Beispiel,> 146.66667e-9 . Es ist notwendig, die Differenz fu-fh in int32_t zu> rechnen. Wenn man diese Differenz in float64_t rechnet kommt statt 88> die Differenz 64 raus, weil bei der Umwandlung der beiden integer32_t zu> float32_t die Genauigkeit flöten geht.>> So looft dette aba.> Cheers> Detlef>> float64_t Fu,Fh;> float32_t Ff;>> int32_t fu=600000000;> int32_t fh=599999912;>> Fh=f_sd((float32_t)(fu-fh));> Fu=f_sd((float32_t)fu);> Fh=f_div(Fh,Fu);> Ff=f_ds(Fh);>> printf("%e",Ff);> return;
Hallo Detlef,
gut von Dir persönlich zu hören. :-)
Genau das war mein Problem, dass die Genauigkeit ja beim Umwandeln
flöten ging.
Meine Dateien haben folgendes Datum:
avr_f64.c 29.05.2015
avr_f64.h 20.12.2008
er weigert sich gerade die funktionen f_sd und f_ds zu finden, obgleich
sie in den Dateien vorhanden sind (undefined reference to f_sd)
Stimmt das was mit der Dateikombination nicht, oder kann ich
mittlerweile keine #includes mehr ausführen vor laute Bitsucherei...
Nachtrag:
Bei mir (aktuller GCC auf einem Intel irgendwas) geht das mit 4 Byte
float auch direkt:
printf("%e ",((float32_t)(fu)-(float32_t)(fh))/(float32_t)fu );
kommt richtig raus. Da brauchts' den 8 Byte Code garnicht. Weiss nicht,
warum die IEEE754 Libs für den ATMega das nicht richtig berechnen.
Cheers
Detlef
Detlef_A schrieb:> Hallo Chris,>> ich habe die Dateien aus meinem Ursprungspost> Beitrag "64 Bit float Emulator in C, IEEE754 compatibel"> vom 2.12.2007 genommen, die funzen.>> Cheers> Detlef
Hi Detlef,
That's the goal !!!! It works !!!
Just let me know where to send the 10kG box with icecream :-)
Cheers,
Christian
Vielen Dank an alle Mithelfer. Ich war auf dem Weg den Tip von Fritz
Katze zu testen als Detlef wieder ins Spiel kam. Da ich Detlefs Lösung
schon (wenn auch falsch) im code auskommentiert hatte habe ich diese nun
wieder aufgenommen und mit Seinem Beispiel sofort zum Laufen gebracht.
Danke nochmals für Eure Hilfe
Das Thema ist gelöst
chrisrabe schrieb:> Es handelt sich bei den Werten um Quarz Frequenzen. Der Quarz hat 6MHz
D.h.: Der Divisor ist zu Designzeit bekannt und konstant. Damit brauchst
du nicht mal wirklich eine Division, sondern nur eine hinreichend
"breite" Multiplikation, die i.A. sehr viel billiger ist, da oft in
Hardware unterstützt.
Du suchst dir einfach eine hinreichend genaue Festkommarepräsentation
des Kehrwehrts von 6.000.000 aus und multiplizierst deine Werte immer
nur mit dieser.
Beim Ergebnis rückst du das Komma um genau soviele binäre Stellen nach
links, wie die Festkommadarstellung des Divisors binäre Nachkommastellen
hat.
Wenn man das dann noch richtig durchdenkt, kann man bei der
Multiplikation dann auch noch viele Instruktionen einsparen, die nur
Sachen wegspeichern würden, die sowieso niemals mehr gebraucht werden.
>>>>>
ich da zu (2 Stellen fehlen), aber wie gesagt ist das ganze dynamisch
und beide Werte ändern sich mit jedem Durchlauf in den beiden
Nachkommastellen.
Mussma ganz lesen. Hilft manchmal.
Cheers
Detlef
Detlef _. schrieb:> Mussma ganz lesen. Hilft manchmal.
Schon recht.
Trotzdem fühlt man sich stark an C. F. Gauß erinnert:
„Der Mangel an mathematischer Bildung gibt sich durch
nichts so auffallend zu erkennen, wie durch maßlose
Schärfe im Zahlenrechnen.“
Joerg W. schrieb:> Aber wenn die Abweichungen im Vergleich zum Absolutwert> sehr klein sind, kann man (abhängig von der geforderten> Genauigkeit) die vom Divisor abhängige Hyperbel in diesem> Bereich als linear annehmen.>> 1-599999999/600000000 = 1 - 0.99999999833 = 0.00000000167> 1-599999910/599999911 = 1 - 0.99999999833 = 0.00000000167>> Also gilt in diesem Bereich genähert:>> 1-A/B = 0.00000000167 * (B-A)
Ja - aber man muss gar nicht so weit gehen und einen konstanten
Divisor annehmen.
Die oben von Markus vorgeschlagene Umformung
1 - A/B = B/B -A/B = (B-A) / B
ist ja allgemeingültig.
Die (ganzzahlige) Differenz (B-A) muss wegen der Stellen-
auslöschung mit voller Genauigkeit berechnet werden. Den
Divisor B kann man jedoch durch einen Näherungswert B*
ersetzen, da bei der Division nur der RELATIVE Fehler
des Divisors von Belang ist.
Wenn das Resultat z.B. auf fünf geltende Ziffern genau
sein soll, muss der Divisor lediglich mit mehr als fünf
Stellen (z.B. siebenstellig) behandelt werden - volle
neunstellige Genauigkeit ist nicht erforderlich!
Kleine Differenzen fast gleicher Zahlen sind ein Kunstfehler
der Numerik. Sowas formt man, wann immer möglich, in einen
Quotienten um.
Weiter als zu diesem Test bin ich damals nicht gekommen, der dividiert
in Assembler eine 64bit-Zahl (100Mio)² durch eine 32bit-Zahl (200Mio)
und gibt sie an ein Display als BCD-Ziffern aus. Dazu habe ich Biruas
32/32-Bit-Routine auf 64/32 erweitert. Die Umformung Bin-BCD braucht
mehr Platz als die Division.
chrisrabe schrieb:> oder im code aktuell umgestellt zu:> float fq = (((float)Fu) - ((float)Fl)) / ((float)(Fu));
Ohh. Das sehe ich erst jetzt...
Erster Fehler: Es muss natürlich zuerst die ganzzahlige
Differenz "Fu-Fl" mit voller Stellenzahl berechnet werden,
und dann nach Gleitkomma konvertiert werden.
> Da Ergebnis und Operanden Float sind, werden bei der> Berechnung nicht ausreichend signifikante Stellen verwendet.> Dadurch wird das Ergebnis ungenau. Es wird statt 599999912> z.B. nur mit 59999991 gerechnet wegen der 8 signifikanten> Stellen bei float.
Wenn das so korrekt wiedergegeben ist, dann liegt natürlich
ein weiterer grober Fehler, und zwar bei der Konvertierung,
vor. 59.999991 Millionen sind etwas deutlich anderes als
599.999912 Millionen!
Richtig wären 599.99991 Millionen!
Warum soll der uC immer wieder die gleichen 3 Werte ausrechnen?
Formeln:
für fq eingesetzt:
Was ist der Input in die Formel? --> x und Fl
In welchen Grenzen bewegen sich diese Inputs?
Wenn es ausreichend enge Grenzen sind, könnte man auch überlegen, eine
2-dimensionale Lookup-Table anzulegen, nach dem Motto:
Ergebnis = array[ x - offset_x ][ Fl - offset_fl ];
Das Array wird auf einem PC einmalig berechnet und im uC wird lediglich
noch die vorgekaute Tabelle ausgelesen.
... is dann zur Laufzeit recht fix.
Gruß,
Kay
>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Trotzdem fühlt man sich stark an C. F. Gauß erinnert:
„Der Mangel an mathematischer Bildung gibt sich durch
nichts so auffallend zu erkennen, wie durch maßlose
Schärfe im Zahlenrechnen.“
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
Schon recht, aber in diesem thread gehts ja nicht um Mathematik sondern
um 'Schärfe im Zahlenrechnen'.
Cheers
Detlef
Detlef_A schrieb:> Schon recht, aber in diesem thread gehts ja nicht um> Mathematik sondern um 'Schärfe im Zahlenrechnen'.
Mit Verlaub - aber das sehe ich anders.
Es ging primär darum, den Term (1 - A/B) numerisch
korrekt auszuwerten. Zahlreiche Beiträge - Deine eigenen
eingeschlossen - zeigen auf, dass dies mit 32bit-Floats
möglich ist, wenn man die günstigere Darstellung (B-A)/B
verwendet.
Und wie wurde das Problem nun tatsächlich gelöst?
Die Rechnung wurde auf 64bit umgestrickt; wo der Fehler
in der 32bit-Arithmetik lag, bleibt im Dunkeln.
Ich persönlich finde das äußerst unbefriedigend.