mikrocontroller.net

Forum: Mikrocontroller und Digitale Elektronik Schwach im Kopfrechnen


Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Sers Ng,


mein ATMega32 ist zu schwach im Kopfrechnen ...

Ich lese Daten aus einem GPS-RX aus und versuche die Entfernung
zwischen zwei punkten zu brechnen (der 1. Punkt wurde vorher
gespeichert), wenn ich den code im MS DevStudio laufen lasse, funzt der
Code, auf dem AVR bekomme ich immer 3,11 KM (am anfang) raus ...

Wenn die Entfernung größer wird, dann springt er auf 6 oder 9 KM, was
ich merkwürdig finde ...

Rechne ich mit dem Taschenrechner nach, dann bekomme ich bis zu 1m oder
2m ein "korrektes" ergebnis ...

Vieleicht hat ja jmd eine Idee...

Vielen Dank im Vorraus,


Christian



#define PI3 57.295779513082320876798154814105

    //variables
    ///////////
    double dWidth1  = atol(szNorth);
    double dLength1 = atol(szEast);
    double dWidth2  = atol(szNorthHome);
    double dLength2 = atol(szEastHome);
    double dResult   = 0.0;
    double t1         = 0.0;
    double t2         = 0.0;
    double t3         = 0.0;

    if(strlen(szNorthHome) > 0)
    {
      strnset(szDST, 0, 10);

      t1 = sin(dWidth1 / PI3) * sin(dWidth2 / PI3);
      t2 = cos(dWidth1 / PI3) * cos(dWidth2 / PI3);
      t3 = t1 + t2 * cos((dLength1 - dLength2) / PI3);

      if(t3 < 1)
      {
        dResult = (6371.0 * acos(t3));
      }
      else
      {
        dResult = 0;
      };
    };

    dtostrf(dResult, 5, 4, (char*)szDST);
  }
  else
  {
    dtostrf(0, 5, 4, (char*)szDST);
  };

Autor: Christian Zietz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Enthalten szNorth, szEast, szNorthHome und szEastHome denn nur Integer?
Denn atol konvertiert ja nur Stringdarstellungen von Integern. Sprich
atol("52.1") ergibt 52, auch wenn Du's einem Double zuweist.
Evtl. solltest Du Dir mal atof (und das restliche User-Manual zur
avr-libc) angucken.

Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Christian,


atof ist in meiner libc noch nicht implementiert ...

Also suche ich zur Zeit eine andere Lösung, wobei atol als

long atol(const char *__nptr)

definiert ist, jetzt müsste ich wissen wie sich long verhält ...


Lg


Christian

