diff options
author | Marti Bolivar <mbolivar@leaflabs.com> | 2011-02-24 14:42:30 -0500 |
---|---|---|
committer | Marti Bolivar <mbolivar@leaflabs.com> | 2011-02-24 15:45:41 -0500 |
commit | 4e493c670aaabd8179976621d4b0bf3997fdc814 (patch) | |
tree | febfef70c77381508cbcc19976c5eaf23c35ad09 | |
parent | 7a5627be405c5f3353f58198ec4437a7e8138dff (diff) | |
download | librambutan-4e493c670aaabd8179976621d4b0bf3997fdc814.tar.gz librambutan-4e493c670aaabd8179976621d4b0bf3997fdc814.zip |
Rewrote Print class.
The old Print class couldn't print uint64 values, and featured
hand-hacked functionality better handled by snprintf(). Redid it
using snprintf(), using "[u]int[8,16,32,64]" types for more clarity,
and eliminated some private methods in favor of auxiliary functions in
Print.cpp.
Breaking compatibility with original implementation in three ways:
- Print::print(double) is now accurate to 6 digits, rather
than 2; this is consistent with the default behavior of the %f
format specifier, and if you're using floating point, it's slow
enough that you probably want the increased accuracy.
- The only bases you can print a number to are 2, 8, 10, and
16. 8, 10, and 16 already have format specifiers, and 2 is an
important special case; others complicate matters unnecessarily.
- Printing numbers in bases other than 10 treats them as
unsigned quantities (i.e., won't print '-' characters). This is
more consistent with C++'s behavior for hexadecimal and octal
literals (e.g., 0xFFFFFFFF has type uint32).
Updated HardwareSerial and USBSerial class documentation to reflect
the new behavior.
-rw-r--r-- | docs/source/lang/api/serial.rst | 35 | ||||
-rw-r--r-- | docs/source/lang/api/serialusb.rst | 35 | ||||
-rw-r--r-- | wirish/Print.cpp | 245 | ||||
-rw-r--r-- | wirish/Print.h | 87 |
4 files changed, 235 insertions, 167 deletions
diff --git a/docs/source/lang/api/serial.rst b/docs/source/lang/api/serial.rst index ca89b31..58002e3 100644 --- a/docs/source/lang/api/serial.rst +++ b/docs/source/lang/api/serial.rst @@ -113,25 +113,34 @@ means that you can use any of these functions on any of ``Serial1``, Print the argument's digits over the USART, in decimal format. -.. cpp:function:: HardwareSerial::print(long n) +.. cpp:function:: HardwareSerial::print(long long n) Print the argument's digits over the USART, in decimal format. Negative values will be prefixed with a ``'-'`` character. -.. cpp:function:: HardwareSerial::print(unsigned long n) +.. cpp:function:: HardwareSerial::print(unsigned long long n) Print the argument's digits over the USART, in decimal format. -.. cpp:function:: HardwareSerial::print(long n, int base) +.. _lang-serial-print-n-base: - Print the digits of ``n`` over the USART, in base ``base`` (which - may be between 2 and 16). The ``base`` value 2 corresponds to - binary, 8 to octal, 10 to decimal, and 16 to hexadecimal. Negative - values will be prefixed with a ``'-'`` character. +.. cpp:function:: HardwareSerial::print(int n, int base) + + Print the digits of ``n`` over the USART, in base ``base``. The + ``base`` value 2 corresponds to binary, 8 to octal, 10 to decimal, + and 16 to hexadecimal (you can also use the symbolic constants + ``BIN``, ``OCT``, ``DEC``, ``HEX``). If ``base`` is 10, negative + values will be prefixed with a ``'-'`` character (otherwise, ``n`` + will be interpreted as an unsigned quantity). + +.. cpp:function:: HardwareSerial::print(long long n, int base) + + Same behavior as the above :ref:`print(int n, int base) + <lang-serial-print-n-base>`, except with 64-bit values. .. cpp:function:: HardwareSerial::print(double n) - Print ``n``, accurate to 2 digits after the decimal point. + Print ``n``, accurate to 6 digits after the decimal point. .. _lang-serial-println: @@ -155,15 +164,19 @@ means that you can use any of these functions on any of ``Serial1``, Like ``print(n)``, followed by ``"\r\n"``. -.. cpp:function:: HardwareSerial::println(long n) +.. cpp:function:: HardwareSerial::println(long long n) Like ``print(n)``, followed by ``"\r\n"``. -.. cpp:function:: HardwareSerial::println(unsigned long n) +.. cpp:function:: HardwareSerial::println(unsigned long long n) Like ``print(n)``, followed by ``"\r\n"``. -.. cpp:function:: HardwareSerial::println(long n, int base) +.. cpp:function:: HardwareSerial::println(int n, int base) + + Like ``print(n, b)``, followed by ``"\r\n"``. + +.. cpp:function:: HardwareSerial::println(long long n, int base) Like ``print(n, b)``, followed by ``"\r\n"``. diff --git a/docs/source/lang/api/serialusb.rst b/docs/source/lang/api/serialusb.rst index 3bb8c00..87fa641 100644 --- a/docs/source/lang/api/serialusb.rst +++ b/docs/source/lang/api/serialusb.rst @@ -109,26 +109,35 @@ world!")``. Print the argument's digits over the USB connection, in decimal format. -.. cpp:function:: USBSerial::print(long n) +.. cpp:function:: USBSerial::print(long long n) Print the argument's digits over the USB connection, in decimal format. Negative values will be prefixed with a ``'-'`` character. -.. cpp:function:: USBSerial::print(unsigned long n) +.. cpp:function:: USBSerial::print(unsigned long long n) Print the argument's digits over the USB connection, in decimal format. -.. cpp:function:: USBSerial::print(long n, int base) +.. _lang-serial-print-n-base: - Print the digits of ``n`` over the USB connection, in base ``base`` - (which may be between 2 and 16). The ``base`` value 2 corresponds - to binary, 8 to octal, 10 to decimal, and 16 to hexadecimal. - Negative values will be prefixed with a ``'-'`` character. +.. cpp:function:: USBSerial::print(int n, int base) + + Print the digits of ``n`` over USB, in base ``base``. The ``base`` + value 2 corresponds to binary, 8 to octal, 10 to decimal, and 16 to + hexadecimal (you can also use the symbolic constants ``BIN``, + ``OCT``, ``DEC``, ``HEX``). If ``base`` is 10, negative values + will be prefixed with a ``'-'`` character (otherwise, ``n`` will be + interpreted as an unsigned quantity). + +.. cpp:function:: HardwareSerial::print(long long n, int base) + + Same behavior as the above :ref:`print(int n, int base) + <lang-serialusb-print-n-base>`, except with 64-bit values. .. cpp:function:: USBSerial::print(double n) - Print ``n``, accurate to 2 digits after the decimal point. + Print ``n``, accurate to 6 digits after the decimal point. .. _lang-serialusb-println: @@ -152,15 +161,19 @@ world!")``. Like ``print(n)``, followed by ``"\r\n"``. -.. cpp:function:: USBSerial::println(long n) +.. cpp:function:: USBSerial::println(long long n) Like ``print(n)``, followed by ``"\r\n"``. -.. cpp:function:: USBSerial::println(unsigned long n) +.. cpp:function:: USBSerial::println(unsigned long long n) Like ``print(n)``, followed by ``"\r\n"``. -.. cpp:function:: USBSerial::println(long n, int base) +.. cpp:function:: USBSerial::println(int n, int base) + + Like ``print(n, b)``, followed by ``"\r\n"``. + +.. cpp:function:: USBSerial::println(long long n, int base) Like ``print(n, b)``, followed by ``"\r\n"``. diff --git a/wirish/Print.cpp b/wirish/Print.cpp index c66ca61..9c52321 100644 --- a/wirish/Print.cpp +++ b/wirish/Print.cpp @@ -1,91 +1,127 @@ -/* - * Print.cpp - Base class that provides print() and println() - * Copyright (c) 2008 David A. Mellis. All right reserved. +/****************************************************************************** + * The MIT License * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. + * Copyright (c) 2011 LeafLabs, LLC. * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. * - * Modified 23 November 2006 by David A. Mellis - */ + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ -#include "wirish.h" #include "Print.h" -//------------------------------ Public Methods ------------------------------- +#include <cstdio> +#include <climits> +#include <cstring> + +// We'll allocate character buffers of size INT_BUF_SIZE to hold the +// string representations of numbers; this value ensures that they're +// big enough to accomodate the biggest integral value + null byte. +// +// E.g., consider -(2^63-1) = -9223372036854775807, which is 20 +// characters long, including the minus sign. The other edge cases +// are similar. +// +// (Nonetheless, use snprintf everywhere, just in case of error). +#define INT_BUF_SIZE 21 + +// An IEEE-754 double buys you about 16 digits of precision; there's +// the possibility of minus signs, a decimal point, 'e+'/'e-', etc. +// While the Right Thing is to follow Steele and White, I'm just going +// to double what I consider a safe number of bytes and hope for the +// best. +#define DOUBLE_BUF_SIZE 40 + +static void fillBase(char *buf, int buf_size, int64 n, + uint8 n_real_bits, int base); +static void fillBinary(char *buf, int64 n, int start_bit); +static char baseToFmtSpec(int base); void Print::write(const char *str) { - while (*str) - write(*str++); + for (const char *c = str; *c != '\0'; c++) { + write(*c); + } } void Print::write(void *buffer, uint32 size) { - uint8 *ch = (uint8*)buffer; - while (size--) { - write(*ch++); + for (uint32 i = 0; i < size; i++) { + write(*((uint8*)buffer + i)); } } -void Print::print(uint8 b) { - this->write(b); -} - void Print::print(char c) { - print((byte) c); + print((uint8) c); } void Print::print(const char str[]) { write(str); } -void Print::print(int n) { - print((long) n); +void Print::print(uint8 b) { + write(b); } -void Print::print(unsigned int n) { - print((unsigned long) n); +void Print::print(int32 n) { + print(n, DEC); } -void Print::print(long n) { - if (n < 0) { - print('-'); - n = -n; - } - printNumber(n, 10); +void Print::print(uint32 n) { + print((uint64) n); } -void Print::print(unsigned long n) { - printNumber(n, 10); +void Print::print(int64 n) { + print(n, DEC); } -void Print::print(long n, int base) { - if (base == 0) { - print((char) n); - } else if (base == 10) { - print(n); - } else { - printNumber(n, base); - } +void Print::print(uint64 n) { + char buf[INT_BUF_SIZE]; + snprintf(buf, INT_BUF_SIZE, "%llu", n); + write(buf); +} + +void Print::print(int32 n, int base) { + // Worst case: sign bit set && base == BIN: 32 bytes for digits + + // 1 null (base == BIN means no minus sign). + char buf[33]; + fillBase(buf, sizeof(buf), (int64)n, 32, base); + write(buf); +} + +void Print::print(int64 n, int base) { + // As above, but now 64 bytes for bits + 1 null + char buf[65]; + fillBase(buf, sizeof(buf), n, 64, base); + write(buf); } void Print::print(double n) { - printFloat(n, 2); + char buf[DOUBLE_BUF_SIZE]; + // This breaks strict compliance with the Arduino library behavior + // (which is equivalent to using "%.2f"), but that's really not + // enough. According to Stroustrup, "%f" without precision is + // equivalent to ".6f", which is much better. + snprintf(buf, DOUBLE_BUF_SIZE, "%f", n); + write(buf); } void Print::println(void) { - print('\r'); - print('\n'); + print("\r\n"); } void Print::println(char c) { @@ -103,27 +139,32 @@ void Print::println(uint8 b) { println(); } -void Print::println(int n) { +void Print::println(int32 n) { print(n); println(); } -void Print::println(unsigned int n) { +void Print::println(uint32 n) { print(n); println(); } -void Print::println(long n) { +void Print::println(int64 n) { print(n); println(); } -void Print::println(unsigned long n) { +void Print::println(uint64 n) { print(n); println(); } -void Print::println(long n, int base) { +void Print::println(int32 n, int base) { + print(n, base); + println(); +} + +void Print::println(int64 n, int base) { print(n, base); println(); } @@ -133,58 +174,54 @@ void Print::println(double n) { println(); } -//------------------------------ Private Methods ------------------------------ +// -- Auxiliary functions ----------------------------------------------------- -void Print::printNumber(unsigned long n, uint8 base) { - unsigned char buf[8 * sizeof(long)]; // Assumes 8-bit chars. - unsigned long i = 0; - - if (n == 0) { - print('0'); - return; - } - - while (n > 0) { - buf[i++] = n % base; - n /= base; +static void fillBase(char *buf, int buf_size, int64 n, + uint8 n_real_bits, int base) { + if (base == BIN) { + fillBinary(buf, n, n_real_bits - 1); + } else { + char spec = baseToFmtSpec(base); + char fmt[5]; + + if (base == BYTE) + n = (uint8)n; + + if (n_real_bits == 32) { + snprintf(fmt, sizeof(fmt), "%%l%c", spec); + snprintf(buf, buf_size, fmt, (int32)n); + } else { + snprintf(fmt, sizeof(fmt), "%%ll%c", spec); + snprintf(buf, buf_size, fmt, n); + } } - - for (; i > 0; i--) - print((char) (buf[i - 1] < 10 ? - '0' + buf[i - 1] : - 'A' + buf[i - 1] - 10)); } -void Print::printFloat(double number, uint8 digits) { - // Handle negative numbers - if (number < 0.0) { - print('-'); - number = -number; +// Assumes sizeof(buf) > start_bit. +static void fillBinary(char *buf, int64 n, int start_bit) { + int b = 0; // position in buf + int i = start_bit; // position in n's bits + while(!(n & (1 << i))) { + i--; } - - // Round correctly so that print(1.999, 2) prints as "2.00" - double rounding = 0.5; - 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; - print(int_part); - - // Print the decimal point, but only if there are digits beyond - if (digits > 0) { - print("."); + for(; i >= 0; i--) { + buf[b++] = '0' + ((n >> i) & 0x1); } - - // Extract digits from the remainder one at a time - while (digits-- > 0) { - remainder *= 10.0; - int toPrint = int(remainder); - print(toPrint); - remainder -= toPrint; + buf[b] = '\0'; +} + +static char baseToFmtSpec(int base) { + switch (base) { + case DEC: + return 'd'; + case HEX: + return 'x'; + case OCT: + return 'o'; + case BYTE: + return 'd'; + default: + // Shouldn't happen, but give a sensible default + return 'd'; } } diff --git a/wirish/Print.h b/wirish/Print.h index dc21183..4527948 100644 --- a/wirish/Print.h +++ b/wirish/Print.h @@ -1,62 +1,67 @@ -/* - * Print.h - Base class that provides print() and println() - * Copyright (c) 2008 David A. Mellis. All right reserved. +/****************************************************************************** + * The MIT License * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 of - * the License, or (at your option) any later version. + * Copyright (c) 2011 LeafLabs, LLC. * - * This library is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. + * Permission is hereby granted, free of charge, to any person + * obtaining a copy of this software and associated documentation + * files (the "Software"), to deal in the Software without + * restriction, including without limitation the rights to use, copy, + * modify, merge, publish, distribute, sublicense, and/or sell copies + * of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - * 02110-1301 USA. - */ + * The above copyright notice and this permission notice shall be + * included in all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS + * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + *****************************************************************************/ -#ifndef Print_h -#define Print_h +#ifndef _PRINT_H_ +#define _PRINT_H_ -#include <libmaple_types.h> -#include <stdio.h> // for size_t +#include "libmaple_types.h" -#define DEC 10 -#define HEX 16 -#define OCT 8 -#define BIN 2 -#define BYTE 0 +#define DEC 10 +#define HEX 16 +#define OCT 8 +#define BIN 2 +#define BYTE 0 // yuck -class Print -{ - private: - void printNumber(unsigned long, uint8); - void printFloat(double, uint8); +class Print { public: virtual void write(uint8) = 0; virtual void write(const char *str); - virtual void write(void *, uint32); + virtual void write(void *buf, uint32 size); + void print(char); void print(const char[]); void print(uint8); - void print(int); - void print(unsigned int); - void print(long); - void print(unsigned long); - void print(long, int); + void print(int32); + void print(uint32); + void print(int64); + void print(uint64); + void print(int32, int); + void print(int64, int); void print(double); + void println(void); void println(char); void println(const char[]); void println(uint8); - void println(int); - void println(unsigned int); - void println(long); - void println(unsigned long); - void println(long, int); + void println(int32); + void println(uint32); + void println(int64); + void println(uint64); + void println(int32, int); + void println(int64, int); void println(double); }; |