Forum: Mikrocontroller und Digitale Elektronik 32Bit Division mit inline Assembler


von chrisrabe (Gast)


Lesenswert?

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
float fq = (((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.
1
asm volatile (
2
3
";***********************************************"
4
";* unsigned rounded division  -  32 bit"
5
";*"
6
";***********************************************"
7
8
"urdiv32:"  "\n\t"
9
10
"mov  r24, r20    ;T = B"  "\n\t"
11
"mov  r25, r21"  "\n\t"
12
"mov  r26, r22"  "\n\t"
13
"mov  r27, r23"  "\n\t"
14
"lsr  r27      ;B / 2"  "\n\t"
15
16
"ror  r26"    "\n\t"
17
"ror  r25"    "\n\t"
18
"ror  r24"    "\n\t"
19
"add  r16, r24    ; A = A + B / 2"  "\n\t"
20
"adc  r17, r25"  "\n\t"
21
"adc  r18, r26"  "\n\t"
22
"adc  r19, r27"  "\n\t"
23
24
":"
25
"; output-operand-list"
26
27
":"
28
"; input-operand-list"
29
30
":"
31
"; clobber-list"
32
33
34
35
";***********************************************"
36
";* unsigned division  -  32 bit"
37
";*"
38
";***********************************************"
39
"; a3..0 = a3..0 / b3..0 (Ganzzahldivision)"
40
"; b3..0 = a3..0 % b3..0 (Rest)"
41
"; cycle: max 684"
42
43
"udiv32:"        "\n\t"
44
45
"clr    r24"    "\n\t"
46
"clr    r25"    "\n\t"
47
"clr    r26"    "\n\t"
48
"clr    r27"    "\n\t"
49
"ldi    r28, 32"  "\n\t"
50
51
"udi1:"          "\n\t"
52
53
"lsl    r16"    "\n\t"
54
"rol    r17"    "\n\t"
55
"rol    r18"    "\n\t"
56
"rol    r19"    "\n\t"
57
"rol    r24"    "\n\t"
58
"rol    r25"    "\n\t"
59
"rol    r26"    "\n\t"
60
"rol    r27"    "\n\t"
61
"cp    r24, r20"    "\n\t"
62
63
"cpc    r25, r21"  "\n\t"
64
"cpc    r26, r22"  "\n\t"
65
"cpc    r27, r23"  "\n\t"
66
"brcs    udi2"    "\n\t"
67
"sub    r24, r20"  "\n\t"
68
"sbc    r25, r21"  "\n\t"
69
"sbc    r26, r22"  "\n\t"
70
"sbc    r27, r23"  "\n\t"
71
"inc    r16"    "\n\t"
72
73
"udi2:"          "\n\t"
74
"dec  r28"      "\n\t"
75
"brne  udi1"      "\n\t"
76
"mov  r20, r24"    "\n\t"
77
"mov  r21, r25"    "\n\t"
78
"mov  r22, r26"    "\n\t"
79
"mov  r23, r27"    "\n\t"
80
"ret");
Könnte mir hier bitte jemand die Übergabe der Parameter eintragen, so 
wie sie funktionieren sollte? Ich komme einfach nicht weiter.


Viele Grüße
C

von (prx) A. K. (prx)


Lesenswert?

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.

: Bearbeitet durch User
von Fritz Katze (Gast)


Lesenswert?

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.

von Pandur S. (jetztnicht)


Lesenswert?

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

von chrisrabe (Gast)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

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.

von Pandur S. (jetztnicht)


Lesenswert?

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.

von Markus (Gast)


Lesenswert?

> 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?

von chrisrabe (Gast)


Lesenswert?

@ 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?

von chrisrabe (Gast)


Lesenswert?

// 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.

von Karl H. (kbuchegg)


Lesenswert?

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.

von chrisrabe (Gast)


Lesenswert?

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

von Karl H. (kbuchegg)


Lesenswert?

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?

: Bearbeitet durch User
von chrisrabe (Gast)


Lesenswert?

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.

von chrisrabe (Gast)


Lesenswert?

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.

von Christoph db1uq K. (christoph_kessler)


Lesenswert?

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.

von Joerg W. (joergwolfram)


Lesenswert?

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

von Markus (Gast)


Lesenswert?

Du kanns dir mit der Inkludedatei

#include <stdfix.h>

Festkommazahlenunterstützung holen.

long long accum F1;

von chrisrabe (Gast)


Lesenswert?

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:

von Andreas B. (buyman)


Lesenswert?

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.

von Markus W. (dl8mby)


Lesenswert?

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

von (prx) A. K. (prx)


Lesenswert?

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.

von chrisrabe (Gast)


Lesenswert?

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.

von chrisrabe (Gast)


Lesenswert?

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.

von chrisrabe (Gast)


Lesenswert?

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.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von Markus W. (dl8mby)


Lesenswert?

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

von chrisrabe (Gast)


Lesenswert?

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.

von chrisrabe (Gast)


Lesenswert?

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.

von ich (Gast)


Lesenswert?

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)

von (prx) A. K. (prx)


Lesenswert?

Nicht ich schrieb:
> (Den Tcp/Ip Stack in Assembler hat er uns aber noch nicht gezeigt)

Logisch. Da sind 32-Bit Datentypen drin. ;-)

von Detlef_A (Gast)


Lesenswert?

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;

von Joerg W. (joergwolfram)


Lesenswert?

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

von chrisrabe (Gast)


Lesenswert?

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...

von Detlef_A (Gast)


Lesenswert?

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

von Detlef_A (Gast)


Lesenswert?

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

von chrisrabe (Gast)


Lesenswert?

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

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

chrisrabe schrieb:
> 1-599999997/600000000 =  ...
Wegen
kannst du den arctan für Werte in dem von dir angedeuteten Wertebereich 
einfach erhalten per

Beispiel:

von c-hater (Gast)


Lesenswert?

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.

von Detlef _. (detlef_a)


Lesenswert?

>>>>>
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

von Possetitjel (Gast)


Lesenswert?

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.“

von Possetitjel (Gast)


Lesenswert?

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.

von Christoph db1uq K. (christoph_kessler)


Angehängte Dateien:

Lesenswert?

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.

von Possetitjel (Gast)


Lesenswert?

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!

von Kay I. (imperator)


Lesenswert?

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

von Detlef_A (Gast)


Lesenswert?

>>>>>>>>>>>>>>>>>>>>>>>>>>>>
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

von Possetitjel (Gast)


Lesenswert?

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.

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
Noch kein Account? Hier anmelden.