diff options
author | Marti Bolivar <mbolivar@leaflabs.com> | 2011-04-12 01:13:52 -0400 |
---|---|---|
committer | Marti Bolivar <mbolivar@leaflabs.com> | 2011-04-12 17:44:27 -0400 |
commit | fc3126378e0c13b03b3c5dd886bebb2014878717 (patch) | |
tree | 14465f06a7229beed78381073dadd4441c8c8538 | |
parent | cdd7bb7a13442160fea29e10ddc61402f56f4bd1 (diff) | |
download | librambutan-fc3126378e0c13b03b3c5dd886bebb2014878717.tar.gz librambutan-fc3126378e0c13b03b3c5dd886bebb2014878717.zip |
Compromise Print implementation.
The users really hated the code size requirements for an snprintf()-based
Print implementation, but I really hated how bad the old implementation was.
Revised version fixes bugs related to printing 64-bit values and has some
improved behavior when it comes to printing doubles. Now, instead of
happily printing garbage values when large doubles are printed, we try
printing "<large double>" or "-<large double>" (depending on sign) when
the argument is too big for the old strategy to accommodate.
-rw-r--r-- | wirish/Print.cpp | 119 | ||||
-rw-r--r-- | wirish/Print.h | 40 |
2 files changed, 117 insertions, 42 deletions
diff --git a/wirish/Print.cpp b/wirish/Print.cpp index c66ca61..3d7e836 100644 --- a/wirish/Print.cpp +++ b/wirish/Print.cpp @@ -18,16 +18,38 @@ * 02110-1301 USA * * Modified 23 November 2006 by David A. Mellis + * Modified 12 April 2011 by Marti Bolivar <mbolivar@leaflabs.com> */ +#include <math.h> +#include <limits.h> + +#ifndef LLONG_MAX +/* + * Note: + * + * At time of writing (12 April 2011), the limits.h that came with the + * newlib we distributed didn't include LLONG_MAX. Because we're + * staying away from using templates (see /notes/coding_standard.rst, + * "Language Features and Compiler Extensions"), this value was + * copy-pasted from a println() of the value + * + * std::numeric_limits<long long>::max(). + */ +#define LLONG_MAX 9223372036854775807LL +#endif + #include "wirish.h" #include "Print.h" -//------------------------------ Public Methods ------------------------------- +/* + * Public methods + */ void Print::write(const char *str) { - while (*str) + while (*str) { write(*str++); + } } void Print::write(void *buffer, uint32 size) { @@ -42,7 +64,7 @@ void Print::print(uint8 b) { } void Print::print(char c) { - print((byte) c); + print((byte)c); } void Print::print(const char str[]) { @@ -50,14 +72,22 @@ void Print::print(const char str[]) { } void Print::print(int n) { - print((long) n); + print((long long)n); } void Print::print(unsigned int n) { - print((unsigned long) n); + print((unsigned long long)n); } void Print::print(long n) { + print((long long)n); +} + +void Print::print(unsigned long n) { + print((unsigned long long)n); +} + +void Print::print(long long n) { if (n < 0) { print('-'); n = -n; @@ -65,13 +95,13 @@ void Print::print(long n) { printNumber(n, 10); } -void Print::print(unsigned long n) { +void Print::print(unsigned long long n) { printNumber(n, 10); } -void Print::print(long n, int base) { +void Print::print(unsigned long long n, int base) { if (base == 0) { - print((char) n); + print((char)n); } else if (base == 10) { print(n); } else { @@ -114,16 +144,26 @@ void Print::println(unsigned int n) { } void Print::println(long n) { - print(n); + print((long long)n); println(); } void Print::println(unsigned long n) { + print((unsigned long long)n); + println(); +} + +void Print::println(long long n) { print(n); println(); } -void Print::println(long n, int base) { +void Print::println(unsigned long long n) { + print(n); + println(); +} + +void Print::println(unsigned long long n, int base) { print(n, base); println(); } @@ -133,10 +173,12 @@ void Print::println(double n) { println(); } -//------------------------------ Private Methods ------------------------------ +/* + * Private methods + */ -void Print::printNumber(unsigned long n, uint8 base) { - unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars. +void Print::printNumber(unsigned long long n, uint8 base) { + unsigned char buf[CHAR_BIT * sizeof(long long)]; unsigned long i = 0; if (n == 0) { @@ -149,30 +191,57 @@ void Print::printNumber(unsigned long n, uint8 base) { n /= base; } - for (; i > 0; i--) - print((char) (buf[i - 1] < 10 ? - '0' + buf[i - 1] : - 'A' + buf[i - 1] - 10)); + for (; i > 0; i--) { + print((char)(buf[i - 1] < 10 ? + '0' + buf[i - 1] : + 'A' + buf[i - 1] - 10)); + } } +/* According to snprintf(), + * + * nextafter((double)numeric_limits<long long>::max(), 0.0) ~= 9.22337e+18 + * + * This slightly smaller value was picked semi-arbitrarily. */ +#define LARGE_DOUBLE_TRESHOLD (9.1e18) + +/* THIS FUNCTION SHOULDN'T BE USED IF YOU NEED ACCURATE RESULTS. + * + * This implementation is meant to be simple and not occupy too much + * code size. However, printing floating point values accurately is a + * subtle task, best left to a well-tested library function. + * + * See Steele and White 2003 for more details: + * + * http://kurtstephens.com/files/p372-steele.pdf + */ void Print::printFloat(double number, uint8 digits) { + // Hackish fail-fast behavior for large-magnitude doubles + if (abs(number) >= LARGE_DOUBLE_TRESHOLD) { + if (number < 0.0) { + print('-'); + } + print("<large double>"); + return; + } + // Handle negative numbers if (number < 0.0) { print('-'); number = -number; } - // Round correctly so that print(1.999, 2) prints as "2.00" + // Simplistic rounding strategy so that e.g. print(1.999, 2) + // prints as "2.00" double rounding = 0.5; - for (uint8 i=0; i<digits; ++i) { + for (uint8 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; + long long int_part = (long long)number; + double remainder = number - int_part; print(int_part); // Print the decimal point, but only if there are digits beyond @@ -183,8 +252,8 @@ void Print::printFloat(double number, uint8 digits) { // Extract digits from the remainder one at a time while (digits-- > 0) { remainder *= 10.0; - int toPrint = int(remainder); - print(toPrint); - remainder -= toPrint; + int to_print = (int)remainder; + print(to_print); + remainder -= to_print; } } diff --git a/wirish/Print.h b/wirish/Print.h index dc21183..a4a93bf 100644 --- a/wirish/Print.h +++ b/wirish/Print.h @@ -16,29 +16,28 @@ * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA. + * + * Modified 12 April 2011 by Marti Bolivar <mbolivar@leaflabs.com> */ -#ifndef Print_h -#define Print_h +#ifndef _PRINT_H_ +#define _PRINT_H_ #include <libmaple_types.h> -#include <stdio.h> // for size_t -#define DEC 10 -#define HEX 16 -#define OCT 8 -#define BIN 2 -#define BYTE 0 +enum { + BYTE = 0, + BIN = 2, + OCT = 8, + DEC = 10, + HEX = 16 +}; -class Print -{ - private: - void printNumber(unsigned long, uint8); - void printFloat(double, uint8); - public: +class Print { +public: virtual void write(uint8) = 0; virtual void write(const char *str); - virtual void write(void *, uint32); + virtual void write(void*, uint32); void print(char); void print(const char[]); void print(uint8); @@ -46,7 +45,9 @@ class Print void print(unsigned int); void print(long); void print(unsigned long); - void print(long, int); + void print(long long); + void print(unsigned long long); + void print(unsigned long long, int); void print(double); void println(void); void println(char); @@ -56,8 +57,13 @@ class Print void println(unsigned int); void println(long); void println(unsigned long); - void println(long, int); + void println(long long); + void println(unsigned long long); + void println(unsigned long long, int); void println(double); +private: + void printNumber(unsigned long long, uint8); + void printFloat(double, uint8); }; #endif |