Working last days on the driver for MAX7219 8 digit display module I found that dtostrf() ESP8266 STDLIB function is not working properly for values containing “0”‘s after dot, like: 1.0256,1.00256, etc.

With the quick written test code from below and  the collaboration of the great guys from Arduino.cc Forum was able to confirm that it is a nasty BUG and is affecting ESP8266 libraries only, on Arduino (AVR) boards results confirmed from different sources as been OK.

TEST 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[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
//the number represented as string for reference 
Serial.print(". Expected value :"); Serial.print(sVal); 
//display lenght of the obtained string after conversion   Serial.print(" - String Length: ");Serial.print(strl);
//display the value as stored in stringVal 
Serial.print(" - Conversion value in stringVal :");
Serial.println(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
}

WRONG RESULT – Arduino ESP8266 build

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


TEST PASSED RESULT – Arduino (AVR) build:

1. Expected value :1.2560 - String Length: 7 - Conversion value in stringVal: 1.2560
2. Expected value :1.0256 - String Length: 7 - Conversion value in stringVal: 1.0256
3. Expected value :1.0026 - String Length: 7 - Conversion value in stringVal: 1.0026 

For ESP8266 Arduino IDE package the “dtostrf()” function is hidding in “core_esp8266_noniso.c” and is called by “#include<stdlib_noniso.h>

Original 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;
    }
    sprintf(out, "%d", (int) remainder);

    return s;
}

Going step-by-step thru the code is quite easy to find out that the remainder part is moved into integer, but on the iteration process the leading zeroes are not added into the buffer.

The fix is simple, changing the remainder add section in a way to properly add the “0”‘s to the buffer  until the remainder part become non-zero:

while(prec-- > 0) {
        remainder *= 10.0;
        if((int)remainder == 0){
                *out = '0';
                 ++out;
        }
    }

The fixed dtostrf() 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;
}

To fix your own ESP8266 Arduino IDE lib, just replace the old “dtostrf()” function  in “core_esp8266_noniso.c” with the code above.

I have posted it also on esp8266.com, maybe somebody will put the fix also in mainstream code.

All the credits for the bugfix will go this time to “Delta_G” from Arduino.cc forum, who was the quickest in posting a solution. They are also other solutions, including mine, but this one it’s the most elegant until now.

Please feel free to add yours if you want 🙂


Leave a Reply

Your email address will not be published. Required fields are marked *

Related Posts

AC Dimmer

MPDMv7.5 Arduino IDE driver example

For a more detailed description please take a look at the Part 1: MPDMv7.5 AC Dimmer devboard overview MPDMv7.5 AC Dimmer devboard is also available on Tindie Store A very simple MPDMv7.5 Arduino IDE driver Read more…

AC Dimmer

MPDMv4 – Arduino Mega Driver example

For any new orders/requests please feel free to use as usual: tech at esp8266-projects.com. MPDMv4 Boards are also available on Tindie: AC MAINS Dimmer – MPDMv4 ————————————————— DISCLAIMER ————————————————–       WARNING!! You will Read more…

AC Dimmer

MPDMv4 AC dimmer board – ArduinoIDE driver example

————————————————— DISCLAIMER ————————————————–       WARNING!! You will play with LIVE MAINS!! Deadly zone!!        If you don’t have any experience and are not qualified for working with MAINS power I will not Read more…