www.mikrocontroller.net

Forum: Codesammlung 64 Bit float Emulator in C, IEEE754 compatibel

Autor: Detlef _a (detlef_a)
Datum: 02.12.2007 02:37
Dateianhang: avr_float64.zip (4,9 KB, 164 Downloads)

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: 09.04.2008 20:12
Dateianhang: IEEE754_double.zip (1 MB, 74 Downloads)

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: 09.04.2008 20:19

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: 13.04.2008 18:37
Dateianhang: IEEE754_double.zip (1 MB, 50 Downloads)

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

Antwort schreiben

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

Wichtige Regeln - erst lesen, dann posten!

  • Suchfunktion und Betreffsuche benutzen - vielleicht gibt es schon einen ähnlichen Beitrag
  • Aussagekräftigen Betreff wählen
  • Im Betreff angeben um welchen Controllertyp es geht (AVR, PIC, ...)
  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang
  • JPEG-Dateien (.jpg) nur für Fotos und Scans verwenden
  • Schaltpläne, Screenshots usw. als PNG oder GIF anhängen

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [pre]vorformatierter Text (z.B. Code in anderen Sprachen)[/pre]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel





Hinweis: der Originalbeitrag ist mehr als 6 Monate alt.

webmaster@mikrocontroller.netImpressumWerbung auf Mikrocontroller.net