From a54f5132630fb2825e27d84cf4ddf3d8ee22192a Mon Sep 17 00:00:00 2001 From: Sidney Leung Date: Thu, 31 Mar 2016 11:50:12 -0700 Subject: [PATCH] Jira-575, Jira-506: Bug fixes to print float and double correctly, in a string or std output. --- cores/arduino/Print.cpp | 29 +++++++++--- cores/arduino/WString.cpp | 8 ++-- cores/arduino/stdlib_noniso.cpp | 79 ++++++++++++++++++++++++++++++--- cores/arduino/stdlib_noniso.h | 4 +- 4 files changed, 102 insertions(+), 18 deletions(-) diff --git a/cores/arduino/Print.cpp b/cores/arduino/Print.cpp index aa8c9404..2e5a5352 100644 --- a/cores/arduino/Print.cpp +++ b/cores/arduino/Print.cpp @@ -289,18 +289,29 @@ size_t Print::printLongLong(unsigned long long n, uint8_t base) { size_t Print::printFloat(double number, uint8_t digits) { size_t n = 0; + int exponent = 0; + double tmp; 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; + if (number < 0.0) { + n += print('-'); + number = -number; + } + + // Chk if integer part has more than 8 digits. + tmp = number; + while (true) { + tmp /= 10.0; + exponent++; + if (tmp < 10.0) break; } + if (exponent > 8) + number = tmp; + else + exponent = 0; // Round correctly so that print(1.999, 2) prints as "2.00" double rounding = 0.5; @@ -328,5 +339,11 @@ size_t Print::printFloat(double number, uint8_t digits) remainder -= toPrint; } + // Print the exponent portion + if (exponent) { + n += print("e+"); + n += print(exponent); + } + return n; } diff --git a/cores/arduino/WString.cpp b/cores/arduino/WString.cpp index 2f748fb8..ec03d1ae 100644 --- a/cores/arduino/WString.cpp +++ b/cores/arduino/WString.cpp @@ -135,14 +135,14 @@ String::String(float value, unsigned char decimalPlaces) { init(); char buf[33]; - *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); + *this = dtostrf(value, (33 - 1), decimalPlaces, buf); } String::String(double value, unsigned char decimalPlaces) { init(); char buf[33]; - *this = dtostrf(value, (decimalPlaces + 2), decimalPlaces, buf); + *this = dtostrf(value, (33 - 1), decimalPlaces, buf); } String::~String() @@ -363,14 +363,14 @@ unsigned char String::concat(unsigned long long num) unsigned char String::concat(float num) { char buf[20]; - char* string = dtostrf(num, 4, 2, buf); + char* string = dtostrf(num, (20 - 1), 2, buf); return concat(string, strlen(string)); } unsigned char String::concat(double num) { char buf[20]; - char* string = dtostrf(num, 4, 2, buf); + char* string = dtostrf(num, (20 - 1), 2, buf); return concat(string, strlen(string)); } diff --git a/cores/arduino/stdlib_noniso.cpp b/cores/arduino/stdlib_noniso.cpp index 874dfec4..89f4f1d5 100644 --- a/cores/arduino/stdlib_noniso.cpp +++ b/cores/arduino/stdlib_noniso.cpp @@ -147,9 +147,76 @@ char* ultoa( unsigned long val, char *string, int radix ) return string; } -char *dtostrf (double val, signed char width, unsigned char prec, char *sout) { - char fmt[20]; - sprintf(fmt, "%%%d.%df", width, prec); - sprintf(sout, fmt, val); - return sout; -} \ No newline at end of file +char * dtostrf(double number, unsigned char width, unsigned char prec, char *s) { + + if (isnan(number)) { + strcpy(s, "nan"); + return s; + } + if (isinf(number)) { + strcpy(s, "inf"); + return s; + } + + char* out = s; + int exponent = 0; + unsigned char len, expLen; + double tmp; + + // Handle negative numbers + if (number < 0.0) { + *out++ = '-'; + number = -number; + } + + // The integer portion has to be <= 8 digits. Otherwise, the + // string is in exponent format. + tmp = number; + for (;;) { + tmp /= 10.0; + exponent++; + if (tmp < 10.0) break; + } + if (exponent > 8) + number = tmp; + else + exponent = 0; + + // 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, "%ld", int_part); + + // Don't go beyond the given width of the string + len = (unsigned char)(out - s); + expLen = (exponent == 0) ? 0 : 5; // 5 places for exponent expression + if ((prec + len + expLen) > width) + prec = width - len - expLen; + + // Print the decimal point, but only if there are digits beyond + if (prec > 0) { + *out = '.'; + ++out; + prec--; + // Copy character by character to 'out' string + for (unsigned char decShift = prec; decShift > 0; decShift--) { + remainder *= 10.0; + sprintf(out, "%d", (int)remainder); + out++; + remainder -= (double)(int)remainder; + } + } + + // Print the exponent if exists + if (exponent) + sprintf(out, "e+%.3d", exponent); + + return s; +} diff --git a/cores/arduino/stdlib_noniso.h b/cores/arduino/stdlib_noniso.h index fc48a535..1d0d8d06 100644 --- a/cores/arduino/stdlib_noniso.h +++ b/cores/arduino/stdlib_noniso.h @@ -37,10 +37,10 @@ char* utoa (unsigned int val, char *s, int radix); char* ultoa (unsigned long val, char *s, int radix); -char* dtostrf (double val, signed char width, unsigned char prec, char *s); +char* dtostrf (double val, unsigned char width, unsigned char prec, char *s); #ifdef __cplusplus } // extern "C" #endif -#endif \ No newline at end of file +#endif