Dtostrf and string buffer size

What will happen if I have this code:

...
float v = 1234.567;
char b[4];

dtostrf(v, 0, 2, b);
...

I guess dtostrf will write outside the string b which may cause memory corruption, or?

What is the smallest safe size of buffer b?

You are correct, this is code that will be not reliable.
2 digits after the comma + 4 digits before the comma anyway (value 1234) + ' .' + a #00 as terminator at the end of the string.
But, as the size of the result depends on the parameters of dostrf, a smallest safe side really depends on the parameters you use and the value you want to display.

1 Like

If you want to be absolute sure - what is the smallest size of b I can have?

Are you running on an AVR based arduino?
Do you know the range of your data?

Using UNO 3, Nano and Mega2560 and Arduino IDE 1.8.12

Ok so the precision can be be limited to 7 digits after the dot
Account fir the sign, the dot and the trailing null char
Then it’s up to the largest integral part you can display

Usually I have an idea what the float value can be, but assume that I have absolutely no idea what the float value can be and I want to make sure that the buffer is always big enough? 15, 30, 100 bytes?

Try this:

// Arduino Uno (with 32-bit float)
// dtostrf( float input, minimal width, digits after dot, buffer)

char buffer[80];

void setup() 
{
  Serial.begin( 115200);
  float v = 100000000000000000000000000000000000000.0;
  dtostrf(v, 0, 2, buffer);  // <--- bug ?, width is zero and 2 decimal digits ?
  Serial.write( buffer);
}

void loop() { }

I count 42 characters plus zero-terminator, that makes 43.
Try making that float number larger, it will no longer fit in a 'float'.
If a buffer of 43 is good enough, then use an array of 80 characters to be sure.

I'll try it.

Just make sure to cap the number of digits to 7

(and note that 100000000000000000000000000000000000000.0 cannot be represented correctly. you could try float v = -1e37; to get the smallest float)

char buffer[80];

void setup()
{
  Serial.begin( 115200);
  Serial.println();
  float v = -1e37;
  dtostrf(v, 0, 7, buffer);
  Serial.write( buffer);
}

void loop() {}

that would lead to -9999999900000000000000000000000000000.0000000

so the question is do you want to find the largest integral part that will be represented without loss?

a good read

I asked because I want to make sure that the buffer is large enough to avoid memory corruption.

Normally the float value is in the range +-10000.000 (in the sketch I'm working with just now).
But from time to time there is always a condition one haven't thought about that might give a float value that is larger than the char buffer. So having a large enough buffer is just to avoid memory corruption in such circumstances.

one way is to add bound checks if the float value is out of range, cap it or return an error message in the string buffer

I had it in my mind - writing a myDtostrf, which do check the float value before calling dtostrf.

If the number is too big, you can show it in scientific notation.
There is dtostre(), but there could be other functions as well.

Stochastic test result: 42 + digits after the decimal point

pow(-26.0, 27.0) converted with 9 digits after the decimal:
-160058320000000000000000000000000000000.000000000

Too bad it doesn't use scientific or engineering notation:
-1.6005832e38
-160.05832e36
You could fit any expressable number in 14 characters

Wrote this code to avoid buffer overflow:

char* smartDtostrf(const float theValue, const int width, const int decimals, char* str, const size_t strSize)
{
  size_t  digits;
  
  // Check the float value.
  digits = (size_t)log10(theValue);
  if (0.0 > theValue) {
    ++digits;
  }
  digits += 1 + decimals;

  // Buffer too small?
  if (3 > strSize - digits) {
    // Too small.
    dtostre(theValue, str, decimals, DTOSTR_UPPERCASE);
  } else {
    // Buffer size OK.
    dtostrf(theValue, width, decimals, str);
  }

  return str;
}

Comments?