Forum: Compiler & IDEs Problem mit atol()


von Andi K. (fry12)


Lesenswert?

Hallo zusammen,

ich möchte aus einem Char-Array, in dem Ziffern einer Zahl gespeichert 
sind, einen bestimmten Teil extrahieren und mit Hilfe von atol() bzw. 
atoi() in einen integer konvertieren.
1
char data[] = "-12.99";
2
uint32_t integer = (uint32_t)(atol(data+1));

Das data+1 als Funktionsargument für atol() dient dem Überspringen des 
Vorzeichens. Das Ganze läuft auf einem Arduino Micro.

Keine große Aufgabe an sich dachte ich, aber bei Zahlen mit Vorzeichen 
funktioniert das irgendwie nicht. Das wird mir über die serielle 
Schnittstelle ausgespuckt:
1
data[0]: -
2
data[1]: 1
3
data[2]: 2
4
data[3]: .
5
data[4]: 9
6
data[5]: 9
7
data[6]: 
8
integer = 4294967284

Eigentlich müsste atol() bei data[1] anfangen und direkt nach data[2] 
aufhören, da danach ein Punkt kommt und das nicht mehr als Integer 
interpretiert werden kann oder habe ich das falsch verstanden?

Hat vielleicht jemand eine Idee?

: Bearbeitet durch User
von Udo S. (urschmitt)


Lesenswert?

Seit wann kann ein long eine Kommazahl speichern?
Wenn schon brauchst du atof()
Ausserdem sollte man ein long auch nicht unbesehen in ein unsigned long 
casten, hat aber hier nichts mit dem Problem zu tun.

Du brauchst ein C Buch.

von Andi K. (fry12)


Lesenswert?

Ich will keine Floatzahl abspeichern, ich möchte nur den ganzzahligen 
Anteil der Zahl (ohne Vorzeichen). Das dürfte doch eigentlich mit atoi 
bzw. atol funktionieren, weil die am character '.' automatisch 
abbrechen.

: Bearbeitet durch User
von _??? (Gast)


Lesenswert?

Andi K. schrieb:
> Eigentlich müsste atol() bei data[1] anfangen und direkt nach data[2]
> aufhören, da danach ein Punkt kommt und das nicht mehr als Integer
> interpretiert werden kann oder habe ich das falsch verstanden?

12 Genau das wird bei mir ausgegeben --> mingw-gcc

von Udo S. (urschmitt)


Lesenswert?

Andi K. schrieb:
> Eigentlich müsste atol() bei data[1] anfangen und direkt nach data[2]
> aufhören, da danach ein Punkt kommt und das nicht mehr als Integer
> interpretiert werden kann oder habe ich das falsch verstanden?

Ja, denn atol erwartet von DIR, daß du nur konvertierbare Zeichenketten 
reinstellst, ansonsten ist das Ergebnis undefiniert.

Andi K. schrieb:
> weil die am character '.' automatisch abbrechen.
Sicher? Ich würde sagen im Fehlerfall ist die Ausgabe undefiniert und du 
musst dafür sorgen, daß ein konvertierbarer String übergeben wird.

von Andi K. (fry12)


Lesenswert?

Udo Schmitt schrieb:
> Sicher? Ich würde sagen im Fehlerfall ist die Ausgabe undefiniert und du
> musst dafür sorgen, daß ein konvertierbarer String übergeben wird.

Bisher habe ich immer gelesen, dass beide Funktionen beim ersten nicht 
konvertierbaren Zeichen abbrechen. Nur falls die Zahl zu groß wird, wäre 
die Ausgabe undefiniert.

von _??? (Gast)


Lesenswert?

long int atol ( const char * str );

Convert string to long integer
Parses the C-string str interpreting its content as an integral number, 
which is returned as a value of type long int.

 The function first discards as many whitespace characters (as in 
isspace) as necessary until the first non-whitespace character is found.
!!!!
Then, starting from this character, takes an optional initial plus or 
minus sign followed by as many base-10 digits as possible, and 
interprets them as a numerical value.
!!!!

 The string can contain additional characters after those that form the 
integral number, which are ignored and have no effect on the behavior of 
this function.

 If the first sequence of non-whitespace characters in str is not a 
valid integral number, or if no such sequence exists because either str 
is empty or it contains only whitespace characters, no conversion is 
performed and zero is returned.

von PittyJ (Gast)


Lesenswert?

Bei mir geht das auch.

Der Fehler muss in Teilen des Programmes sein, die nicht gepostet 
wurden.

übrigens: 4294967284 ist -12 als signed. Vielleicht hilft das ja.

von Dirk B. (dirkb2)


Lesenswert?

Wie gibst du denn integer (über die serielle Schnitstelle) aus.
Das wäre nicht der erste Fall wo die Überprüfung falsch gemacht wird.

