[SOLVED] ESP8266 - Arduino IDE - dtostrf() conversion function problems! - Page 1
Forum section: electronics / general

author: TJ232
author_rank: Frequent Contributor
timestamp: June 15, 2015, 12:08:30 pm
content: 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:Code: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; //temporarily holds data from valsString stringVal = ""; //data on buff is copied to this stringfloat 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 buffstringVal = charVal;int strl = stringVal.length()-1;Serial.print("Conv val :"); Serial.println(charVal);Serial.print("Length: ");Serial.println(strl); //display stringSerial.print("Conv val :"); Serial.println(stringVal);Serial.println("\n");delay(3000); // delay in between reads for stability}For reference:MAX7219 Arduino IDE Driver - ESP8266

author: amyk
author_rank: Super Contributor
timestamp: June 15, 2015, 01:10:58 pm
content: 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: seehttps://lists.nongnu.org/archive/html/gcl-devel/2012-10/pdfkieTlklRzN.pdffor a good article about it.

author: TJ232
author_rank: Frequent Contributor
timestamp: June 15, 2015, 01:31:26 pm
content: Quote from: amyk on June 15, 2015, 01:10:58 pmLet's try the code below, much easier to see what's going on:Code:void setup() {// initialize serial communication at 9600 bits per second:Serial.begin(9600);}void print_float(int no, float fVal, String sVal ){char charVal; //temporarily holds data from valsString stringVal = ""; //data on buff is copied to this stringdtostrf(fVal, 8, 4, charVal); //4 is mininum width, 3 is precision; float value is copied onto buffstringVal = charVal;int strl = stringVal.length()-1;Serial.print(no); //just a order numberSerial.print(". - Expected value :"); Serial.print(sVal); //the number represented as string for referenceSerial.print(" - String Length: ");Serial.print(strl); //display lenght of the obtained string after conversionSerial.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 1print_float(2,1.0256,"1.0256"); //No 2print_float(3,1.00256,"1.0026"); //No 3Serial.println();delay(5000); // delay in between reads for stability}The result:Code:1. - Expected value :1.2560 - String Length: 5 - Conversion value in stringVal :1.25602. - Expected value :1.0256 - String Length: 4 - Conversion value in stringVal :1.2563. - Expected value :1.0026 - String Length: 3 - Conversion value in stringVal :1.26Smells like a bug for me.

author: rs20
author_rank: Super Contributor
timestamp: June 15, 2015, 01:39:00 pm
content: 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.

author: TJ232
author_rank: Frequent Contributor
timestamp: June 15, 2015, 01:45:18 pm
content: Quote from: rs20 on June 15, 2015, 01:39:00 pmYes, 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.

author: amyk
author_rank: Super Contributor
timestamp: June 15, 2015, 02:46:38 pm
content: Bug is probably in here somewhere:http://minimosd-extra.googlecode.com/svn/trunk/libraries/FastSerial/ftoa_engine.S

author: TJ232
author_rank: Frequent Contributor
timestamp: June 15, 2015, 04:06:35 pm
content: Quote from: amyk on June 15, 2015, 02:46:38 pmDon'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:Code: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 numbersif(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 itunsigned 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 beyondif(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/

author: TJ232
author_rank: Frequent Contributor
timestamp: June 15, 2015, 07:24:03 pm
content: 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:Code: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 numbersif(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 itunsigned 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 beyondif(prec > 0) {*out = '.';++out;}while(prec-- > 0) {remainder *= 10.0;if((int)remainder == 0){*out = '0';++out;}}sprintf(out, "%d", (int) remainder);return s;}