Go Down

Topic: sprintf with floats for M0  (Read 647 times) previous topic - next topic

dsyleixa

Jun 21, 2018, 04:52 pm Last Edit: Sep 07, 2018, 09:03 am by dsyleixa
hello,
I just received my new M0 Board (a Adafruit ItsyBitsy M0), and could not make sprintf and/or dtostrf work (SAMD Boards installed correctly).

Then I found this piece of code which I c+p'ed into my Sketch - and now surprisingly  both sprintf and dtostrf work fine. But I am curious about 2 things:
1) how does that snippet  make both functions work, especially sprintf for fp?
2) do I always have to c+p it or is there also a way by a #include <....> ?

Snippet:

Code: [Select]
#pragma once

#ifdef __cplusplus
extern "C" {
#endif

char *dtostrf(double val, signed char width, unsigned char prec, char *sout);

#ifdef __cplusplus
}
#endif


complete code:
Code: [Select]
//


#pragma once

#ifdef __cplusplus
extern "C" {
#endif

char *dtostrf(double val, signed char width, unsigned char prec, char *sout);

#ifdef __cplusplus
}
#endif


int i=12345;
int32_t l=1234567890;
float  p=3.14159265358979323846;  // 20 Nachkommastellen
double e=2.71828182845904523536;  // 20 Nachkommastellen
char buf[30];

void setup() {
  // Serial output in setup() unvisible
  Serial.begin(115200);

  delay(1);
  Serial.println("sprintf Test\n");
  sprintf(buf, "i=%d", i); Serial.println(buf);
  sprintf(buf, "l=%ld", l); Serial.println(buf);
  sprintf(buf, "f=%25.20f", p); Serial.println(buf);
  sprintf(buf, "e=%25.20f", e); Serial.println(buf);
  delay(1);
}

void loop() {
  // Serial output works
  sprintf(buf, "i=%d", i); Serial.println(buf);
  
  sprintf(buf, "l=%dl", l); Serial.println(buf);
  
  sprintf(buf, "f=%25.20f", p); Serial.println(buf);
  dtostrf(p,25,20,buf);  Serial.print("  ");  Serial.println(buf);
  
  sprintf(buf, "e=%25.20f", e); Serial.println(buf);
  dtostrf(e,25,20,buf);  Serial.print("  ");  Serial.println(buf);

  
  delay(10000);

}

Juraj

#1
Aug 06, 2018, 05:04 pm Last Edit: Aug 06, 2018, 05:07 pm by Juraj
linking dtostrf.c enables float support in printf with asm(".global _printf_float"). linking sprintf doesn't enable it. using String with float uses dtostrf so enables it too.

the lines you copy-pasted are in avr/dtostrf.h. #include <avr/dtostrf.h> would do
You can't write an Arduino sketch if you didn't learn programming. Not the language, but the concepts of programming - algorithms and data types.

dsyleixa

#2
Sep 06, 2018, 09:27 pm Last Edit: Sep 07, 2018, 09:04 am by dsyleixa
thanks, unfortunately I totally missed your replay... :-/

but that's strange now -
again I used sprintf but it doesn't work any longer in my new code surprisingly -

how can I make sprintf work for floats with my M0 ?

my M0 core is 1.6.19,  IDE 1.8.5


sprintf(buf, "d=%+6.1f r=%+4d p=%+4d", anglef, roll, pitch);

shows just
d= r=  +4 p=  +2


instead, this works though:
dtostrf(anglef, 6, 1, buf1);
sprintf(buf, "d=%s r=%+4d p=%+4d", buf1, roll, pitch);

Juraj

You can't write an Arduino sketch if you didn't learn programming. Not the language, but the concepts of programming - algorithms and data types.

dsyleixa

#4
Sep 06, 2018, 10:52 pm Last Edit: Sep 07, 2018, 09:04 am by dsyleixa
#include <avr/dtostrf.h>
I did that, but neverthless sprintf don't work for floats
(1st line above)

shows just
d= r=  +4 p=  +2

dsyleixa

