While playing around with ESP8266 CBDBv2 Evo DevBoard and Arduino IDE 1.6.4 found that it's returning wrong values for any float with more than 1 "0" after "." aka small values like 0.0256.
Can you please try and check also with a Arduino board for the result with the simple code below for 0.256, 0.0256, 0.00256 values:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}
// the loop routine runs over and over again forever:
void loop() {
char charVal[10]; //temporarily holds data from vals
String stringVal = ""; //data on buff is copied to this string
float fVal = 0.0256;
Serial.print("Value :");
Serial.println((float)fVal);
dtostrf(fVal, 8, 4, charVal); //4 is mininum width, 3 is precision; float value is copied onto buff
stringVal = charVal;
int strl = stringVal.length()-1;
Serial.print("Conv val :"); Serial.println(charVal);
Serial.print("Length: ");Serial.println(strl); //display string
Serial.print("Conv val :"); Serial.println(stringVal);
Serial.println("\n");
delay(3000); // delay in between reads for stability
}
For reference:
MAX7219 Arduino IDE Driver - ESP8266
What are the incorrect values?
This could be an obvious bug, but converting strings to and from floating-point, and preserving precision, is actually not so easy: see https://lists.nongnu.org/archive/html/gcl-devel/2012-10/pdfkieTlklRzN.pdf for a good article about it.
Let's try the code below, much easier to see what's going on:
void setup() {
// initialize serial communication at 9600 bits per second:
Serial.begin(9600);
}
void print_float(int no, float fVal, String sVal )
{
char charVal[12]; //temporarily holds data from vals
String stringVal = ""; //data on buff is copied to this string
dtostrf(fVal, 8, 4, charVal); //4 is mininum width, 3 is precision; float value is copied onto buff
stringVal = charVal;
int strl = stringVal.length()-1;
Serial.print(no); //just a order number
Serial.print(". - Expected value :"); Serial.print(sVal); //the number represented as string for reference
Serial.print(" - String Length: ");Serial.print(strl); //display lenght of the obtained string after conversion
Serial.print(" - Conversion value in stringVal :"); Serial.println(stringVal); //display the value as stored in stringVal
}
void loop() {
print_float(1,1.256,"1.2560"); //No 1
print_float(2,1.0256,"1.0256"); //No 2
print_float(3,1.00256,"1.0026"); //No 3
Serial.println();
delay(5000); // delay in between reads for stability
}
The result:
1. - Expected value :1.2560 - String Length: 5 - Conversion value in stringVal :1.2560
2. - Expected value :1.0256 - String Length: 4 - Conversion value in stringVal :1.256
3. - Expected value :1.0026 - String Length: 3 - Conversion value in stringVal :1.26
Smells like a bug for me.
I'm sure you already know this, but just to be sure -- this bug (and it does look like a bug to me) is a bug in libraries/compiler, not in your hardware.
I'm sure you already know this, but just to be sure -- this bug (and it does look like a bug to me) is a bug in libraries/compiler, not in your hardware.
Yes, I know, I am just asking if anybody else noticed this strange behavior.
Will be great if somebody with a Arduino board can run the code and give the result.
I don't have any around now to see if is only ESP8266 STDLIB or general Arduino problem.
Bug is probably in here somewhere:
http://minimosd-extra.googlecode.com/svn/trunk/libraries/FastSerial/ftoa_engine.S
Don't know for native Arduino, but for ESP8266 extension the "dtostrf()" monkey hides in
"core_esp8266_noniso.c" and is called by "#include<stdlib_noniso.h>"
If anybody want to have fun, a copy below:
char * dtostrf(double number, signed char width, unsigned char prec, char *s) {
if(isnan(number)) {
strcpy(s, "nan");
return s;
}
if(isinf(number)) {
strcpy(s, "inf");
return s;
}
if(number > 4294967040.0 || number < -4294967040.0) {
strcpy(s, "ovf");
return s;
}
char* out = s;
// Handle negative numbers
if(number < 0.0) {
*out = '-';
++out;
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for(uint8_t i = 0; i < prec; ++i)
rounding /= 10.0;
number += rounding;
// Extract the integer part of the number and print it
unsigned long int_part = (unsigned long) number;
double remainder = number - (double) int_part;
out += sprintf(out, "%d", int_part);
// Print the decimal point, but only if there are digits beyond
if(prec > 0) {
*out = '.';
++out;
}
while(prec-- > 0) {
remainder *= 10.0;
}
sprintf(out, "%d", (int) remainder);
return s;
}
For my specific application, the MAX7219 8 Digit Display driver workaround is in the code here:
https://www.eevblog.com/forum/microcontrollers/max7219-8-bit-led-display-module-esp8266/
MISTERY SOLVED: it is a bug in the ESP8266 dtostrf() function implementation -> "core_esp8266_noniso.c".
It was affecting ESP8266 code only. Confirmed and fixed with the guys from Arduino Forum.
Correct dtostrf() function code below:
char * dtostrf(double number, signed char width, unsigned char prec, char *s) {
if(isnan(number)) {
strcpy(s, "nan");
return s;
}
if(isinf(number)) {
strcpy(s, "inf");
return s;
}
if(number > 4294967040.0 || number < -4294967040.0) {
strcpy(s, "ovf");
return s;
}
char* out = s;
// Handle negative numbers
if(number < 0.0) {
*out = '-';
++out;
number = -number;
}
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for(uint8_t i = 0; i < prec; ++i)
rounding /= 10.0;
number += rounding;
// Extract the integer part of the number and print it
unsigned long int_part = (unsigned long) number;
double remainder = number - (double) int_part;
out += sprintf(out, "%d", int_part);
// Print the decimal point, but only if there are digits beyond
if(prec > 0) {
*out = '.';
++out;
}
while(prec-- > 0) {
remainder *= 10.0;
if((int)remainder == 0){
*out = '0';
++out;
}
}
sprintf(out, "%d", (int) remainder);
return s;
}