Skip to content

Commit

Permalink
Use sprintf to output floats in Print/dtostrf
Browse files Browse the repository at this point in the history
Fixes esp8266#7043

Two slightly different custom routines were implemented by hand in
dtostrf (an AVR-lib non-ISO function) and Print.  This resulted in
inconsistent output of float/double vars when rounding was needed.

Replace them all with a call to sprintf(), removing the duplicated, not
quite correct code.

Print(String(float)) and Print(float) now generate the same output.
  • Loading branch information
earlephilhower committed Feb 9, 2020
1 parent 56b90a2 commit f762721
Show file tree
Hide file tree
Showing 3 changed files with 8 additions and 112 deletions.
45 changes: 2 additions & 43 deletions cores/esp8266/Print.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -268,47 +268,6 @@ size_t Print::printNumber(unsigned long n, uint8_t base) {
}

size_t Print::printFloat(double number, uint8_t digits) {
size_t n = 0;

if(isnan(number))
return print("nan");
if(isinf(number))
return print("inf");
if(number > 4294967040.0)
return print("ovf"); // constant determined empirically
if(number < -4294967040.0)
return print("ovf"); // constant determined empirically

// Handle negative numbers
if(number < 0.0) {
n += print('-');
number = -number;
}

// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
for(uint8_t i = 0; i < digits; ++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;
n += print(int_part);

// Print the decimal point, but only if there are digits beyond
if(digits > 0) {
n += print(".");
}

// Extract digits from the remainder one at a time
while(digits-- > 0) {
remainder *= 10.0;
int toPrint = int(remainder);
n += print(toPrint);
remainder -= toPrint;
}

return n;
char buf[40];
return write(dtostrf(number, 0, digits, buf));
}
2 changes: 2 additions & 0 deletions cores/esp8266/Print.h
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@
#include "WString.h"
#include "Printable.h"

#include "stdlib_noniso.h"

#define DEC 10
#define HEX 16
#define OCT 8
Expand Down
73 changes: 4 additions & 69 deletions cores/esp8266/core_esp8266_noniso.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
Expand All @@ -40,75 +41,9 @@ char* ultoa(unsigned long value, char* result, int base) {
}

char * dtostrf(double number, signed char width, unsigned char prec, char *s) {
bool negative = false;

if (isnan(number)) {
strcpy(s, "nan");
return s;
}
if (isinf(number)) {
strcpy(s, "inf");
return s;
}

char* out = s;

int fillme = width; // how many cells to fill for the integer part
if (prec > 0) {
fillme -= (prec+1);
}

// Handle negative numbers
if (number < 0.0) {
negative = true;
fillme--;
number = -number;
}

// Round correctly so that print(1.999, 2) prints as "2.00"
// I optimized out most of the divisions
double rounding = 2.0;
for (uint8_t i = 0; i < prec; ++i)
rounding *= 10.0;
rounding = 1.0 / rounding;

number += rounding;

// Figure out how big our number really is
double tenpow = 1.0;
int digitcount = 1;
while (number >= 10.0 * tenpow) {
tenpow *= 10.0;
digitcount++;
}

number /= tenpow;
fillme -= digitcount;

// Pad unused cells with spaces
while (fillme-- > 0) {
*out++ = ' ';
}

// Handle negative sign
if (negative) *out++ = '-';

// Print the digits, and if necessary, the decimal point
digitcount += prec;
int8_t digit = 0;
while (digitcount-- > 0) {
digit = (int8_t)number;
if (digit > 9) digit = 9; // insurance
*out++ = (char)('0' | digit);
if ((digitcount == prec) && (prec > 0)) {
*out++ = '.';
}
number -= digit;
number *= 10.0;
}

// make sure the string is terminated
*out = 0;
char fmt[32];
sprintf(fmt, "%%%d.%df", width, prec);
sprintf(s, fmt, number);
return s;
}

Expand Down

0 comments on commit f762721

Please sign in to comment.