von Udo S. (urschmitt)


Lesenswert?

_??? schrieb:
> The string can contain additional characters after those that form the
> integral number, which are ignored and have no effect on the behavior of
> this function.

Ok, hatte ich so nicht mehr im Kopf.

PittyJ schrieb:
> Der Fehler muss in Teilen des Programmes sein, die nicht gepostet
> wurden.
>
> übrigens: 4294967284 ist -12 als signed. Vielleicht hilft das ja.

Wichtiger Hinweis. Also würde atol funktionieren wie in der Spec, nur 
das (von mir ganz oben ja angemahnte casten von long to unsigned long) 
würde daraus die fehlerhafte Zahl machen.

Aber dann ist der reale code anders als der gepostete.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Udo Schmitt schrieb:
>
> Wichtiger Hinweis. Also würde atol funktionieren wie in der Spec, nur
> das (von mir ganz oben ja angemahnte casten von long to unsigned long)
> würde daraus die fehlerhafte Zahl machen.

Nö.
Denn er setzt sein atol ja auf die erste Ziffer an. Das '-' hat er 
bereits übergangen.
atol liefert 12.
Die Frage ist nur, wie er seine Ausgabe macht, bzw. was da zwischen atol 
und AUsgabe sonst noch alles passiert und welche Datentypen involviert 
sind.
Leider sind das alles Dinge, die er nicht zeigt.

von Dirk B. (dirkb2)


Lesenswert?

Es gibt ja auch noch atoul oder strtoul extra für unsigned.

von Andi K. (fry12)


Lesenswert?

Dirk B. schrieb:
> Das wäre nicht der erste Fall wo die Überprüfung falsch gemacht wird.
Welche Überprüfung meinst du?

Dirk B. schrieb:
> Wie gibst du denn integer (über die serielle Schnitstelle) aus.
Das ganze läuft ganz standardmäßig per
1
Serial.println(integerDigits);

PittyJ schrieb:
> Der Fehler muss in Teilen des Programmes sein, die nicht gepostet
> wurden.
>
> übrigens: 4294967284 ist -12 als signed. Vielleicht hilft das ja.

Danke für den Hinweis! Ja, der Fehler muss wohl woanders liegen. Im 
Großen und Ganzen soll eine Kommazahl, die in einem char-Array 
gespeichert ist, in Vor- und Nachkommaanteil zerlegt werden, sowie 
erkannt werden, ob es sich um eine negative Zahl handelt. Das alles 
dient der Darstellung auf acht Siebensegmentanzeigen.

Noch eine kurze Frage am Rande: Folgende Funktionsdeklarationen müssten 
doch praktisch identisch sein oder?
1
void function(char* data);
2
void function(char data[]);
Beide Male wird nur der Zeiger auf das erste Array-Element übergeben, 
oder?

von Udo S. (urschmitt)


Lesenswert?

Karl Heinz schrieb:
> Nö.
> Denn er setzt sein atol ja auf die erste Ziffer an. Das '-' hat er
> bereits übergangen.
> atol liefert 12.

Das wäre zuviel Zufall. Wetten, daß der gepostete Code nicht exakt 
seinem Code entspricht, der den fehler verursacht.

: Bearbeitet durch User
von Andi K. (fry12)


Lesenswert?

Dirk B. schrieb:
> Es gibt ja auch noch atoul oder strtoul extra für unsigned.
Danke das werd ich mir mal anschauen!

von Andi K. (fry12)


Lesenswert?

Udo Schmitt schrieb:
> Karl Heinz schrieb:
>> Nö.
>> Denn er setzt sein atol ja auf die erste Ziffer an. Das '-' hat er
>> bereits übergangen.
>> atol liefert 12.
>
> Das wäre zuviel Zufall. Wetten, daß der gepostete Code nicht exakt
> seinem Code entspricht, der den fehler verursacht.

Okay nun hier der Original-Code, um allen Verschwörungstheorien Einhalt 
zu gebieten:
1
uint32_t integerDigits;
2
    
3
(_signNumber == NEGATIVE) ? (integerDigits = (uint32_t)(atol(data+1))) : (integerDigits = (uint32_t)(atol(data)));

Das Problem muss wie bereits erwähnt woanders liegen. Aber das ist jetzt 
auch egal, ich werd jetzt hier keine hunderte Zeilen Code posten ;)

Das mit dem uint32_t-gecaste habe ich gemacht, da ich mir nicht sicher 
war, ob long beim Arduino Micro auch wirklich ein 32 Bit unsigned int 
ist.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Das _signNumber == NEGATIVE wird die Probleme machen, denn die 
Umwandlung erfolgt mit Vorzeichen und das gecaste verhindert die Warnung

von Udo S. (urschmitt)


Lesenswert?

