Forum: Mikrocontroller und Digitale Elektronik Schwach im Kopfrechnen


von Christian (Gast)


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);
  };

von Christian Zietz (Gast)


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.

von Christian (Gast)


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

von crazy horse (Gast)


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

von Rolf Magnus (Gast)


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.

von Christian (Gast)


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

von Tom (Gast)


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

von Karl (Gast)


Lesenswert?

>atof ist in meiner libc noch nicht implementiert ...

Wie wär's mit sscanf ?

von Jochen (Gast)


Angehängte Dateien:

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
1
// for.cpp : Definiert den Einstiegspunkt für die Konsolenanwendung.
2
//
3
4
#include <iostream>
5
#include <tchar.h>
6
#include <float.h>
7
8
9
10
float atoff(const char *s);
11
float strtof(const char *s, char * *endptr);
12
static int isinfinity(float d);
13
14
typedef struct {
15
  unsigned long sign : 1;    /*       */
16
  unsigned long exp  : 8;    /*   (mantissa)  */
17
  unsigned long mant : 23;  /*   (mantissa)  */
18
} SGL_FLT;
19
20
typedef SGL_FLT * SGL_FLTP;
21
22
#define  SGL_INFEXP  0xff    
23
24
25
26
27
int _tmain(int argc, _TCHAR* argv[])
28
{
29
  char ttt[]="52.4";
30
  float fff;
31
  fff=atoff(ttt);
32
33
34
35
  return 0;
36
}
37
38
39
40
/*****************************************************************************
41
 *       atoff
42
 *            float         
43
 *        #include <stdlib.h>
44
 *          float atoff(const char *s);
45
 *
46
 *        const char *s;       
47
 *              float     
48
 *       S.Kanamori
49
 *       K.Kato
50
 *        2002/12/01      
51
52
*****************************************************************************/
53
float atoff(const char *s)
54
{
55
  return strtof(s, (char * *)NULL);
56
}
57
58
59
60
61
/*****************************************************************************
62
 *       strtof
63
 *            float         
64
 *        #include <stdlib.h>
65
 *          float strtof(const char *s, char **endptr);
66
 *
67
 *        const char *s;       
68
 *          char **endptr;                      
69
 *       0L :      
70
 *              float     
71
 *       S.Kanamori
72
 *       K.Kato
73
 *        2002/12/01      
74
75
*****************************************************************************/
76
float strtof(const char *s, char * *endptr)
77
{
78
  float d, e;
79
  float base;
80
  int exp = 0;
81
  char sign = '+';
82
  char esign = '+';
83
  char *p = (char *)s;
84
85
  d = e = 0.0F;
86
87
  if (!p) {
88
    /*         NULL    0.0     (ANSI      ) */
89
    return 0.0F;
90
  }
91
92
  /*    (  )      */
93
  for (; isspace(*p); p++);
94
95
  if ((*p == '-') || (*p == '+')) {
96
    /*       */
97
    sign = *p++;
98
  }
99
  if (!isdigit(*p))
100
    goto STRTOD_END;
101
102
  /*          */
103
  for (s = p; isdigit(*s); s++) {
104
    /* 10      */
105
    d = (d * 10) + (float)(*s & 0xf);
106
  }
107
108
  /*           */
109
  if (*s == '.')
110
    s++;
111
112
  /*          */
113
  for (base = 1.0F; isdigit(*s); s++) {
114
    base /= 10;
115
    e += base * (float)(*s & 0xf);
116
  }
117
  /*         */
118
  d += e;
119
120
  if (*s == 'e' || *s == 'E') {
121
    /* d.dd..e+dd             */
122
    s++;
123
    if (*s == '+' || *s == '-') {
124
      /*       */
125
      esign = *s++;
126
    }
127
    for (; isdigit(*s); s++) {
128
      if (exp > FLT_MAX_10_EXP)
129
        continue;
130
      exp = exp * 10 + (int)(*s & 0xf);
131
    }
132
  }
133
  if (d && exp) {
134
    /*         */
135
    if (esign == '-') {
136
      if ((-exp) <= FLT_MIN_10_EXP) {
137
        /* Under-flow   */
138
        d = 0.0F;
139
        errno = ERANGE;
140
        goto STRTOD_END;
141
      }
142
    } else {
143
      if (exp >= FLT_MAX_10_EXP) {
144
        /* Over-flow   */
145
        d = FLT_MAX;
146
        errno = ERANGE;
147
        goto STRTOD_END;
148
      }
149
    }
150
151
    for ( ; exp; ) {
152
      if (exp / 10) {
153
        if (esign == '-')
154
          d /= 10000000000.0F;
155
        else
156
          d *= 10000000000.0F;
157
        exp -= 10;
158
      } else {
159
        d = ((esign == '-') ? (d / 10.0F) : (d * 10.0F));
160
        exp--;
161
      }
162
      if (isinfinity(d))
163
        break;
164
      if (d == 0.0F) {
165
        /* Under-flow   */
166
        errno = ERANGE;
167
        break;
168
      }
169
    }
170
  }
171
172
  STRTOD_END:
173
  if (endptr)
174
    /*                         */
175
    *endptr = (char *)s;
176
  if (sign == '-')
177
    d = -(d);
178
  if (isinfinity(d))
179
    errno = ERANGE;
180
  return d;
181
}
182
183
/*****************************************************************************
184
 *       isinfinity
185
 *          float                   
186
 *        static int isinfinity(float d);
187
 *
188
 *        float d;    float    
189
 *       1 : Infinity
190
 *          0 : Normal
191
 *       S.Kanamori
192
 *       K.Kato
193
 *        2002/12/01      
194
195
*****************************************************************************/
196
static int isinfinity(float d)
197
{
198
  SGL_FLTP dp;
199
200
  dp = (SGL_FLTP)&d;
201
  if (dp->exp == SGL_INFEXP) {
202
    /* Infinity */
203
    return 1;
204
  }
205
  return 0;
206
}

von Jochen (Gast)


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

von Christian Zietz (Gast)


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?

von Christian (Gast)


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/2002-10/msg00085.html

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

von tenner (Gast)


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.

von Christian (Gast)


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

von A.K. (Gast)


Lesenswert?

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

von Christian (Gast)


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

von Christian (Gast)


Lesenswert?

Hallo NG,


acos nun selbst implementiert, immer noch mist ...

Lg


Christian

von A.K. (Gast)


Lesenswert?

Satire live.

von Jochen (Gast)


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

von Christian (Gast)


Angehängte Dateien:

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

von A.K. (Gast)


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.

von Christian (Gast)


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

von tenner (Gast)


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.

von A.K. (Gast)


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.

von Stupsi (Gast)


Lesenswert?

@Christian

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

von Frage (Gast)


Lesenswert?

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

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Warum kein Festkomma ;)

von Philipp B. (philipp_burch)


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

von Stupsi (Gast)


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.

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.