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
chardata[]="-12.99";
2
uint32_tinteger=(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?
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.
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.
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
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.
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.
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.
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.
_??? 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.
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.
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
voidfunction(char*data);
2
voidfunction(chardata[]);
Beide Male wird nur der Zeiger auf das erste Array-Element übergeben,
oder?
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.
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:
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.
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.
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
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
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!
>
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
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.
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.
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".
Dann reicht ein (signed) long aber Dicke aus und macht die Sache
einfacher.
1
int32_tinteger=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.
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?
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
voidfoo(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
voidfoo(inta[])
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.