Andi K. schrieb:
>> übrigens: 4294967284 ist -12 als signed. Vielleicht hilft das ja.
>
> Danke für den Hinweis! Ja, der Fehler muss wohl woanders liegen.

Andi K. schrieb:
> Okay nun hier der Original-Code, um allen Verschwörungstheorien Einhalt
> zu gebieten:
> uint32_t integerDigits;
>
> (_signNumber == NEGATIVE) ? (integerDigits = (uint32_t)(atol(data+1))) :
> (integerDigits = (uint32_t)(atol(data

Wie wird _signNumber erkannt. Wenn diese Erkennung fehlerhaft ist 
funktioniert es nicht.
Es muss nur noch ein Whitespace vor dem Minus sein, und dein Code 
funktioniert auch nicht mit "(uint32_t)(atol(data+1))", sondern 
produziert genau den Fehler.

Fakt ist daß du uns hier auf eine falsche Spur geführt hast, weil der 
von dir gepostete Code den Fehler gar nicht produziert.

von Dirk B. (dirkb2)


Lesenswert?

Andi K. schrieb:
> Das mit dem uint32_t-gecaste habe ich gemacht, da ich mir nicht sicher
> war, ob long beim Arduino Micro auch wirklich ein 32 Bit unsigned int
> ist.

Und selbst wenn es nicht so ist, was soll das bringen?

Es kann nichts dazu gedichtet werden, wenn long kleiner ist und es wird 
eh abgeschnitten wenn long größer ist

von _??? (Gast)


Lesenswert?

Andi K. schrieb:
> Im
> Großen und Ganzen soll eine Kommazahl, die in einem char-Array
> gespeichert ist, in Vor- und Nachkommaanteil zerlegt werden, sowie
> erkannt werden, ob es sich um eine negative Zahl handelt.

Schau Dir mal modf an:

http://www.cplusplus.com/reference/cmath/modf/?kw=modf

von Dirk B. (dirkb2)


Lesenswert?

Wo kommt denn das char-Array her?
Und können die Zahlen wirklich soooo groß werden, dass du unbedingt ein 
unsigned long brauchst?

von _??? (Gast)


Lesenswert?

double fracpart,intpart;
    char data[] = "-12.99";
    double x =(atof(data));
    fracpart = modf(x,&intpart);
    printf("%f = %f + %f\n",x,intpart,fracpart);

von Karl H. (kbuchegg)


Lesenswert?

Andi K. schrieb:

> Okay nun hier der Original-Code, um allen Verschwörungstheorien Einhalt
> zu gebieten:

Wenn du den Original Code zeigen willst, dann zeige den vollständigen 
Code und nicht immer nur 1 oder 2 Zeilen aus denen man nichts ermitteln 
kann!

>
1
> uint32_t integerDigits;
2
> 
3
> (_signNumber == NEGATIVE) ? (integerDigits = (uint32_t)(atol(data+1))) : 
4
> (integerDigits = (uint32_t)(atol(data)));
5
>

Da sind ja schon wieder zig andere Sachen im Code versteckt, die schief 
gegangen sein könnten. Woher sollen wir wissen, was da wirklich schief 
gegangen ist?

Im übrigen hast du den ?: Operator nicht wirklich verstanden.
Der Vorteil des ternären Operators gegenüber einem if besteht darin, 
dass er ein Operator ist und als solcher ein Ergebnis liefert. Genauso 
wie eine Addition oder eine Multiplikation auch. Ein Ergebnis mit dem 
man in Ausdrücken weiter arbeiten kann.

Du hast hier nur möglichst kompliziert
1
  if (_signNumber == NEGATIVE)
2
    integerDigits = (uint32_t)(atol(data+1));
3
  else
4
    integerDigits = (uint32_t)(atol(data));
geschrieben (ein if durch ein ? ersetzt und ein else durch ein :) und 
den Vorteil des ternären OPerators überhaupt nicht ausgenutzt. Sonst 
hättest du zb geschrieben
1
   integerDigits  =  (_signNumber == NEGATIVE) ? (uint32_t)(atol(data+1)) :
2
                                                 (uint32_t)(atol(data));
oder aber den Operator noch weiter rein gezogen an die einzige Stelle, 
die in den beiden möglichen Fällen tatsächlich unterschiedlich behandelt 
werden soll: nämlich ob bei data[0] oder bei data[1] begonnen werden 
soll.
1
   integerDigits  =  atol( data + (_signNumber == NEGATIVE) ? 1 : 0 );
oder
1
   integerDigits  =  atol( &data[ (_signNumber == NEGATIVE) ? 1 : 0 ] );

da hätte dir der ternäre Operator wirklich was gebracht, weil du die 
Code-Duplizierung des Funktionsaufrufs vermeidest, man sofort sieht dass 
in jedem Fall auf die immer gleiche Variable zugewiesen wird bzw. klar 
ist, dass in beiden Fällen jeweils mit data gearbeitet wird und sich nur 
der Index unterscheidet. In deiner Version bringt er aber nichts. Ganz 
im Gegenteil. Das ist genau so ein Fall, den C-Verteufler immer (zu 
Recht) anprangern: viel kryptischer Schnickschnack für nichts.

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Mich interessiert ja immer noch, wo diese -12.99 in dem char-Array her 
kommen.

von Andi K. (fry12)


Lesenswert?

Udo Schmitt schrieb:
> Wie wird _signNumber erkannt. Wenn diese Erkennung fehlerhaft ist
> funktioniert es nicht.
Ganz einfach per if-Abfrage:
1
if(data[0] == '-' && _signNumber != NEGATIVE) {
2
    _signNumber == NEGATIVE;
3
}
Die Erkennung funktioniert jedenfalls, das habe ich getestet.

Dirk B. schrieb:
> Wo kommt denn das char-Array her?
Das steht einfach so direkt in der loop()-Funktion.

Dirk B. schrieb:
> Und können die Zahlen wirklich soooo groß werden, dass du unbedingt ein
> unsigned long brauchst?
Ich hab mich da für unsigned long entschieden, weil unsigned int als 
16Bit angelegt wird und nur bis 65k geht, ich aber einen Wertebereich 
bis 99999 benötige (da ich im Extremfall 99999.999 anzeigen lassen 
möchte).

Dirk B. schrieb:
> Mich interessiert ja immer noch, wo diese -12.99 in dem char-Array her
> kommen.
Ja sorry, ich sitz hier nicht ununterbrochen vorm PC mit offener 
mikrocontroller.net-Seite ;)

@Karl Heinz:
Danke für deinen Beitrag. Man sieht, ich bin absoluter Anfänger in C. 
Ich habe den ?-Operator an dieser Stelle einfach mal "getestet".

: Bearbeitet durch User
von Dirk B. (dirkb2)


Lesenswert?

Dann reicht ein (signed) long aber Dicke aus und macht die Sache 
einfacher.
1
int32_t integer = atol(data);
2
3
 integer = (integer < 0) ? -integer : integer ;

Andi K. schrieb:
> Dirk B. schrieb:
>> Wo kommt denn das char-Array her?
> Das steht einfach so direkt in der loop()-Funktion.

Wenn das irgendwie durch Nutzereingabe kommt, ok.

Wenn du aber extra eine Fließkommazahl in das char-Array ausgibst um an 
das Vorzeichen und den Vor- und Nachkommaanteil zu kommen, dann ist das 
nicht ok.

von Andi K. (fry12)


Lesenswert?

Dirk B. schrieb:
> Wenn du aber extra eine Fließkommazahl in das char-Array ausgibst um an
> das Vorzeichen und den Vor- und Nachkommaanteil zu kommen, dann ist das
> nicht ok.
Ne, mit Fließkommazahlen wird hier überhaupt nicht gearbeitet, das steht 
direkt so in der loop()-Funktion.

Dirk B. schrieb:
> Dann reicht ein (signed) long aber Dicke aus
Nur zum Verständnis: Ob ich jetzt signed long oder unsigned long nehme 
ist doch vom Speicherverbrauch vollkommen egal, oder? Beide Male 
benötige ich 32 Bit.

Kann noch jemand etwas zu meiner Frage von weiter oben mit den 
Funktionsdeklarationen sagen?

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

Andi K. schrieb:

>> Dann reicht ein (signed) long aber Dicke aus
> Nur zum Verständnis: Ob ich jetzt signed long oder unsigned long nehme
> ist doch vom Speicherverbrauch vollkommen egal, oder? Beide Male
> benötige ich 32 Bit.


Richtig.

Aber dein mega-umständlicher Code schrumpft zu einem 2 Zeiler zusammen, 
den man auch aus 2 Meter Entfernung in unter 3 Sekunden entziffern kann.

> Kann noch jemand etwas zu meiner Frage von weiter oben mit den
> Funktionsdeklarationen sagen?

Ja, ist identisch. In allen Belangen. Die Schreibweise mit den [] hat 
maximal einen dokumentatorischen Wert, da bei
1
void foo( int * a )
nicht a priori klar ist, ob hier ein Pointer übergeben wird weil die 
Funktion die Variable vom Aufrufer ändern will, oder ob ein Pointer 
übergeben wird, weil von der Funktion ein Array erwartet wird.
Mittels
1
void foo( int a[] )
erhofft man sich einen gewissen dokumentatorischen Wert, in dem die [] 
ausdrücken, dass foo hier tatsächlich ein Array haben will.
Allerdings: Das ist lediglich syntaktischer Zucker. Die beiden Formen 
sind in allen Belangen technisch völlig identisch.

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.