Forum: Compiler & IDEs Betragsberechnung eines Vectors in C, am16


von Micha S. (e-tec)


Lesenswert?

Betragsberechnung eines Vectors in C
====================================

Guten Abend!!

Hab mal eine kleine Frage.
Ich möchte in C den Betrag eines 3D Vectors berechnen, über phythagoras.
Betrag(bzw Länge) x = Wurzel aus der Summe der Quadrate.

Nun muss man ja gezwungenermassen mit potenzen rechnen, bzw mit 
funktionen die diese berechnen. (sqrt/pow)

Kann man das nicht irgendwie umgehen? normalerweise sagt man ja X^2 = 
x*x oder pow(x, 2) oder sqr(x)

ich würde gerne auf die bibliotheksfunktionen komplett verzichten.
das quadrat am liebsten so x = x^2; (ich weiß der "^" ist eigentlich der 
Xor op) und ebenso die quadratwurzel mit x = x^(1/2)  (werde ich vorher 
noch mit 1000 multiplizieren, da ich die berechnung in int durchführen 
möchte.)

momentan sieht die berechnung so aus:
1
betrag  = ( (adc.raw_data[0]-X_OFFSET) * (adc.raw_data[0]-X_OFFSET) );
2
betrag += ( (adc.raw_data[1]-Y_OFFSET) * (adc.raw_data[1]-Y_OFFSET) );
3
betrag += ( (adc.raw_data[2]-Z_OFFSET) * (adc.raw_data[2]-Z_OFFSET) );
4
betrag  = (ui16) sqrt(betrag);

kann ich die sqrt() funktion irgendwie umgehen?
Grund ist folgender (mit sqrt):

AVR Memory Usage
    ----------------
    Device: atmega16
    Program:    6878 bytes (42.0% Full)
    (.text + .data + .bootloader)
    Data:        676 bytes (66.0% Full)

und ohne, der grund liegt klar auf der hand...

AVR Memory Usage
    ----------------
    Device: atmega16
    Program:    3776 bytes (23.0% Full)
    (.text + .data + .bootloader)
    Data:        412 bytes (40.2% Full)

von Jannis C. (kabelwurm)


Lesenswert?

Doppelt

von Micha S. (e-tec)


Lesenswert?

schon gesehen & gelöscht ;)

von hp-freund (Gast)


Lesenswert?


von Micha S. (e-tec)


Lesenswert?

Schön :) gibts das auch in c oder muss ihc unbedingt den asm code 
einbinden?

von DirkB (Gast)


Lesenswert?

Micha S. schrieb:
> kann ich die sqrt() funktion irgendwie umgehen?

Was machst du denn mit dem Betzrag.
Wenn du die nur vergleichen willst, brauchst du die Wurzel nicht ziehen.

von Micha S. (e-tec)


Lesenswert?

schon klar :) naja eig. würde es mit ein paar tricks auch so gehen, 
danach den wert skalieren und gut. ich werte einen beschleunigungssensor 
aus, der betrag ist die resultierende kraft (im stand die erdanz.)

von hp-freund (Gast)


Lesenswert?

Micha S. schrieb:
> Schön :) gibts das auch in c oder muss ihc unbedingt den asm code
> einbinden?

So schwer ist das nicht. Habe es gerade mal probiert:

http://www.mikrocontroller.net/articles/AVR-GCC-Tutorial/Assembler_und_Inline-Assembler#Assembler-Dateien

Lässt sich compilieren, ob das Ergebnis stimmt weiß ich allerdings nicht 
:-)

von Micha S. (e-tec)


Lesenswert?

genau, und um zu wissen ob es stimmt müsste ich den befehlssatz von 
meinem kleinen lernen (zu faul...:)
aber ichg werde es mal bei gelegenheit einbinden und testen (wobei mir 
da schon mal beim durchschauen push & pop fehlen)

von Ralf (Gast)


Lesenswert?

Hallo,

habe mal den folgenden Code von Jack Crenshaw (Embedded Math Toolkit) 
verwendet:
1
uint32 Sqrt(uint32 ulRadicand)
2
{
3
   uint32 ulRem  = 0;
4
   uint32 ulRoot = 0;
5
   int32  slIdx;
6
   for (slIdx = 0; slIdx < 16; slIdx++)
7
   {
8
      ulRoot     <<= 1;
9
      ulRem        = ((ulRem << 2) + (ulRadicand >> 30));
10
      ulRadicand <<= 2;
11
      ulRoot++;
12
      if (ulRoot <= ulRem)
13
      {
14
         ulRem -= ulRoot;
15
         ulRoot++;
16
      }
17
      else
18
      {
19
         ulRoot--;
20
      }
21
   }
22
   return (ulRoot >> 1);
23
}

