So no %f for sprintf.... what's your solution? [SOLUTION]

[FIX]
I wrote a utility function, see below
[/FIX]

I'm gobsmacked that %f is not supported in sprintf... (I'm not going to prove it to the person who is just about to ask me to... I have three broken teeth and a split lip so there!).

So what do you do please? I need it to conveniently (loadsa Serial.prints are a bit of a mess) format output strings to be passed to Processing and sprintf would have been ideal.

  • PString looks nearly there...

  • Hacking the compiler process to get AVR functionality (or whatever) to compile (if possible at all) sounds too fragile...

  • dtostrf() - can't find the format characters it supports :frowning:

  • Build your own...

Thnx

dtostrf(resultstring *, width, decimal points, float value);

Assign the value to an int. There is your number before the decimal point.
Multiply it by 10^n where n is the number of digits you want after the decimal point. Assign that value to an int and modulus divide by 10^n. There is your number after the decimal point. Use "%d.%d" with those two values in sprintf().

My solution: don't use floats in the first place.

Just work with bigger numbers.

E.g., instead of volts (2.87V), work with millivolts (2870mV). Instead of seconds (12.847s) work with milliseconds (12847ms). Etc.

Lots of geometry calculations so its all floating point stuff

I think you got the params wrong :frowning:

dtostrf(resultstring *, width, decimal points, float value);

Header file says

extern char *dtostrf(double __val, signed char __width,
                     unsigned char __prec, char *__s);

Here is a little piece of code I've put together to deal with the no float support in sprintf.

Arrch could you post your solution as code please??

/*
    Utility function for use in printing floats into buffers like sprintf(...

    The library function dtostrf is the basis of this utility. It does a lot of work
    in converting floats to strings but what makes it a bit irritating is that it needs
    a buffer to write the float into which makes it very cumbersome in in-line code -
    just rewrite the sprintf example below using dtostrf!

    A collection of static char buffers is maintained in the function so that they are used
    consequetively with each call. A single buffer cannot be used when the function is used
    as a parameter to a call like sprintf in the example/test code below. This is simply because
    the compiler writes the code so that all the calls to fs2 are made before passing the
    results of the calls to the routine sprintf.

    This function does hard work of cycling through the internal buffers. The default
    number of buffers is 10 so a sprintf, for example, with more than 10 calls to fs2 will
    overwrite the the beginning of the buffer. Avoid this by making a subsequent call to sprintf.

    Please feel free to use this as you wish... I'm sure some enterprising coder will improve it :)

    Alan Boother 2013/07/03
*/

// float to string
char *f2s(float f, int p){
  char * pBuff;                         // use to remember which part of the buffer to use for dtostrf
  const int iSize = 10;                 // number of bufffers, one for each float before wrapping around
  static char sBuff[iSize][20];         // space for 20 characters including NULL terminator for each float
  static int iCount = 0;                // keep a tab of next place in sBuff to use
  pBuff = sBuff[iCount];                // use this buffer
  if(iCount >= iSize -1){               // check for wrap
    iCount = 0;                         // if wrapping start again and reset
  }
  else{
    iCount++;                           // advance the counter
  }
  return dtostrf(f, 0, p, pBuff);       // call the library function
}

And here is some test/sample code

// test/example code below

char sOut[200];                         // big space for output

void setup()                            // test funtion in setup
{
  float s0 = 0.1;                       // this one will be overwritten by s10
                                        // with the current buffer in f2s of 10 floats
  float s1 = 1.1;
  float s2 = 2.1;
  float s3 = 3.1;
  float s4 = 4.1;
  float s5 = 5.1;
  float s6 = 6.1;
  float s7 = 7.1;
  float s8 = 8.1;
  float s9 = 9.1;
  float s10 = 10.1;

  Serial.begin(9600);


  sprintf(sOut, "0: %s, 1:%s, 2:%s, 3:%s, 4:%s, 5:%s, 6:%s, 7:%s, 8:%s, 9:%s, 10:%s", f2s(s0, 4), f2s(s1, 4), f2s(s2, 4), f2s(s3, 4), f2s(s4, 4), f2s(s5, 4), f2s(s6, 4), f2s(s7, 4), f2s(s8, 4), f2s(s9, 4), f2s(s10, 4));
  Serial.println(sOut);
  Serial.println("The output above has overwritten 0:with 10:'s value");
  Serial.println();

  sprintf(sOut, "0: %s, 1:%s, 2:%s, 3:%s, 4:%s, 5:%s, 6:%s, 7:%s, 8:%s, 9:%s", f2s(s0, 4), f2s(s1, 4), f2s(s2, 4), f2s(s3, 4), f2s(s4, 4), f2s(s5, 4), f2s(s6, 4), f2s(s7, 4), f2s(s8, 4), f2s(s9, 4));
  Serial.println(sOut);
  Serial.println("The output above only needs 10 buffers so overwriting hasn't occured");
  Serial.println();

}


