www.mikrocontroller.net

Forum: Projekte & Code 64 Bit float Emulator in C, IEEE754 compatibel


Announcement: there is an English version of this forum on EmbDev.net. Posts you create there will be displayed on Mikrocontroller.net and EmbDev.net.
Autor: Detlef _a (detlef_a)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hi,
anbei findet Ihr einige Routinen, die das Rechnen mit 64 Bit float 
Zahlen auch auf den AVRs erlauben. Der Emulator ist in K&R C geschrieben 
und angepaßt und getestet für den Einsatz mit gcc. Er erlaubt die 
Rechnung mit ca. 15 gültigen Stellen anstelle der 6 Stellen für den 
eingebauten 32Bit float Typ. Das Zahlenformat entspricht IEEE754.

Das Headerfile erzeugt einen neuen Typ float64_t , der in einem uint64_t 
untergebracht wird. 64Bit floats können von den vorhandenen 
standardisierten 32Bit floats konvertiert werden:

(float64_t) x = f_sd((float32_t)a); (sd: single to double).
Umgekehrt gehts so:
(float32_t) x = f_ds((float64_t)a);

Es sind die vier Grundrechenarten und die Kehrwertberechnung 
implementiert.
(float64_t) x = f_add    ((float64_t) a,(float64_t) b);
(float64_t) x = f_sub    ((float64_t) a,(float64_t) b);
(float64_t) x = f_mul    ((float64_t) a,(float64_t) b);
(float64_t) x = f_div    ((float64_t) a,(float64_t) b);
(float64_t) x = f_inverse((float64_t) a);

Die Leistung für die Division liegt bei ca. 200 flops auf nem Mega 128 
@16Mhz, Kehrwertbildung ist ähnlich lahm, der Rest geht schneller.

Die Behandlung von Sonderzahlen (NaN, +/-Infinity, etc.) und 
Over/Underflows ist nicht normgerecht implementiert. 1/0 liefert 
allerdings Infinity und die Rundung zu 0 für betragsmäßig zu kleine 
Ergebnisse ist drin.

Das File avr_f64.c enthält den Rechencode. Im main.c findet sich ein 
framework für den Test.

Ich habe diese Routinen erstellt, weil ich für meine Belange mit dem 32 
Bit Floatformat nicht genügend Genauigkeit erreichen konnte. Ich habe 
relativ viel Aufwand in die Tests gesteckt, wie auch im main.c zu sehen 
ist. Trotzdem kann ich natürlich Fehler nicht ausschließen.

Ich würde mich über rege Benutzung und Rückmeldung freuen.

Cheers
Detlef

Autor: Florian K. (makrocontroller)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Gute Arbeit und kompakter Code !

Ich habe deinen Code zum Anlass genommen, die Bibliothek um die
Funktionen sqrt, exp, log, sin, cos, tan, arcsin, arccos, arctan sowie
um die Konvertierung vom und ins Dezimalsystem zu erweitern. Ich
verwende dafür bestimmte Hilfsfunktionen, welche sich auch dazu eignen,
die den Code für 4 Grundrechenarten kompakter zu schreiben. Daher habe
ich +, -, *, / auch entsprechend abgeändert. Andernfalls würden
Code-Teile, die Ähnliches tun, nebeneinander existieren, was man sich
auf einem µC nicht leisten sollte.

Meine Performance-Messungen auf einem ATMega32 (16 MHz) ergaben ca. 1000
Multiplikationen und 400 Divisionen pro Sekunde. Die Performance von
exp, log, sin, cos, tan, arcsin, arccos, arctan hängt auch vom
übergebenen Argument ab und lag zwischen 50 und 200
Funktionsauswertungen pro Sekunde.

Der Code ist ebenfalls ANSI-C-kompatibel. Bei den math. Fkt. wird in den
meisten Fällen die float64-Zahl geliefert, welche sich durch Rundung des
exakten Ergebnisses ergibt. Manchmal wird in die falsche Richtung
gerundet. Nur in speziellen Fällen kann es vorkommen, dass deutlich
weniger als alle 52 Mantissebits signifikant sind: Wenn x nicht nahe bei
Null, aber sehr nahe einer Nullstelle von sin, cos oder tan liegt, ist
der absolute Fehler zwar in der Größenordnung 2E-16; weil der
Funktionswert aber nahe bei Null liegt, ist der relative Fehler (= abs.
Fehler / Fkt.-Wert) deutlich größer als 2E-16. Größere Fehler treten bei
sin, cos, tan auch auf, wenn x betragsmäßig sehr groß ist, z.B. 1E10.
Für die Praxis dürfte sich der Aufwand nicht lohnen, diese Fehler zu
verkleinern.

