Festkomma-Arithmetik



  • Hi
    Ich habe in meinem Programm eine Formel(Cosinussatz) im Einsatz und möchte das Ergebnis möglichst genau berechnen.
    (b*b+c*c-a*a)*32768/(2*b*c)
    Wenn ich das einfach so übernehme, bekomme ich einen Überlauf (800 < a,b,c < 1200). Und nun würde ich gerne wissen ob es eine geschickte Methode gibt das zu Berechnen (ohne Gleitkomma).

    LG



  • Fragesteller01 schrieb:

    Hi
    Ich habe in meinem Programm eine Formel(Cosinussatz) im Einsatz und möchte das Ergebnis möglichst genau berechnen.
    (b*b+c*c-a*a)*32768/(2*b*c)
    Wenn ich das einfach so übernehme, bekomme ich einen Überlauf (800 < a,b,c < 1200). Und nun würde ich gerne wissen ob es eine geschickte Methode gibt das zu Berechnen (ohne Gleitkomma).

    LG

    Eigentlich ist das ganz OK. Die 32768 mit der 2 zu kürzen, wäre jetzt kindisch, das löst das Problem nicht wirklich.
    Dein Lieblingsprozessor erlaubt sicherlich eine nette Funktion i32 MULDIV(i32 a,i32 b,i32 c) zu bauen, das a*b/c berechnet, aber ein 64-bitteiges Zwischenergebnis benutzt.


  • Mod

    Wenn ich annehme, dass der Überlauf bei 2 oder 4 Milliarden ist, dann geht dies hier schonmal besser:
    (b*b+c*c-a*a)/(2*b*c)*32768
    Ist dafür natürlich etwas ungenauer, dafür aber ohne Überlauf. Ich weiß, das ist eine ziemlich billige Lösung, aber was besseres fällt mir gerade nicht ein.





  • Fragesteller01 schrieb:

    (b*b+c*c-a*a)*32768/(2*b*c)
    (800 < a,b,c < 1200)

    Also schlimmstenfalls (1199*1199+1199*1199-801*801)*32768/(...)
    =3516803*32761/(...)
    4294967296/3516803=1221 (abrunden auf 1024)

    (b*b+c*c-a*a)*1024/(2*b*c)*32
    sollte Dich gerade noch unterhalb 2^32 halten.

    Also
    (b*b+c*c-a*a)*1024/(b*c)*16

    Offensichlich hat das Ergebnis die vier untersten Bits auf 0.

    Aber schau trotzdem deinen Prozessor genauer an.



  • Danke für die Antworten, aber vielleicht muss ich das ganze noch ein bisschen genauer beschreiben.
    Ich habe einen 8-Bit µC (AVR), daher helfen mir Win-API oder fertige Linux Funktionen wenig. Leider bin ich auch in Asm nicht so fit, dass selbst implementieren zu können -> daher auch C.
    mein a und c sind beide in etwa gleich (~1000).
    Mein b wird allerdings ebenfalls über den Kosinussatz bestimmt ( b= mysqrt(a*a+c*c-(2*a*c*mycos(gamma)/32768));//das "/32768" kommt daher, dass das Ergebnis von mycos() = cos()*32768 ).
    Vielleicht steh ich im Moment einfach aufm Schlauch, aber kann man das nicht in etwa so eine Form bringen : z=x/y+w ??

    LG



  • Wieviele Bit haben Deine Zahlen a, b und c? Sind sie Vorzeichenlos?
    Was der Prozessor kann, scheint da http://www.atmel.com/dyn/resources/prod_documents/DOC0856.PDF zu stehen.



  • a,b,c sind 16Bit signed Variablen.
    Danke für den Link, aber Assembler-Code möchte ich nicht verwenden, da die Funktion bis zu einem gewissen Grad portabel sein sollte (also reines C).
    Außerdem habe ich beim schnellen überfliegen auch keinen Befehl gefunden der das in einem machen würde 🙄
    LG



  • Fragesteller01 schrieb:

    a,b,c sind 16Bit signed Variablen.

    b= mysqrt(a*a+c*c-(2*a*c*mycos(gamma)/32768))

    a*a könnte bis zu 1200^2=1440000 sein. Darf aber nur 32767 werden. Also um Faktor 39 runter müssen wir. Nehmen wir 64. Statt a*a/64 kann ich auch a/8*a/8 sagen.

    Wäre es jetzt möglich, alle Zahlen um den Faktor 8 runterzuskalieren?

    b/8= mysqrt((a/8)(a/8)+(c/8)(c/8)-(2*(a/8)*(c/8)*mycos(gamma)/32768))

    b= mysqrt((a/8)(a/8)+(c/8)(c/8)-(2*(a/8)*(c/8)*mycos(gamma)/32768))*8

    Nur so mal vermutet.

    Nein, mycos(gamma) ist ja noch viel zu groß. Es wäre toll, 32-bittige Zwischenergebnisse zu haben. Schade.



  • Es gibt durchaus int32_t und int64_t (und natürlich die zugehörigen unsigned), aber ich hoffte auf eine schnelle Lösung ohne Überlauf mittels umformen zu kommen, da die Registerzahl ja weit nicht so groß ist wie auf einem "normalen" Prozessor (wenn ich micht nicht täusche habe ich 32 8Bit-Register). Und da sind 64 und 32Bit Variablen nicht zu vernachlässigen.

    LG



  • Wenn ich jetzt z.B. für a=900 setze und die 2 Varianten berechne (a/8*a/8 bzw a²/64) bekomme ich Ergebnisse die relativ weit voneinander entfernt liegen (12544 und 12656). Mein Ziel ist es mehr oder weniger nur die Nachkommastelle zu verlieren.
    Das wird aber vermutlich nur gehen wenn ich vorher die Produkte berechne und erst dannach dividiere 😞
    Ich werd das ganze dann wohl oder übel mit 64Bit Variablen rechnen müssen.
    Danke für die Hilfe
    LG


Anmelden zum Antworten