void loop(){
}

acboother:
I think you got the params wrong :frowning:

dtostrf(resultstring *, width, decimal points, float value);

Header file says

extern char *dtostrf(double __val, signed char __width,

unsigned char __prec, char *__s);

oops :blush:sorry. My mistake. Typed it wrong

acboother:
Arrch could you post your solution as code please??

Really simple-

Say you start with a float value:

float myFloat = 123.45;

We want to break this up into two ints: 123 and 45. Getting 123 is trivial:

int a = myFloat; // a will contain 123

We can then multiple myFloat by 10^n where n is the number of digits precision. We want two digits, so we multiply it by 10^2 or 100, and store the result in an int.

int b = myFloat * 100; // b will contain 12345

To extract 45 from 12345, we can use modulus division:

b = b % 100; // 12345 % 100 = 45

We can then finally pass the result into sprintf:

sprintf(buffer, "The number is %d.%d", a, b);

Be aware, though, that you may need to use long variables instead of ints, depending on what the maximum value you can get, and how many digits precision you want.

Arrch - I thought this was you meant but it doesn't work... look at 123.045 it returns 123.4... oh dear. :disappointed_relieved:

void setup()
{

  char buffer[100];

  float myFloat = 123.045;

  int a = myFloat; // a will contain 123

  int b = myFloat * 100; // b will contain 12345


  b = b % 100; // 12345 % 100 = 45


  sprintf(buffer, "The number is %d.%d", a, b);
  Serial.begin(9600);
  Serial.println(buffer);
}

void loop(){
}

When in doubt, print it out.

If you printed b, you would see that b will contain 4, not 45. if you want it to contain 45, you need to multiply it by 10^3 or 1000. You will also want to modify your sprintf format codes to account for leading zeros. An example of such appears at the bottom of this page:

http://www.cdf.toronto.edu/~ajr/209/notes/printf.html

You missed the point it should be 123.045 - the zero is missing

But you know it is missing, therefore you can restore it.
%d.%d is just too simplistic.
Why do you expect the %d on the right of the point to behave any differently to the one on the left?

acboother:
You missed the point it should be 123.045 - the zero is missing

You must not have read the second sentence of my post:

You will also want to modify your sprintf format codes to account for leading zeros. An example of such appears at the bottom of this page:

CSC 209: C: printf and scanf

Agreed

and it doesn't handle negative numbers...

acboother:
Agreed

and it doesn't handle negative numbers...

Again, something that can be easily accounted for.

acboother:
I'm gobsmacked that %f is not supported in sprintf...

But it IS supported by the AVR libC.
The issue is that that the Arduino IDE links in a version
of sprintf() that that has floating point support explicitly removed to save code space.
Combine that with the IDE does not let you change the linker options to allow linking in
the version that does support floats.
(The newer IDE used for DUE does allow modifying the linker options)

Have a read of this thread for details and how to patch your libc libraries to permanently enable
floating point support in the xxprintf() routines:
http://forum.arduino.cc/index.php?topic=124809.0

--- bill

I noticed some format stuff lurking in the core code.

It does seem unfortunate that the code to support it isn't available as something that handles floats is obviously floating about (pun intended, sorry) to support Serial.print printing floats... oh well, such are things.

acboother:
I'm gobsmacked that %f is not supported in sprintf... (I'm not going to prove it to the person who is just about to ask me to... I have three broken teeth and a split lip so there!).

A lot of functionality is in the libraries, and the linker then strips it out if you don't use it. So far so good. But the %f is a runtime thing, so the linker can't strip it out, and it takes a lot of memory. Thus it was omitted.

Other people are gobsmacked that printf takes so much program memory, so you can't please everyone. :slight_smile:

There is a "format double" library available, see this link:

http://forum.arduino.cc/index.php?topic=85692.msg1127105#msg1127105

It's not too much trouble to include that and then get your floats printed out nicely.

Plus, as I recall, Serial.print handles floats natively.

I like the idea of using the sort of routine you mention for formatting in order to keep the program size down by avoiding the clutter of additional library functionality that's not needed.

I would also 'embed' the print buffers inside it at the same time, as I did in my example above, as one of the fundamental irritants is having to provide the print buffer as a parameter. Having to manage them and how long the line of code becomes is a pain and a source of typing woes, again imagine my sprintf with having to provide the print buffers as well. :frowning:

I have some source code for a big number library too, C compilers, linked list managers etc etc... except its on a 3.5 floppy disc... doh