Merkwürdigkeit: sprintf() mit float

Hi,

über diese Zeile bin ich gestern gestolpert:

00000000,00:00:00,,,,,,,6

Sie wurde geschrieben von meinem Programm, das Meßwerte ausgeben soll.

Hmmmmm. Also habe ich mal Google befragt und bin zu der Aussage gekommen, daß die Funktion sprintf() keine Float darstellen könne. Empfohlen wurde die Verwendung der Funktion dtostrf(). Diese habe ich in mein Programm eingebaut und nur noch gestaunt. Hier ist das Programmstück zur Ausgabe obiger Werte:

        sprintf( buffer, "%04d%02d%02d,%02d:%02d:%02d,", year, month, day, hour, minute, second);
        DEBUG_PRINT2USB( buffer );
        myFile.print( buffer );
        
        dtostrf(lat, 10, 6, buffer);          <-- um diese Zeile geht es hier
        sprintf(buffer, "%10.6f,%10.6f,", lat, lon);
        DEBUG_PRINT2USB( buffer );
        myFile.print( buffer );
  
        sprintf(buffer, "%5.1f,%5.1f,", pm10, pm25);
        DEBUG_PRINT2USB( buffer );
        myFile.print( buffer );

        sprintf(buffer, "%4.1f,%4.1f,", temperature, humidity);
        DEBUG_PRINT2USB( buffer );
        myFile.print( buffer );
  
        sprintf( buffer, "%x", sampledValues);
        DEBUG_PRINTLN2USB(buffer);
        myFile.println( buffer );

Mit diesem Programm, also mit der Funktion dtostrf(), erhalte ich folgende Ausgabe:

00000000,00:00:00, 0.000000, 0.000000, 3.6, 2.3,20.9,41.1,6

Kommentiere ich die eine Zeile mit der Funktion dtostrf() aus, dann fehlen wieder die Floats, füge ich die Zeile wieder ein, so erhalte ich die Floats. Dabei wird in dieser Zeile ein Buffer gefüllt, der aber nicht verwendet wird.

Ergebnis: sprintf() kann Floats darstellen, wenn man die Bibliotheksroutine überlistet.

Dazu passt folgende Beobachtung: in meinem Programm verwende ich das Makro DEBUG_PRINTLN2USB. Dieses habe ich wie folgt definiert:

#define DEBUG

#ifdef DEBUG
#define    DEBUG_PRINT2USB(...)    debugSerial.print(__VA_ARGS__)
#define    DEBUG_PRINTLN2USB(...)  debugSerial.println(__VA_ARGS__)
#else
#define    DEBUG_PRINT2USB(...)
#define    DEBUG_PRINTLN2USB(...)
#endif

Damit unterscheide ich zwischen Debug- und Release-Version. Nehme ich jetzt den Aufruf der Funktion dtostrf() raus, dann kann das Programm die Floats in der DEBUG-Version, aber nicht mehr in der Release-Version.

Merkwürdig. Eigentlich kann die Funktion sprintf() die Floats darstellen.

Schönen Gruß
AugustQ

PS: ich arbeite mit dem seeeduino LoRaWAN with GPS, falls dies von Bedeutung ist.

Merkwürdig. Eigentlich kann die Funktion sprintf() die Floats darstellen.

Du kannst deinem sprintf das float beibringen!

Dazu legst du neben die schon vorhandene/verwendete platform.txt eine platform.local.txt

Mit dem Eintrag:

# erlaubt float in sprintf und seinen Brüdern.
compiler.c.elf.extra_flags=-Wl,-u,vfprintf -lprintf_flt -lm

evtl. auch noch interessant:

# erlaubt float in sscanf und seinen Brüdern.
compiler.c.elf.extra_flags=-Wl,-u,vfscanf -lscanf_flt -lm

oder:

# erlaubt float in sscanf,sprintf und ihren Brüdern.
compiler.c.elf.extra_flags=-Wl,-u,vfprintf -lprintf_flt  -Wl,-u,vfscanf -lscanf_flt -lm

Nachteil: Es nimmt sich einen guten Schluck aus Flash Flasche.

combie:
Nachteil: Es nimmt sich einen guten Schluck aus Flash Flasche.

Das dürfte bei einem ATSAMD21G18, 32-Bit ARM Cortex M0+ nicht so kritisch sein, eher schon das Finden der richtigen platform.txt.

ich hatte bei meinem M0 das selbe Problem.
füge mal oben im Code ein

#ifdef SAMD_SERIES
#include "avr/dtostrf.h"
#endif

und dann in setup() an den Anfang zusätzlich die Zeile
asm(".global _printf_float");

(Ich hoffe, mein M0 ist zu deinem board kompatibel)

Beispielcode:

#ifdef SAMD_SERIES 
  #include "avr/dtostrf.h"
#endif


void setup()
{
   Serial.begin(115200);
   delay(2000);
   
   asm(".global _printf_float");
   
   Serial.println("print Floating Point on Arduino");
   Serial.print("regular Serial.print(1.234) ");
   Serial.println(1.234);

   char buf[40];
   Serial.print("sprintf(2.345) ");
   sprintf(buf, "%6.4f", 2.345);
   Serial.println(buf);

   Serial.print("dtostrf(3.456) ");
   dtostrf(3.456, 6, 4, buf);
   Serial.println(buf);
}

void loop()
{
}

agmue:
Das dürfte bei einem ATSAMD21G18, 32-Bit ARM Cortex M0+ nicht so kritisch sein, eher schon das Finden der richtigen platform.txt.

Das ist mir doch glatt durch die Lappen gegangen.

Ich nehme alles zurück.
Denn meine Ansage bezieht sich nur auf den AVR.
Für andere, keine Gewähr.

Hi,

ich habe mal nachgeschaut:

ohne dtostrf():

Der Sketch verwendet 32056 Bytes (12%) des Programmspeicherplatzes.

mit dtostrf():

Der Sketch verwendet 41568 Bytes (15%) des Programmspeicherplatzes.

Alles unproblematisch.

Schönen Gruß
AugustQ

und hast du denn jetzt meinen Sketch probiert und bei dir entsprechend geändert? Mit welchem Ergebnis?

Hi dsyleixa,

ich habe die Antwort erst heute gesehen, sorry.

Ich habe mal mein Programm geändert:

  • den include habe ich über das define eingefügt (war vorher bereits drin, ohne define)
  • die asm-Anweisung habe ich in setup() eingefügt
  • meine silly dtostrf()-Anweisung habe ich auskommentiert

und das funktioniert.

Danke. Ich werde das so übernehmen.

Schönen Gruß
AugustQ