Ich habe den Code v.a. nach Compilierung unter Visual C++ und nur
stichprobenartig auf dem eigentlichen Zielsystem (ATMega32) getestet.
Die Funktionen sqrt, exp, log, sin, cos, tan, arcsin, arccos, arctan
habe ich zum einen mit ausgewählten Spezial-Zahlen und zum anderen
jeweils mit 1E9 Zufallszahlen getestet, bei denen sowohl Mantisse als
auch Exponent zufällig waren. Das Ergebnis habe ich mit dem Ergebnis
verglichen, das die double-Arithmetik unter VC++ liefert. Mögliche
Differenzen wurden toleriert, wenn sich jeweils folgende beiden
Intervalle überlappen: Das erste Intervall ergibt sich, indem der von
meinem Code gelieferte Funktionswert einmal auf den nächstkleineren
darstellbaren float64-Wert (untere Intervallgrenze) und zum anderen auf
den nächstgrößeren (obere Grenze) abgeändert wird. Das zweite Intervall
ergibt sich, indem die entsprechende Funktion der PC-math-Bib. mit dem
nächstkleineren Funktionsargument und einmal mit dem nächstgrößeren
Argument evaluiert wird. Bei den 1E9 zufälligen Funktionsargumenten
waren alle Ergebnisse meines Codes mit diesen Kriterien tolerierbar. Das
beweist jedoch nicht, dass es keinen Bug gibt. Bugs können gerade auch
in Spezialfällen auftreten, die auch durch viele Tests mit Zufallszahlen
nicht auftreten.

Als Demo-Applikation benutze ich den mit einer RC5-Fernbedienung
steuerbaren Taschenrechner, den ich auch schon für die
Gleitkomma-Bibliothek Gleitkomma-Bibliothek für AVR verwendet habe.
Man kann alle Funktionen der float64-Bibliothek mit #defines in der
Datei avr_f64.h in die Kompilierung einbeziehen bzw. sie ausschließen.
Wenn nur +, -, *, / sowie die Konvertierung vom und ins Dezimalsystem
verwendet werden, benötigt die Taschenrechner-Applikation ca. 20 kB. Ich
habe auf meinem ATMega32 noch exp und log aktiviert, was auf 28 kB
führt. Mit allen Funktionen werden ca. 39 kB benötigt -- nichts für
einen ATMega32.

Außer der float64-Bibliothek verwende ich in der
Taschenrechner-Anwendung den RC5-Code von Peter Dannegger, Code für LCDs
von Peter Fleury und USART-Code von Ulrich Radig.

Autor: Florian K. (makrocontroller)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe in der Funktion f_to_string() einen Bug gefunden und 
korrigiert.
Ich habe den alten Download gelöscht und den neuen Download eingestellt.

Autor: Florian K. (makrocontroller)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe festgestellt, dass die Benutzung der vom avr-gcc Compiler zur
Verfügung gestellten 64-Bit-Integer Division ca. 4 kB kostet. Daher habe
ich die float64-Bib. so geändert, dass, falls
   #define F_PREFER_SMALL_BUT_SLOWER_CODE
oder
   #define F_PREFER_SMALLEST_BUT_SLOWEST_CODE
vorhanden ist, eine eigene primitive Division verwendet wird, mit der
man bis zu 4500 Bytes sparen kann. Dann ist die Performance der Division
allerdings nur noch ca. 100 pro Sekunde bei
F_PREFER_SMALLEST_BUT_SLOWEST_CODE oder 250 pro Sekunde bei
F_PREFER_SMALL_BUT_SLOWER_CODE. Auch die math. Fkt. (exp, log, ...) sind
dann langsamer.

