Turning a floating point number into a
string is not an easy task, since a floating point number is represented in
binary with a mantissa (digits part) and exponent (power multiplier).
You need some fairly complex code to perform floating point mathematics so the easiest way is to use a
pre-defined function.
Arduino floating point numbers are usually represented using 32bits
when you use 8 bit processors; This is variable of type 'float' .
For example Uno and Nano use 32bits. When you use a 32bit processor
you'll probably get 64 bits (this is type 'double').
You can easily find how much memory your processor uses with this code:
For an Arduino Uno both values will be 4 i.e. 32bits for 'float' or
'double'. For an ESP32 or Arduino Due, you'll get 4 and 8. Indicating
single and double precision are available i.e. 32bit or 64bit.
Knowing that type 'double' exists, explains the following function name:
dtostrf - Double To String Function.
When you want to turn an Arduino float to string, this is the
function that you use. The function actually accepts a value of type
'double' but, since Arduino code treats 'double' as
'float' it also converts numbers of type 'float' into strings. Type
'double' is not implemented for Arduino 8 bit processor boards.
The prototype of dtostrf is:
char *dtostrf (double value, signed char width, unsigned char prec, char *buffer)
Parameters for dtostrf | dtostrf usage |
value | The floating point value to be converted into a string. |
width | Defines the minimum space for output including possible decimal and sign. |
prec | The number of places displayed after the decimal point. |
buf |
The character buffer to store the converted float value. |
Return | The function returns a char pointer to the string buffer. |
If you use dtostrf for small numbers there are no unexpected surprises:
void setup() { char buf[6]; Serial.begin(115200); float v = 1.23; dtostrf(v,5,2,buf); Serial.print(buf); Serial.println("<") ; } void loop() { }
Here the output is:
1.23<
The left arrow shows the end of the string so it occupies 4 characters
- remember to allow for zero string terminator and possible sign output - a total of 6 bytes.
Note that a larger value (v), with more digits to the left of the decimal point, will generate more output characters in
the buffer, so make sure the buffer is large enough for your largest
input value.
One thing you might not expect is that when you write a variable in
scientific notation the string output function outputs a real value. it
will expand the scientific notation into its real number equivalent!
There is no convenient scientific notation so very large or very small
numbers will output long strings!
For instance for the following scientific notation values you will get the expanded output shown:
float v = 1.2345e17; // Code for this output: dtostrf(v,20,2,buf);
String output will be : 123450000000000000.00 [ 21 characters ]
float v= 1.2345e-12; // Code for this output: dtostrf(v,20,2,buf);
String output will be : 0.00
Note: In this case the precision parameter does limit the output string length generated.
To get a string output that means something for that value (v) use the following code: dtostrf(v,20,17,buf):
String output will be : 0.00000000000123450 [19 characters ]
The following program changes the width parameter (from 0 to 19)
while converting the floating point number 1.2345e12 to a string.
void setup() { char buf[12]; Serial.begin(115200); float v = 1.2345e12F; for (int i=0;i<20;i++) { dtostrf(v,i,2,buf); Serial.print("buf :"); Serial.print(buf); Serial.print("<"); Serial.println(i); } } void loop() { }
One thing that you might be interested in, is:
Why does the specification for dtostrf use the words minimum for the width parameter?
The following program shows what happens for changing that minimum -
effectively it adds leading spaces to fullfill the width specified.
However it only adds leading spaces if its output is less than the
width. Look at the 1st 16 values - you can see that dtostrf outputs the
string regardless of the width parameter. It means dtostrf outputs
whatever it wants to generate the output string.
This is the reason that using this function could be a problem and
cause memory leaks - you can unwittingly overwrite the next variable in
memory as there is potentially no limit to the output string length!
In actual fact there is a limit (3.4028235E+38 - you
the maximum value of a float) so 1.2345e39F triggers the limit -
so you can get 38 characters in the buffer +4 (sign etc.). The limit
is bigger for 64 bit doubles - so you could get even longer strings.
Lets assume that you have decided that your program only deals in
values ±(1e7-1) i.e. 0 to 9,999,999 and has two decimal places and a
possible sign output which gives a required string buffer size of
7=1(sign)+1(decimal point)+2(precision) = 11 characters.
You will get the following output:
buf : 1234500100000.00<0 buf : 1234500100000.00<1 buf : 1234500100000.00<2 buf : 1234500100000.00<3 buf : 1234500100000.00<4 buf : 1234500100000.00<5 buf : 1234500100000.00<6 buf : 1234500100000.00<7 buf : 1234500100000.00<8 buf : 1234500100000.00<9 buf : 1234500100000.00<10 buf : 1234500100000.00<11 buf : 1234500100000.00<12 buf : 1234500100000.00<13 buf : 1234500100000.00<14 buf : 1234500100000.00<15 buf : 1234500100000.00<16 buf : 1234500100000.00<17 buf : 1234500100000.00<18 buf : 1234500100000.00<19
Here the output string is already 16 characters long from the very
first conversion! the width parameter does not limit the output string
length. The buffer is overwritten i.e. characters are output past the
end of the length of the buffer!
Yes, in this case it is artificial as the floating point value is set
within the code itself. However, if you decode a value from an input
device e.g. a keyboard, a user could enter that value and cause a memory
overwrite! This will cause unforeseen actions in your system. The
solution is to always check the values of user input before sending the
float value to dtostrf.
A simple bit of code will check the float value before use:
You could write "OVF" (indicating overflow) to the buffer to indicate to the user that there's a problem.
If you look at the parameter specifiction, you can define a negative
width. That would make no sense as you can't have less than zero spaces
but it does not mean that.
A negative width is used to indicate alignment in the output string and it means keep the string number left aligned if there is more space than the output string length.
The following program changes the width parameter (from 0 to -19) while
converting the floating point number 1.2345e12 to a string.
void setup() { char buf[12]; Serial.begin(115200); float v = 1.2345e6F; for (int i=0;i<20;i++) { dtostrf(v,-i,2,buf); Serial.print("buf :"); Serial.print(buf); Serial.print("<"); Serial.println(i); } } void loop() { }
Now the output will be
buf :1234500.00<0 buf :1234500.00<1 buf :1234500.00<2 buf :1234500.00<3 buf :1234500.00<4 buf :1234500.00<5 buf :1234500.00<6 buf :1234500.00<7 buf :1234500.00<8 buf :1234500.00<9 buf :1234500.00<10 buf :1234500.00 <11 buf :1234500.00 <12 buf :1234500.00 <13 buf :1234500.00 <14 buf :1234500.00 <15 buf :1234500.00 <16 buf :1234500.00 <17 buf :1234500.00 <18 buf :1234500.00 <19
Notice how dtostrf again outputs the string even though the width parameter is less than the string (up to width = 10).
Written by John Main, who has a degree in Electronic Engineering.
Comments
Have your say about what you just read! Leave me a comment in the box below.
Don’t see the comments box? Log in to your Facebook account, give Facebook consent, then return to this page and refresh it.