0.4.0 printHelpers

This commit is contained in:
rob tillaart 2023-01-29 11:51:20 +01:00
parent b4704e95d9
commit 4c65b33e04
12 changed files with 324 additions and 48 deletions

View File

@ -6,6 +6,17 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/)
and this project adheres to [Semantic Versioning](http://semver.org/).
## [0.4.0] - 2023-01-28
- bump version number as fix in 0.3.1 was serious
- add **toRoman()**
- add toRoman example
- redo **toBytes()** (less RAM)
- update readme.md
- update unit test
- minor edits
----
## [0.3.1] - 2023-01-27
- fix **scieng()** itoa() => sprintf() + conditional ESP32
- add leading 0 for exponents smaller than 10, to better align columns.
@ -15,7 +26,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- update readme.md
- minor edits
## [0.3.0] - 2022-11-29
- add hex(value, digits) + bin(value, digits) 32 and 64 bit
- leading zero's - no separators - no prefix.

View File

@ -23,11 +23,12 @@ data in a way not possible in the standard print library of the Arduino.
- **toBytes()** generates KB MB GB etc.
- **hex()** generates hexadecimal output with leading zeros up to **uint64_t**.
- **bin()** generates binary output with leading zeros up to **uint64_t**.
- **toRoman()** generates a ROMAN representation of a (positive) number.
Details, see below.
#### thread safety
#### Thread safety
Note the functions of this library all share an internal buffer, so the library is
definitely **not** thread safe.
@ -85,7 +86,7 @@ thousands, millions etc which are powers of 3.
float or double to a char array.
**sci()** and **eng()** use the same underlying function called **scieng()**
as the initial code for converting was almost identical.
Although not intended to be used directly, one can use it.
Although not intended to be used directly, one may use it.
The last parameter **exponentMultiple** defines where the exponent is a multiple of.
For the **sci()** function this is 1, for the **eng()** function this is 3.
The **scieng()** function works for multiples from 1..9 for the exponent.
@ -100,18 +101,30 @@ mantissa.
representing an amount of bytes a shorter string usable for displaying.
The number of decimals is max 3
Example 3.292.528 ==> "3.140 MB"
Value ranges supported are in steps of powers of 1024.
These will all be shown in UPPERCASE so KB, MB etc.
List of prefixes:
- kilo mega giga tera (1024\^4)
- peta exa zetta yotta (1024\^8)
- xona weka vunda uda (1024\^12)
treda Byte == TDB uses 2 chars to indicate the magnitude so that would
take extra memory or more complex code.
As it is seldom used, "official" support stops with UDA.
| Unit | abbrev. | size | Unit | abbrev. | size |
|:-----------:|:---------:|:--------:|:------------:|:---------:|:--------:|
| kilobytes | KB | 1024 | zettabytes | KB | 1024^7 |
| megabytes | MB | 1024^2 | yottabytes | MB | 1024^8 |
| gigabytes | GB | 1024^3 | xonaytes | GB | 1024^9 |
| terabytes | TB | 1024^4 | wekabytes | TB | 1024^10 |
| petabytes | PB | 1024^5 | vundabytes | PB | 1024^11 |
| exabytes | EB | 1024^6 | udabytes | EB | 1024^12 |
Treda Byte is shortened as "TDB" and uses 2 chars to indicate the magnitude.
That would take extra memory or slightly more complex code.
As it is very seldom used, "official" support stops with UDA.
Should be big enough for some time.
To have some support the code uses lowercase for the next 8 levels:
Note: max uint64_t 2^64 is in the order of exa or zetta bytes.
To have some support for the really big sizes the code uses lowercase for the next 8 levels:
treda sorta rinta quexa pepta ocha nena minga luma (1024\^21 ~~ 10\^63)
To enable this patch the function in the **printHelpers.cpp** file.
#### hex() bin()
@ -138,12 +151,46 @@ Note: Data types not supported, must be cast to an supported type.
Note: There is overlap between **hex(value)** and **print64(value, HEX)**.
The latter does not produce the leading zero's or fixed length output.
----
More formatting functions might be added in the future.
#### toRoman()
https://en.wikipedia.org/wiki/Roman_numerals
A less used but well known print format are the Roman digits.
The library function **toRoman()** will convert any number from 0..100 million into a Roman number.
The numbers 1..5000 ("official" range) are the well known UPPER case characters.
- **char \* toRoman(uint32_t value)** returns Roman string.
| char | unit | notes |
|:------:|:-------|:------------|
| M | 1000 | M = Mille |
| D | 500 |
| C | 100 | C = Cent |
| L | 50 |
| X | 10 |
| V | 5 |
| I | 1 |
| N | 0 | extension |
## Shared buffer
Note: The maximum length returned is 16 characters in the "official" supported range.
4888 == MMMMDCCCLXXXVIII.
Notes:
- value == 0 => N is not part of the "official" numbers but we need it.
- values < 0 are not supported (note parameter is unsigned)
- values between 5K-10K are extended with extra M chars.
- values 10K-100M are represented with lower case characters.
This is not a standard, but it sort of works well.
- values > 100M return OVF == overflow.
- There is no special 'subtract code' for 9000 to have a clear distinction between
"official" and extended numbers.
- The number 4 is often written as IIII on clocks with Roman digits,
although IV would be (more?) correct and therefore IV is used.
## Shared print buffer
The implementation of the function all use a shared buffer to hold the
generated string.
@ -172,32 +219,26 @@ In practice a size of 22 will work for most applications.
When functions are added, the recommended minimum size might increase.
## Operation
See examples.
## Future
#### Must
- check TODO's in the code
#### Should
- documentation
- table for toBytes() - values KB, MB etc. (21 entries 3 x 7 ?) range etc.
- improve readability of the code
#### Could
- investigate separators in **hex()**
- space per 8, 4 or 2
- investigate thread safe version
- pass char buffer as parameter (breaking)
- could be the log10 pow version?
- investigate **toRoman()**
- investigate separators in **hex()**
- space per 8, 4 or 2
- investigate distance print helpers.
- feet(float cm) as 3'2" or 3-7/8 feet
- inch(float cm) as 32"

View File

@ -0,0 +1,53 @@
Arduino UNO
IDE 1.8.19
print_performance.ino
PRINTHELPERS_VERSION: 0.4.0
4
4
Mass moon M = 7.34767309E+20
Speed of light c = 2.99792458E+8
Print E = Mc^2 = 6.6037592413026551656653076E+37
print64
TIME: 22476
660375892052148224
SCI
TIME: 9236
6.603759288787841E+17
ENG
TIME: 7392
660.375976562500000E+15
dtostrf
TIME: 2464
660375890000000000.000000000000000
dtostre
TIME: 1456
6.6037589e+17
toBytes
TIME: 1980
586.531 PB
hex
TIME: 1276
092A206000000000
bin
TIME: 2464
0000100100101010001000000110000000000000000000000000000000000000
toRoman
TIME: 90164
CMXCIX
done...

View File

@ -27,7 +27,7 @@ void setup()
Serial.println();
Serial.println(" Mass moon M = 7.34767309E+20");
Serial.println("Speed of light c = 2.99792458E+8");
Serial.println(" Print E = Mc^2 = 6.6037592413026551656653076E+37\n");
Serial.println(" Print E = Mc^2 = 6.6037592413026551656653076E+37 \n");
Serial.println();
E = 660375924130265516;
@ -148,10 +148,19 @@ void setup()
delay(100);
Serial.println();
Serial.println("toRoman");
delay(100);
start = micros();
for (int i = 0; i < 1000; i++)
{
b = toRoman(i);
}
stop = micros();
Serial.print("TIME: ");
Serial.println(stop - start);
Serial.println(b);
delay(100);
Serial.println();
Serial.println("done...");

View File

@ -22,7 +22,7 @@ void setup()
Serial.println();
Serial.println(" Mass moon M = 7.34767309E+20");
Serial.println("Speed of light c = 2.99792458E+8");
Serial.println(" Print E = Mc^2 = 6.6037592413026551656653076E+37\n");
Serial.println(" Print E = Mc^2 = 6.6037592413026551656653076E+37 \n");
Serial.print(" normal print:\t");
Serial.println(E, 4);

View File

@ -0,0 +1,56 @@
// FILE: print_toRoman.ino
// AUTHOR: Rob Tillaart
// PURPOSE: demo program toRoman
#include "printHelpers.h"
uint32_t start, stop;
void setup()
{
Serial.begin(115200);
Serial.println(__FILE__);
Serial.println(sizeof(float));
Serial.println(sizeof(double));
uint8_t maxlen = 0;
for (uint32_t i = 1; i <= 5000; i++)
{
uint8_t len = strlen(toRoman(i));
if (maxlen < len)
{
maxlen = len;
Serial.print(i);
Serial.print("\t");
Serial.println(toRoman(i));
}
}
Serial.println();
Serial.print("MAXLEN: ");
Serial.println(maxlen);
Serial.println();
delay(1000);
start = micros();
char * b;
for (int i = 1; i <= 5000; i++)
{
b = toRoman(i);
}
stop = micros();
Serial.println((stop - start) / 5000.0);
Serial.println(b);
delay(1000);
Serial.println("\ndone...");
}
void loop()
{
}
// -- END OF FILE --

View File

@ -15,6 +15,8 @@ toBytes KEYWORD2
hex KEYWORD2
bin KEYWORD2
toRoman KEYWORD2
# Constants (LITERAL1)
PRINTHELPERS_VERSION LITERAL1

View File

@ -1,6 +1,6 @@
{
"name": "printHelpers",
"keywords": "Convert, int64, uint64, print, scientific, notation, toBytes",
"keywords": "Convert,int64,uint64,print,scientific,notation,toBytes,HEX,BIN,Roman",
"description": "Arduino library to help printing. int64 and uint64 support base 10 (DEC) and 16 (HEX). Scientific notation of floats.",
"authors":
[
@ -15,7 +15,7 @@
"type": "git",
"url": "https://github.com/RobTillaart/printHelpers"
},
"version": "0.3.1",
"version": "0.4.0",
"license": "MIT",
"frameworks": "arduino",
"platforms": "*",

View File

@ -1,9 +1,9 @@
name=printHelpers
version=0.3.1
version=0.4.0
author=Rob Tillaart <rob.tillaart@gmail.com>
maintainer=Rob Tillaart <rob.tillaart@gmail.com>
sentence=Arduino library to help formatting data for printing. 64 bit integers (base 10 and 16). Engineering and scientific notation.
paragraph=Supports 64 bit integers (base 10 and 16). Engineering and scientific notation. toBytes() for KB MB etc.
paragraph=Supports 64 bit integers (base 10 and 16). Engineering and scientific notation. toBytes() for KB MB, HEX and BIN, Roman numbers.
category=Other
url=https://github.com/RobTillaart/printHelpers
architectures=*

View File

@ -2,7 +2,7 @@
// FILE: printHelpers.cpp
// AUTHOR: Rob Tillaart
// DATE: 2018-01-21
// VERSION: 0.3.1
// VERSION: 0.4.0
// PUPROSE: Arduino library to help formatting for printing.
// URL: https://github.com/RobTillaart/printHelpers
@ -187,7 +187,8 @@ char * scieng(double value, uint8_t decimals, uint8_t em)
// Round correctly so that print(1.999, 2) prints as "2.00"
double rounding = 0.5;
// TODO: can we remove loop to reduce rounding errors
// TODO: can we remove loop to reduce rounding errors?
// additional loop that steps per 1000?
for (uint8_t i = 0; i < decimals; ++i)
{
rounding *= 0.1;
@ -222,7 +223,6 @@ char * scieng(double value, uint8_t decimals, uint8_t em)
// Extract decimals from the remainder one at a time
// to prevent missing leading zero's
// TODO: can we remove loop to reduce rounding errors
while (decimals-- > 0)
{
remainder *= 10;
@ -280,9 +280,13 @@ void sci(Stream &str, double value, uint8_t decimals)
// treda sorta rinta quexa pepta ocha nena minga luma (1024 ^21 ~~ 10^63)
char * toBytes(double value, uint8_t decimals)
{
static char buffer[12];
char t[] = " KMGTPEZYXWVUtsrqponml";
uint8_t i = 0; // i is index of the array == powers of 1024.
char * buffer = __printbuffer;
// to enable full range uncomment the following line
// char units[] = " KMGTPEZYXWVUtsrqponml";
char units[] = " KMGTPEZYXWVU";
uint8_t i = 0; // i is index of the unit array == powers of 1024.
if (isinf(value))
{
strcpy(buffer, "<inf>");
@ -316,16 +320,16 @@ char * toBytes(double value, uint8_t decimals)
}
// UNITS
if (i <= strlen(t))
if (i <= strlen(units))
{
if (i > 0) buffer[pos++] = ' ';
buffer[pos++] = t[i];
buffer[pos++] = units[i];
buffer[pos++] = 'B';
buffer[pos] = 0;
}
else
{
// TODO e.g. E99 B
// no units available
}
return buffer;
}
@ -351,6 +355,7 @@ char * hex(uint64_t value, uint8_t digits)
return buffer;
}
// faster than 64 bit.
char * hex(uint32_t value, uint8_t digits)
{
uint32_t val = value;
@ -375,7 +380,6 @@ char * hex(uint8_t value, uint8_t digits) { return hex((uint32_t) value, digits
// BIN
//
// always leading zero's - no prefix - no separator
char * bin(uint64_t value, uint8_t digits)
{
uint64_t val = value;
@ -390,6 +394,7 @@ char * bin(uint64_t value, uint8_t digits)
return buffer;
}
// faster than 64 bit.
char * bin(uint32_t value, uint8_t digits)
{
uint64_t val = value;
@ -408,6 +413,69 @@ char * bin(uint16_t value, uint8_t digits) { return bin((uint32_t) value, digits
char * bin(uint8_t value, uint8_t digits) { return bin((uint32_t) value, digits); };
////////////////////////////////////////////////////////////
//
// toRoman
//
// experimental
// extended with 10K units generated with the same but lower case chars.
// would expect a special char for 5000?
// need investigation.
char * toRoman(uint32_t value)
{
char * buffer = __printbuffer;
uint32_t val = value;
uint16_t n[13] = { 1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1 };
char roman[13][3] = { "M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I" };
buffer[0] = 0;
int idx = 0;
if (value == 0)
{
strcat(buffer, "N"); // NULL
return buffer;
}
if (value > 100000000UL)
{
strcat(buffer, "OVF"); // overflow
return buffer;
}
if (val >= 10000UL)
{
// 10K units
while(val >= 10000UL)
{
while (val >= (10000UL * n[idx]))
{
strcat(buffer, roman[idx]);
val -= (10000UL * n[idx]);
};
idx++;
}
// set chars to lower
for (int i = 0; i < strlen(buffer); i++)
{
buffer[i] = tolower(buffer[i]);
}
}
// Official part UPPER case letters
while(val > 0)
{
while (val >= n[idx])
{
strcat(buffer, roman[idx]);
val -= n[idx];
};
idx++;
}
return buffer;
}
// -- END OF FILE --

View File

@ -3,7 +3,7 @@
// FILE: printHelpers.h
// AUTHOR: Rob Tillaart
// DATE: 2018-01-21
// VERSION: 0.3.1
// VERSION: 0.4.0
// PUPROSE: Arduino library to help formatting for printing.
// URL: https://github.com/RobTillaart/printHelpers
@ -12,7 +12,7 @@
#include "stdlib.h"
#define PRINTHELPERS_VERSION (F("0.3.1"))
#define PRINTHELPERS_VERSION (F("0.4.0"))
// global buffer used by all functions so no static buffer in every function
@ -98,5 +98,14 @@ char * bin(uint16_t value, uint8_t digits = 16);
char * bin(uint8_t value, uint8_t digits = 8);
////////////////////////////////////////////////////////////
//
// toRoman()
//
// value should be in range 1..9999
// values 10K-100M are experimental (see readme.md)
char * toRoman(uint32_t value);
// -- END OF FILE --

View File

@ -138,6 +138,34 @@ unittest(test_bin)
}
unittest(test_toRoman_standard)
{
assertEqual(0, strcmp("I", toRoman(1)) );
assertEqual(0, strcmp("II", toRoman(2)) );
assertEqual(0, strcmp("III", toRoman(3)) );
assertEqual(0, strcmp("VIII", toRoman(8)) );
assertEqual(0, strcmp("XVIII", toRoman(18)) );
assertEqual(0, strcmp("XXVIII", toRoman(28)) );
assertEqual(0, strcmp("XXXVIII", toRoman(38)) );
assertEqual(0, strcmp("LXXXVIII", toRoman(88)) );
assertEqual(0, strcmp("CLXXXVIII", toRoman(188)) );
assertEqual(0, strcmp("CCLXXXVIII", toRoman(288)) );
assertEqual(0, strcmp("CCCLXXXVIII", toRoman(388)) );
assertEqual(0, strcmp("DCCCLXXXVIII", toRoman(888)) );
assertEqual(0, strcmp("MDCCCLXXXVIII", toRoman(1888)) );
assertEqual(0, strcmp("MMDCCCLXXXVIII", toRoman(2888)) );
assertEqual(0, strcmp("MMMDCCCLXXXVIII", toRoman(3888)) );
assertEqual(0, strcmp("MMMMDCCCLXXXVIII", toRoman(4888)) );
}
unittest(test_toRoman_extended)
{
assertEqual(0, strcmp("N", toRoman(0)) );
assertEqual(0, strcmp("OVF", toRoman(100000001UL)) );
}
unittest_main()