Autor: crazy horse (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
könnte auch daran liegen, dass der Compiler gar kein double unterstützt,
meckert zwar nicht, führt Berechnungen aber trotzdem nur mit
float-Genauigkeit (32bit) aus. Das kann u.U. zu sehr wunderlichen
Erscheinungen führen...

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
> jetzt müsste ich wissen wie sich long verhält ...

Was meinst du damit? long ist halt ein Ganzzahltyp und verhält sich
auch so.

Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Rolf,

ich meine mit wieveil nachkommastellen sich long verhält, da ja
nachkommastellen berechnet werden ...

Insgesamt habe ich 6 Nachkommastellen, die es zu berechnen gilt ...



Lg


Christian

Autor: Tom (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hi

long ist ein doppelter int, double ist ein doppelter float. Du kannst
den String-zu-Zahl-Algorithmus auch selbst implementieren, das ist
nicht wirklich schwer: (die Performance mal ausser Acht gelassen)

 bVorDemKomma = Ja
 nZiffer = 0
 nDurchgang = 0
 Resultat = 0
 Für jeden Buchstaben x
   nDurchgang++
   Ist x = . oder , ?
     bVorDemKomma = Nein
   Ist '0' <= x <= '9'
     nZiffer = ASCII_Code(x) - ASCII_Code(0)
     Ist bVorDemKomma ?
       Resultat = Resultat * 10 + nZiffer
     Sonst
       Resultat = Resultat + nZiffer  10  nDurchgang
   Sonst
     Abbruch (Keine Zahl)

FG & HTH

Tom

Autor: Karl (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
>atof ist in meiner libc noch nicht implementiert ...

Wie wär's mit sscanf ?

Autor: Jochen (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hab hier die atoff() fkt
also mit MS-Dev gehts beim avr muss man vielleicht anpassungen
vornehmen (pointer und so)
ist alles vom nc30 M16c compiler

Gruß
Jochen
// for.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
//

#include <iostream>
#include <tchar.h>
#include <float.h>



float atoff(const char *s);
float strtof(const char *s, char * *endptr);
static int isinfinity(float d);

typedef struct {
  unsigned long sign : 1;    /*       */
  unsigned long exp  : 8;    /*   (mantissa)  */
  unsigned long mant : 23;  /*   (mantissa)  */
} SGL_FLT;

typedef SGL_FLT * SGL_FLTP;

#define  SGL_INFEXP  0xff    




int _tmain(int argc, _TCHAR* argv[])
{
  char ttt[]="52.4";
  float fff;
  fff=atoff(ttt);



  return 0;
}



/*****************************************************************************
 *       atoff
 *            float         
 *        #include <stdlib.h>
 *          float atoff(const char *s);
 *
 *        const char *s;       
 *              float     
 *       S.Kanamori
 *       K.Kato
 *        2002/12/01      

*****************************************************************************/
float atoff(const char *s)
{
  return strtof(s, (char * *)NULL);
}




/*****************************************************************************
 *       strtof
 *            float         
 *        #include <stdlib.h>
 *          float strtof(const char *s, char **endptr);
 *
 *        const char *s;       
 *          char **endptr;                      
 *       0L :      
 *              float     
 *       S.Kanamori
 *       K.Kato
 *        2002/12/01      

*****************************************************************************/
float strtof(const char *s, char * *endptr)
{
  float d, e;
  float base;
  int exp = 0;
  char sign = '+';
  char esign = '+';
  char *p = (char *)s;

  d = e = 0.0F;

  if (!p) {
    /*         NULL    0.0     (ANSI      ) */
    return 0.0F;
  }

  /*    (  )      */
  for (; isspace(*p); p++);

  if ((*p == '-') || (*p == '+')) {
    /*       */
    sign = *p++;
  }
  if (!isdigit(*p))
    goto STRTOD_END;

  /*          */
  for (s = p; isdigit(*s); s++) {
    /* 10      */
    d = (d * 10) + (float)(*s & 0xf);
  }

  /*           */
  if (*s == '.')
    s++;

  /*          */
  for (base = 1.0F; isdigit(*s); s++) {
    base /= 10;
    e += base * (float)(*s & 0xf);
  }
  /*         */
  d += e;

  if (*s == 'e' || *s == 'E') {
    /* d.dd..e+dd             */
    s++;
    if (*s == '+' || *s == '-') {
      /*       */
      esign = *s++;
    }
    for (; isdigit(*s); s++) {
      if (exp > FLT_MAX_10_EXP)
        continue;
      exp = exp * 10 + (int)(*s & 0xf);
    }
  }
  if (d && exp) {
    /*         */
    if (esign == '-') {
      if ((-exp) <= FLT_MIN_10_EXP) {
        /* Under-flow   */
        d = 0.0F;
        errno = ERANGE;
        goto STRTOD_END;
      }
    } else {
      if (exp >= FLT_MAX_10_EXP) {
        /* Over-flow   */
        d = FLT_MAX;
        errno = ERANGE;
        goto STRTOD_END;
      }
    }

    for ( ; exp; ) {
      if (exp / 10) {
        if (esign == '-')
          d /= 10000000000.0F;
        else
          d *= 10000000000.0F;
        exp -= 10;
      } else {
        d = ((esign == '-') ? (d / 10.0F) : (d * 10.0F));
        exp--;
      }
      if (isinfinity(d))
        break;
      if (d == 0.0F) {
        /* Under-flow   */
        errno = ERANGE;
        break;
      }
    }
  }

  STRTOD_END:
  if (endptr)
    /*                         */
    *endptr = (char *)s;
  if (sign == '-')
    d = -(d);
  if (isinfinity(d))
    errno = ERANGE;
  return d;
}

/*****************************************************************************
 *       isinfinity
 *          float                   
 *        static int isinfinity(float d);
 *
 *        float d;    float    
 *       1 : Infinity
 *          0 : Normal
 *       S.Kanamori
 *       K.Kato
 *        2002/12/01      

*****************************************************************************/
static int isinfinity(float d)
{
  SGL_FLTP dp;

  dp = (SGL_FLTP)&d;
  if (dp->exp == SGL_INFEXP) {
    /* Infinity */
    return 1;
  }
  return 0;
}


Autor: Jochen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
ach du bist übrigens schon der zweite der probleme hat des ein avr
falsch rechnet
hier http://www.mikrocontroller.net/forum/read-1-232087.html#new
da funktionierte die sqrt() fkt nicht ganz richtig
vielleicht geben die cos() sin() .. fkt auch falsche ergebnisse zurück

Autor: Christian Zietz (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Christian: long hat KEINE Nachkommastellen, d.h. atol liefert auch
keine Zurück. Was für einen Compiler bzw. was für eine libc verwendest
Du denn?

Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Ng,


danke erst mal für die Antworten...

Das Problem scheint wohl schon an anderer Stelle bekannt zu sein:

http://lists.gnu.org/archive/html/avr-gcc-list/200...

Mist, Ich werde die Tage mal versuchen die funktionen aus der glibc zu
fischen und einzubauen ...

Falls das schon jmnd gemacht hat oder eine andere Lösung hat...


Lg


Christian

Autor: tenner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
dein problem ist weniger die fehlerhafte acos(x) funktion, auch wenn
diese einen zusätzlichen fehler verursachen könnte. das post ist von
2002, der fehler sollte also schon längst gefixt sein.

atol( x ) bricht dir die füße. long hat keine nachkommastellen, es ist
ein ganzzahl datentyp. die funktion atol( x ) berücksichtigt keine
stellen hinter dem komma!

das wurde aber alles hier bereits mehrfach gedsagt! du scheinst in
dieser beziehung lernresistent zu sein.

Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Tenner,


ja, ich habe es wohl schon lang begriffen und auf das letzte WinAVR
upgedated, dort ist atof() auch implementiert, mein Problem bleibt
trotzdem bestehen ...

Versuch mal folgendes und staune:

[C]

char szSomeThing[17];

strnset(szSomeThing, 0, 17); //strnset selbst implementiert

double dStaune = atof("12.3456789\0");
dtostrf(dStaune, 16, 10, (char*)szSomeThing);

und auf einem LCD ausgeben:

12.3456788063

6 setzen...

Nächstes Beispiel:


double d = 0;

for(int n = 0; n <= 100; ++n) d += 0.0000001;

Auf dem LCD: 0.0000101 (stimmt)

Also ist die atof() Funktion für die Wurst...


Nun bin ich gerade dabei die atof() Funktion selbst zu schreiben.
Ich hoffe das es taugt, werde da aber gleich mehr wissen ...


Lg


Christian

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
if (sizeof(double) > sizeof(float))
  printf("das ist garantiert kein WinAVR\n");

Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo NG,


tja, führt auch nicht zum Erfolg ...

Es kann jetzt nur noch an sin() / cos() und acos() liegen ...

Ich werde da später mal was versuchen - gute Nacht ...

Lg


Christian

Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo NG,


acos nun selbst implementiert, immer noch mist ...

Lg


Christian

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Satire live.

Autor: Jochen (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
mhhhhhh
ich hab doch schon ne atof fkt oben fertig reingestellt (Vom nc30
compiler für M16c) und läuft (selbst getestet) mit MS Studio C
ich frag mich nur warum sieht/ließt das keiner?????
wenn andere fkt gefragt sind kann ich ja mal den ganzen source von dem
M16c libs schicken

Autor: Christian (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hallo Jochen,


Du hast natürlich recht, da ich aber einen Fehler suche, wo ich nicht
weiß, wo ich beginnen soll, sind fertige Sachen vileicht eine neue
Fehlerquelle...

Mal davon ab, stehen mir auf dem ATMega32 nur noch 1K zur Verfügung
...

Aber ich währe an der Implementation der sin() cos() und acos()
Funktion interessiert ...

Bis jetzt habe ich das ganze immer im MS Dev Studio testen können, wo
es läuft, es kann jetzt nur noch an sin(), cos() und acos() liegen -
wenn der Compiler in der Lage ist +/-* zu rechnen ...

Vieleicht liegt es auch an den Nachkommastellen, das er bei
Veränderungen in der 5 oder 6 Stelle nicht mitkommt ...

Ich habe mal der MS DevStudio Prj angehängt, es ist gleich wie im
AVR und die Koordinaten sind fest (Österreich / Klagenfurt), im
DevStudio kommen 0.7xx raus der AVR sagt 0.000....

Ich weiß da nicht mehr weiter ...

Ich hoffe das es bald eine Lösung gibt, am Dienstag ist Clubabend und
das Teil soll ja ein wenig mehr können als den QRA-Locator
anzuzeigen....


55 es 73 de Christian OE8CWQ


BTW: Wer mag, kann sich das auch direkt bei mir mal im Shack ansehen
einfach mal auf 145.7875 MHz (-DUP / Tone) durchrufen bin ab 16.00 Uhr
QRV ...

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Aller Komik zum Trotz tut's mir doch weh, mit anzusehen, wie jemand
sein Brett vorm Kopf für die Welt hält. Also noch ein Versuch:

Was beim PC funktioniert, muss beim Controller noch lange nicht
funktionieren. Erst recht nicht, wenn dessen Compiler nicht
ANSI-konform ist.

float.h vom PC:

FLT_DIG  = 6
DBL_DIG  = 15
LDBL_DIG  = 18

float.h von WinAVR:

FLT_DIG  = 6
DBL_DIG  = 6        /* ANSI: >= 10 */
LDBL_DIG  = 6

Im obigen Beispiel:
  atof("12.3456789\0");
  12.3456788063
ist das Ergebnis sogar auf 7 Stellen genau. Besser geht's nicht.

Autor: Christian (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo NG,


so, danke an AK für den Hinweis...

Aber woher hast Du diese Werte ?

FLT_DIG  = 6
DBL_DIG  = 6        /* ANSI: >= 10 */
LDBL_DIG  = 6


Bei mir steht dort:

FLT_DIG _FLT_DIG_

Wobei ich _FLT_DIG_ nirgens finden kann ...

Auch wenn ich diesen Wert erhöhe, so hat das keine Auswirkung, solange
nicht die ganze lib neu kompiliert wird ...

Hat das sich schon mal jemand mit befasst und kann das mal versuchen ?

BTW: Aller Komik zum Trotz tut's mir doch weh, mit anzusehen, wie
jemand sein Brett vorm Kopf für die Welt hält.

--> Ja, da hast Du recht, mit Deinem voherigen Post konnte hier wohl
niemand was anfangen, sonst hätte es ja auch jemand anderes schon
getan.

Ich schreibe in ein Forum, weil ich nicht weiter komme und weil ich
Hilfe benötige - Ich kann halt nicht alles wissen, deshalb sind
Hinweise wie:

if (sizeof(double) > sizeof(float))
  printf("das ist garantiert kein WinAVR\n");

oder der Hinweis "Satire live."  über...

Wenn Du magst, dann bin ich gerne bereit, gemeinsam mit Dir oder allen
anderen eine Lösung für dieses Problem zu finden und der Allgemeinheit
zur Verfügung zu stellen...

Das ist der Sinn dieses Forums und damit kommen wir hier weiter !

Lg


Christian

Autor: tenner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
chirstian scrieb:

"...und auf einem LCD ausgeben:

12.3456788063

6 setzen..."

nix für ungut, aber ich denke du solltest dich mal mit einen guten
c-buch zusammensetzen (K&R) und dir das kapitel über datentypen und
casting duchlesen. dann die docu zum winavr zur hand nehmen und schauen
wie welche datentypen implementiert sind und welchen zahlenraum sie
umfassen.
wenn du dann deinen code entsprechend überarbeitet hast und es immer
noch zu signifikanten fehlern kommt, können wir noch einmal über fehler
in der implementierung von sin(x) cos(x) ect. reden.

Autor: A.K. (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
"Auch wenn ich diesen Wert erhöhe"

Weil der vom Compiler festgelegt ist. Daran gibt's nichts zu ändern,
das verwendete Fliesskommaformat hat nun einmal nicht mehr Stellen zu
bieten. Für mehr als 6 Dezimalstellen musst Du nicht nur die Lib
ändern, sondern auch den Compiler (gcc-*\gcc\config\avr\avr.h,
#define DOUBLE_TYPE_SIZE 32).

Ich ging übrigens davon aus, dass jemand der eben mal atof oder acos
neu schreibt, dem Statement
  if (sizeof(double) > sizeof(float))
    printf("das ist garantiert kein WinAVR\n");
entnehmen kann, dass in WinAVR "double" nicht genauer ist als
"float". Anders als beim PC. Sorry für die Fehleinschätzung.

Abhilfe: (a) Compiler verwenden, der ein 64-bit Fliesskommaformat
unterstützt. (b) C++ verwenden und die Arithmetik darin als eigenen
Datentyp unter Verzicht auf float/double komplett selbst realisieren.

Autor: Stupsi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Christian

Habe das gleiche Problem mit 3,11 km. Konntest Du es lösen?
Ich verwende keine atol oder sonstige Konvertierung.

Autor: Frage (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
warum kein strtod? (da hier an der zuverlässigkeit von atof gezweifelt 
wird)

Autor: Läubi .. (laeubi) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Warum kein Festkomma ;)

Autor: Philipp Burch (philipp_burch)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Stupsi:

Wenn du dir den ganzen Thread durchliest, wirst du feststellen, dass die 
Antwort bereits mehrmals gepostet wurde. Hier nochmal:
Die AVR libc rechnet mit Fliesskommazahlen bis 32 Bit und nicht mehr. 
Basta.
Um genauere Ergebnisse zu erhalten musst du entweder mit genügend 
grossen Festkommawerten arbeiten (Ganzzahltypen verwenden und das Komma 
bei der Ausgabe reinschummeln), oder aber selbst Funktionen für 
Berechnungen mit höherer Genauigkeit erstellen. Ich empfehle Ersteres, 
besonders für ein GPS oder so. Nimm deine gröbste Auflösung mit der du 
noch klarkommst und rechne aus, wie gross deine Datentypen dann sein 
müssen um den ganzen Bereich abzudecken. Wenn's mit 32 Bit geht hast du 
Glück...

Autor: Stupsi (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Auf

http://williams.best.vwh.net/avform.htm#Dist

findet sich eine Formel, mit der Distanzen ohne den bei mir und bei 
Christian aufgetretenen Fehler, der durch die Ungenauigkeit von 
Fliesskommazahlen des Controllers entsteht, berechnet werden können. 
Damit hat sich mein Problem gelöst.

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
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
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.