In this post we are going to see how to convert a text string to a number, either integer or float, in a microprocessor like Arduino.
This is a request that we see frequently on Internet pages and forums, and about which there is sometimes confusion because there is more than one way to perform the conversion, each with its advantages and disadvantages.
In this post we will see the main ways to convert a string to an integer or float, and when it is convenient to use one or the other.
The DEBUG functions are only for visualizing the results. If you use these codes, you can remove the parts related to their definition and use.
Conversion with the String class
The first option we are going to see is to use the String class, which as we know is a wrapper around a dynamic char array that is included in the Arduino libraries.
The String class has the toInt() and toFloat() functions that convert, respectively, the text string to an integer or float.
So, the following code shows the conversion from String to Int,
#define DEBUG(a) Serial.println(a);
String text = "-12345";
void setup()
{
Serial.begin(9600);
long value;
value = text.toInt();
DEBUG(value);
}
void loop()
{
}
While the conversion from String to Float is as follows,
#define DEBUG(a) Serial.println(a);
String text = "-123.45";
void setup()
{
Serial.begin(9600);
float value;
value = text.toFloat();
DEBUG(value);
}
void loop()
{
}
Conversion through the String class is the recommended option whenever we do not have a strong reason not to use it. It is easy to use, efficient, and the Sting library is light enough that its use is not penalized.
The main disadvantage is the absence of control over the process. For example, if the string contains invalid characters the result can be unexpected (and generally incorrect).
Although in most cases this is not a problem, in cases where we have to deal with these situations, we may prefer to perform the conversion “manually” (see the Naive method below)
Conversion with char array
If for some reason we cannot or do not want to use the String class, another option is to perform the conversion directly from a char array.
For this we have the atol and atof functions that respectively convert a char array to an integer or float.
The example for integers is as follows,
#define DEBUG(a) Serial.println(a);
char* text = "-12345";
void setup()
{
Serial.begin(9600);
long value;
value = atol(text);
DEBUG(value);
}
void loop()
{
}
Which in the case of floating point numbers looks like this,
#define DEBUG(a) Serial.println(a);
char* text= "-123.45";
void setup()
{
Serial.begin(9600);
long value;
value = atof(text);
DEBUG(value);
}
void loop()
{
}
The efficiency and behavior is similar to that obtained using the String class since, internally, the String class uses the atol and atof functions to perform the conversion.
In this case, we have the disadvantage of having to work with a char array which, except in very specific cases, will normally be more cumbersome than using the String class.
Therefore, except in a few exceptions, we will usually prefer the functions previously seen in the String class.
Conversion with Naive method
The last case we are going to see is the so-called “naive method”, which is nothing more than a sophisticated way of calling “doing it by hand”.
The following example shows the conversion process for integers, where we process a char array until we find a character that is not a digit between 0 and 9. Previously we have checked if it is a negative number.
#define DEBUG(a) Serial.println(a);
char *text = "-12345";
void setup() {
Serial.begin(9600);
long value;
value = naiveToInt(text);
DEBUG(value);
}
void loop()
{
}
long naiveToInt(const char *charArray) {
long data = 0;
bool isNegative = false;
if (*charArray == '-')
{
isNegative = true;
++charArray;
}
while (*charArray >= '0' && *charArray <= '9')
{
data = (data * 10) + (*charArray - '0');
++charArray;
}
return isNegative ? -data : data;
}
The case for a floating point number is somewhat more complicated since, in addition to detecting the negative symbol, we must detect the decimal separator (in the example ’.’ or ’,’). In this way the conversion is done in two stages, a first one for the integer part before detecting the separator, and a second one for the integer part after detecting the separator.
#define DEBUG(a) Serial.println(a);
char *text = "-123.45";
void setup() {
Serial.begin(9600);
float value;
value = naiveToFloat(text);
DEBUG(value);
}
void loop()
{
}
float naiveToFloat(const char *charArray)
{
long dataReal = 0;
long dataDecimal = 0;
long dataPow = 1;
bool isNegative = false;
if (*charArray == '-')
{
isNegative = true;
++charArray;
}
while ((*charArray >= '0' && *charArray <= '9'))
{
dataReal = (dataReal * 10) + (*charArray - '0');
++charArray;
}
if (*charArray == '.' || *charArray == ',')
{
++charArray;
while ((*charArray >= '0' && *charArray <= '9'))
{
dataDecimal = (dataDecimal * 10) + (*charArray - '0');
dataPow *= 10;
++charArray;
}
}
float data = (float)dataReal + (float)dataDecimal / dataPow;
return isNegative ? -data : data;
}
The performance of the process is similar to the atol and atof function and, by extension, to the functions of the String class. This is because this implementation is similar to that used internally by the atol and atof functions.
The main disadvantage is, logically, having to add the code instead of using it comfortably from existing functions. However, the ease of use is similar.
However, in this case we have the advantage of controlling the entire process. As an example, in the example we have made it possible to accept ’.’ and ’,’ as decimal separators, something that we cannot do with the standard processes.
With simplicity we can modify the code to have a different type of behavior, such as reacting differently to certain characters, processing a file separated by commas, etc.
Therefore, we will use this method mainly when we have special conditions that prevent us from using standard functions, and we have to customize the conversion process.
Download the code
All the code in this post is available for download on Github.