Gruß,
Ralf

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

32-Bit Quadratwurzel für avr-gcc gibt's in AVR Arithmetik: Wurzel.

von Micha S. (e-tec)


Lesenswert?

Sehr schön, danke. hab mir sowas ähnliches gestern bereits selber 
geschrieben:
1
ui16 int_sqrt(ui32 zahl)
2
{
3
   ui16 wurzel = UI16MAX ;
4
   ui16 temp   = wurzel-1;
5
   while ( temp < wurzel ) 
6
   {
7
      wurzel = temp;
8
      temp   = wurzel + zahl/wurzel >> 1;
9
   }
10
   return wurzel;
11
}

funktioniert sehr gut, aber wie man auf den ersten blick sieht werden 
die nachkommastellen nicht berücksichtigt, die funktion eignet sich 
somit mehr für größere zahlen. lt. der funktion ist:
wurzel 4 = wurzel aus 5...8, nämlich 2.

man könnte sie, wenn nötig natürlich noch mit dem modulo erweitern...

von Karl H. (kbuchegg)


Lesenswert?

Ralf's Version dürfte grob geschätzt um mindstens den Faktor 20 bis 30 
schneller sein als deine Newton Iteration.

von Arthur Dent (Gast)


Lesenswert?

Um die Auflösung zu erhöhen eignet sich die Fixed-Point-Arithmetic, 
dabei verschiebst du quasi das Komma um ein bestimmtes Vielfaches von 
zwei und erhälts so Nachkommastellen. Ein Vielfaches von 2 deshalb, weil 
es sich mit Bit-Shift-Operationen realisieren lässt.

Viel Erfolg!

von Ralf G. (ralg)


Lesenswert?

DirkB schrieb:
> Micha S. schrieb:
>> kann ich die sqrt() funktion irgendwie umgehen?
>
> Was machst du denn mit dem Betzrag.
> Wenn du die nur vergleichen willst, brauchst du die Wurzel nicht ziehen.

Ich würde auch lieber diese Variante nehmen.

von Micha S. (e-tec)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Ralf's Version dürfte grob geschätzt um mindstens den Faktor 20 bis 30
> schneller sein als deine Newton Iteration.

Guten morgen, na heute besser gelaunt? ;-)
Ja das ist klar, das ist abhängig von der zahl, je größer desto mehr 
durchläufe & somit langsamer. Mir ging es ja, wie gestern erwähnt, 
hauptsächlich darum die fliesskommaarithmetik zu umgehen, was mir auch 
erst mal gelungen ist. nächstes kriterium war dann der Speicherbedarf, 
deswegen die entscheidung zur Newtonschen Iteration.

von Karl H. (kbuchegg)


Lesenswert?

Micha S. schrieb:

> erst mal gelungen ist. nächstes kriterium war dann der Speicherbedarf,
> deswegen die entscheidung zur Newtonschen Iteration.

Gut.
Das ist jetzt in Ralfs Code noch nicht optimal gelöst. sIdx muss keine 
32 Bit Variable sein und ob die anderen beiden 32 BIt sein müssen, 
müsste man analysieren. Auch der Return Datentyp muss kein 32 Bit Typ 
sein, 16 Bit reichen. Aber abgesehen von den paar Bytes auf dem Stack 
trägt sein Code auch höchst wahrscheinlich weniger auf, als die Division 
in deinem Code, die das eigentliche Übel darstellt. AVR können nur 
schlecht allgemein dividieren. Von daher ist so gut wie jeder Code, der 
keine derartige Division braucht immer besser. Und von der Länge des 
C-Codes in Source Form solltest du dich nicht blenden lassen

von Ralf (Gast)


Lesenswert?

Karl Heinz Buchegger schrieb:
> Das ist jetzt in Ralfs Code noch nicht optimal gelöst. sIdx muss keine
>
> 32 Bit Variable sein und ob die anderen beiden 32 BIt sein müssen,
>
> müsste man analysieren. Auch der Return Datentyp muss kein 32 Bit Typ
>
> sein, 16 Bit reichen.

Was ich bei meinem Code noch dazu sagen muss ist, dass ich den Code auf 
einem STM32 laufen habe. Aus diesem Grund sind meine Variablen 32Bit 
breit damit ich mir irgendwelche Extension Befehle im Assembler sparen 
kann.
Bei einer Umsetzung auf einem AVR sollte man, wie Karl Heinz schon 
geschrieben hat, die Datenformate noch ein wenig für den AVR anpassen.
Gruß,
Ralf

von Micha S. (e-tec)


Lesenswert?

Ja da hast du recht, ich denke ich werde den ralfs code ncoh ein wenig 
für den avr optimieren und bei interesse posten!

Grüße & herzlichst Dankeschön

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.