#5
Sep 07, 2018, 09:02 am Last Edit: Sep 07, 2018, 09:03 am by dsyleixa
so how is it possible to have sprintf for floats for the M0/Zero/Itsybitsy M0/Feather M0 ?
So far it does not work on those platforms.

OTOH, sprintf  works fine with floats  for the Due and the ESP8266 though  !!

dsyleixa


oqibidipo

The magic words have already been uttered here:

linking dtostrf.c enables float support in printf with asm(".global _printf_float")
Put that in some suitable function like setup() or whichever function happens to use floats with sprintf(). Just make sure that the function is actually used.

david_prentice

#8
Sep 09, 2018, 01:19 am Last Edit: Sep 09, 2018, 01:24 am by david_prentice
Arduinos traditionally support C printf() but the cut-down version without f-p.
They expect you to use the C++ print() from Print.h

The overloaded print(float, prec=2) has a default precision of 2.   So you can just say print(float)
If you want a different number of decimal places,   use the full signature.
If you want to control width and precision,   use dtostrf().   

Some ARM cores use the full-fat version of printf().   After all,  you have got plenty of Flash.

Most Arduinos can find dtostrf() by themselves.   The M0 and Zero require you to specifically include the header.   But the M0 and Zero come with full-fat printf() anyway.

STM32 ST Core has a cut-down printf() for compatibility.
STM32 stm32duino Core has full-fat printf()
ESP8266 and ESP32 have full-fat printf()

I did not try Due.

Code: [Select]

#if defined(ARDUINO_SAM_ZERO)
#include "avr/dtostrf.h"
#endif

void setup()
{
    Serial.begin(9600);
    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, "%5.2f", 2.345);
    Serial.println(buf);
    Serial.print("dtostrf(3.456) ");
    dtostrf(3.456, 5, 2, buf);
    Serial.println(buf);
}

void loop()
{
}


Incidentally,   dtostrf() is handy for formatting integers i.e. with prec=0.
Unfortunately,   some cores don't use a proper dtostrf().

David.

dsyleixa

Thank you very much, David, now I see - and now it works for my Itsybitsy M0 (i.e., of course outcommented #ifdef for that core)!
sprintf actually is far better for floats than dtostrf, e.g. for left/right text justify, filling chars, adding + signum for positive values, or adding text and multiple variants and formattings.

I'm very glad it's working now, again, thanks a lot!

david_prentice

No.   dtostrf() can format just as well as printf() i.e. width and precision.
However printf() makes life a lot easier than Serial.print() when you have multiple items.

The whole operation is done in one statement.   Ok,  two if you count sprintf(buf, ...) + Serial.print(buf)

It is wise to write code that will work on ALL Arduino targets.    Most do not have full-fat printf().   All Arduinos have dtostrf().
You can either use dtostrf() in a Serial.print() or as an %s argument in printf().

In an ideal world,   you would not need the specific include.   Especially an include path that will fail on some cores.    Most Arduinos include all relevant headers via "Arduino.h"   (that is prepended to every INO file)

David.

dsyleixa

#11
Sep 10, 2018, 04:18 pm Last Edit: Sep 10, 2018, 04:36 pm by dsyleixa
the goal in that case was
Code: [Select]
sprintf(buf, "d=%+6.1f r=%+4d p=%+4d", anglef, roll, pitch); // right justified, with explicit extra "+" for positive values

or, e.g., for better structered readability on TFTs: 
Code: [Select]
sprintf(buf, "d=%-6.1f r=%-4d p=%-4d", anglef, roll, pitch); // left justified
but that was only 1 example of lots, mostly I need multiples of floats in either line.

dostrf is far less powerful IMO for such combined chart tables having to show different formatted floats, and extremely cumbersome to daisy chain them up then with extra string constants.

I'm glad sprintf works for ARMs and ESPs, and for some AVR - in case - I'll switch to dtostrf then, unavoidably.

 

david_prentice

#12
Sep 10, 2018, 05:14 pm Last Edit: Sep 10, 2018, 05:16 pm by david_prentice
Yes.   Both your examples would be painful to do in Print.h methods.
Fairly painful to do in dtostrf() too.   You would need separate char buffers for each argument.   And conditionals for the + sign.

God did an excellent job when she invented printf().

David.

Go Up