Description
Stream::parseFloat()
builds a long
integer out of the digits it reads, skipping the decimal point, and then it scales it by the appropriate power of 0.1. For example, "3.14"
is interpreted as 314 × 0.12.
If the user provides too many digits after the decimal point, the integer overflows, which triggers undefined behavior and makes the function return garbage. For example, "3.141592654"
is read as 3141592654 × 0.19, but the integer overflows to −1153374642, and Stream::parseFloat()
returns −1.1534.
It could be argued that it is the user's fault, and that they should just not provide more digits than needed by the resolution of the float
data type. The users, however, are not always in control of the format of the data they want to process. I have found an unsuspecting user bitten by this issue.
Test sketch: This uses a class that makes an input Stream
out of a string, in order to not be dependent on the serial monitor.
// Stream in a C string.
class StrStream : public Stream {
public:
StrStream(const char *s_in) : s(s_in) {}
size_t write(uint8_t) { return 0; }
int available() { return strlen(s); }
int read() { return *s ? *s++ : -1; }
int peek() { return *s ? *s : -1; }
private:
const char *s;
};
void setup() {
Serial.begin(9600);
Serial.println(StrStream("3.1415927 ").parseFloat());
Serial.println(StrStream("3.14159265 ").parseFloat());
Serial.println(StrStream("3.141592654 ").parseFloat());
Serial.println(StrStream("3.1415926536 ").parseFloat());
}
void loop(){}
Output:
3.14
3.14
-1.15
0.14