Außerdem ergibt in der neuen Version f_sqrt aus einer negativen Zahl 
korrekterweise NaN (komplexe Zahlen gibt es hier nicht).

Autor: Stefan P. (form)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Großes Lob an euch beide!
Sehr gute Arbeit.

Autor: Peter Dannegger (peda)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Florian K. wrote:
> man bis zu 4500 Bytes sparen kann. Dann ist die Performance der Division
> allerdings nur noch ca. 100 pro Sekunde

Schön wäre es, wenn man dann noch dem AVR-GCC die long long 
Grundrechenarten in Assembler unterjubeln könnte.
Das dürfte neben weiterer Codeeinsparung auch das Tempo massiv 
beschleunigen.


Peter

Autor: Johann L. (gjlayde) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Peter Dannegger wrote:
> Schön wäre es, wenn man dann noch dem AVR-GCC die long long
> Grundrechenarten in Assembler unterjubeln könnte.
>
> Peter

hmmm, für + und - sieht das übel aus, weil die nicht beschrieben sind im 
avr-Backend, d.h. gcc expandiert den adddi3 in QImode (char). Das Carry 
wird dabei in C ausgetextet...

für * und / wird gegen die libgcc2 gelinkt, da könnte man sich evtl. 
reinhängen? ZB in __muldi3 aus _muldi3.o?

Autor: Stefan P. (form)
Datum:
Angehängte Dateien:
  • patch (947 Bytes, 464 Downloads)

Bewertung
0 lesenswert
nicht lesenswert
Ich habe ein paar Kleinigkeiten geändert, damit der Compiler mit 0 
Warnings durchläuft. Das meiste waren "differ in signedness" Warnungen, 
und eine Funktion die definiert, aber unter Umständen nicht genutzt 
wurde.

Getestet ist das ganze aber erst mit:

f_compare
f_abs
f_atof
f_add
f_mult
f_div
f_atof
f_to_string

Vielleicht kann Florian ja mal einen Blick drauf werfen :)

Autor: Florian K. (makrocontroller)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe die Funktion approx_inverse_of_fixpoint_uint64() noch etwas
optimiert. Sie ist nun bei ca. 3000 Bytes weniger Speicherbedarf so
schnell wie die originale Funktion, wenn F_PREFER_SMALL_BUT_SLOWER_CODE
und F_PREFER_SMALLEST_BUT_SLOWEST_CODE nicht definiert sind. In der
neuen
Version kann nur noch das #define F_PREFER_SMALL_BUT_SLOWER_CODE und
nicht F_PREFER_SMALL_BUT_SLOWER_CODE angegeben werden. Durch #define
F_PREFER_SMALL_BUT_SLOWER_CODE werden weitere 800 Bytes gespart,
allerdings ist dann die Rechenzeit deutlich länger.
Ich habe wieder mehr nach Compilierung unter Visual C++ und weniger
unter einem ATMega32 getestet.
Außerdem habe ich die Änderungen von Stefan P. berücksichtigt.

Autor: Florian K. (makrocontroller)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Ich habe einen bug gefunden. Nach ANSI C/C++ darf man mit << oder >> nur 
um eine Bitzahl schieben, die echt kleiner als die Bitlänge des 
Operanden ist, ansonsten ist das Ergebnis undefiniert (tatsächlich 
bekommt man dann z.B. nach Kompilierung unter Visual C++ andere 
Ergebnisse als unter avr-gcc und oftmals nicht das gewünschte Ergebnis). 
In zwei Fällen habe ich eine entsprechende Abfrage vergessen (f_exp(), 
f_mod_intern()). Der Fehler tritt z.B. bei f_exp(f_from_double(1E-80)) 
auf, also eher in Spezialfällen. Ich habe die geänderte Datei avr_f64.c 
angefügt.

Autor: Uwe S. (de0508)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo,

ich habe heute die neue 64-Bit Lib von PeDa mit dem Projekt übersetzt 
und das spart einige Bytes.

Alt
20584 bytes

mit der neuen 64-Bit Lib
20118 bytes

http://www.avrfreaks.net/index.php?name=PNphpBB2&f...

Einfach die Datei  dannis64bit.S kopieren und in das Makefile eintragen 
- fertig !

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel




Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.