refactor(xod-arduino, workspace): replace platform-dependent dtostrf with dtoa and move shared formatters.cpp into cpplib directory and rename it

This commit is contained in:
Kirill Shumilov
2018-11-14 13:39:57 +03:00
parent 3bad189261
commit 602777c43e
23 changed files with 1101 additions and 462 deletions

View File

@@ -1,3 +1,14 @@
/**
* This file makes Catch2 show failed tests properly for the XString type.
* E.G.
* "some" == "something"
* instead of
* {?} == {?}
*
* Catch2 string conversion docs:
* https://github.com/catchorg/Catch2/blob/master/docs/tostring.md
*/
template <typename T>
struct XodStringMaker {
static std::string convert(T value) {

View File

@@ -0,0 +1,161 @@
/*=============================================================================
*
*
* Format Numbers
*
*
=============================================================================*/
/**
* Provide `formatNumber` cross-platform number to string converter function.
*
* Taken from here:
* https://github.com/client9/stringencoders/blob/master/src/modp_numtoa.c
* Original function name: `modp_dtoa2`.
*
* Modified:
* - `isnan` instead of tricky comparing and return "NaN"
* - handle Infinity values and return "Inf" or "-Inf"
* - return `OVF` and `-OVF` for numbers bigger than max possible, instead of using `sprintf`
* - use `Number` instead of double
* - if negative number rounds to zero, return just "0" instead of "-0"
*
* This is a replacement of `dtostrf`.
*/
#ifndef XOD_FORMAT_NUMBER_H
#define XOD_FORMAT_NUMBER_H
namespace xod {
/**
* Powers of 10
* 10^0 to 10^9
*/
static const Number powers_of_10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000,
10000000, 100000000, 1000000000 };
static void strreverse(char* begin, char* end) {
char aux;
while (end > begin)
aux = *end, *end-- = *begin, *begin++ = aux;
};
size_t formatNumber(Number value, int prec, char* str) {
if (isnan(value)) {
strcpy(str, "NaN");
return (size_t)3;
}
if (isinf(value)) {
bool isNegative = value < 0;
strcpy(str, isNegative ? "-Inf" : "Inf");
return (size_t)isNegative ? 4 : 3;
}
/* if input is larger than thres_max return "OVF" */
const Number thres_max = (Number)(0x7FFFFFFF);
Number diff = 0.0;
char* wstr = str;
if (prec < 0) {
prec = 0;
} else if (prec > 9) {
/* precision of >= 10 can lead to overflow errors */
prec = 9;
}
/* we'll work in positive values and deal with the
negative sign issue later */
int neg = 0;
if (value < 0) {
neg = 1;
value = -value;
}
int whole = (int)value;
Number tmp = (value - whole) * powers_of_10[prec];
uint32_t frac = (uint32_t)(tmp);
diff = tmp - frac;
if (diff > 0.5) {
++frac;
/* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
} else if (diff == 0.5 && prec > 0 && (frac & 1)) {
/* if halfway, round up if odd, OR
if last digit is 0. That last part is strange */
++frac;
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
} else if (diff == 0.5 && prec == 0 && (whole & 1)) {
++frac;
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
}
if (value > thres_max) {
if (neg) {
strcpy(str, "-OVF");
return (size_t)4;
}
strcpy(str, "OVF");
return (size_t)3;
}
int has_decimal = 0;
int count = prec;
bool notzero = frac > 0;
/* Remove ending zeros */
if (prec > 0) {
while (count > 0 && ((frac % 10) == 0)) {
count--;
frac /= 10;
}
}
while (count > 0) {
--count;
*wstr++ = (char)(48 + (frac % 10));
frac /= 10;
has_decimal = 1;
}
if (frac > 0) {
++whole;
}
/* add decimal */
if (has_decimal) {
*wstr++ = '.';
}
notzero = notzero || whole > 0;
/* do whole part
* Take care of sign conversion
* Number is reversed.
*/
do
*wstr++ = (char)(48 + (whole % 10));
while (whole /= 10);
if (neg && notzero) {
*wstr++ = '-';
}
*wstr = '\0';
strreverse(str, wstr - 1);
return (size_t)(wstr - str);
}
} // namespace xod
#endif

View File

@@ -42,79 +42,7 @@
# define pgm_read_ptr(addr) (*(const void **)(addr))
#endif
//----------------------------------------------------------------------------
// Compatibilities
//----------------------------------------------------------------------------
#if !defined(ARDUINO_ARCH_AVR) && !defined(__DTOSTRF_H_)
/*
* Provide dtostrf function for non-AVR platforms. Although many platforms
* provide a stub many others do not. And the stub is based on `sprintf`
* which doesnt work with floating point formatters on some platforms
* (e.g. Arduino M0).
*
* This is an implementation based on `fcvt` standard function. Taken here:
* https://forum.arduino.cc/index.php?topic=368720.msg2542614#msg2542614
*/
char *dtostrf(double val, int width, unsigned int prec, char *sout) {
int decpt, sign, reqd, pad;
const char *s, *e;
char *p;
s = fcvt(val, prec, &decpt, &sign);
if (prec == 0 && decpt == 0) {
s = (*s < '5') ? "0" : "1";
reqd = 1;
} else {
reqd = strlen(s);
if (reqd > decpt) reqd++;
if (decpt == 0) reqd++;
}
if (sign) reqd++;
p = sout;
e = p + reqd;
pad = width - reqd;
if (pad > 0) {
e += pad;
while (pad-- > 0) *p++ = ' ';
}
if (sign) *p++ = '-';
if (decpt <= 0 && prec > 0) {
*p++ = '0';
*p++ = '.';
e++;
while ( decpt < 0 ) {
decpt++;
*p++ = '0';
}
}
while (p < e) {
*p++ = *s++;
if (p == e) break;
if (--decpt == 0) *p++ = '.';
}
if (width < 0) {
pad = (reqd + width) * -1;
while (pad-- > 0) *p++ = ' ';
}
*p = 0;
return sout;
}
#endif
namespace xod {
//----------------------------------------------------------------------------
// Type definitions
//----------------------------------------------------------------------------
#if __SIZEOF_FLOAT__ == 4
typedef float Number;
#else
typedef double Number;
#endif
typedef bool Logic;
typedef unsigned long TimeMs;
typedef uint8_t DirtyFlags;
//----------------------------------------------------------------------------
// Global variables
//----------------------------------------------------------------------------

View File

@@ -0,0 +1,17 @@
/*=============================================================================
*
*
* Basic XOD types
*
*
=============================================================================*/
namespace xod {
#if __SIZEOF_FLOAT__ == 4
typedef float Number;
#else
typedef double Number;
#endif
typedef bool Logic;
typedef unsigned long TimeMs;
typedef uint8_t DirtyFlags;
} // namespace xod

View File

@@ -14,6 +14,8 @@ import programTpl from '../platform/program.tpl.cpp';
import preambleH from '../platform/preamble.h';
import listViewsH from '../platform/listViews.h';
import listFuncsH from '../platform/listFuncs.h';
import typesH from '../platform/types.h';
import formatNumberH from '../platform/formatNumber.h';
import uartH from '../platform/uart.h';
import memoryH from '../platform/memory.h';
import stlH from '../platform/stl.h';
@@ -306,10 +308,12 @@ export const renderProject = def(
preambleH,
config,
stlH,
typesH,
listViewsH,
memoryH,
uartH,
omitLocalIncludes(listFuncsH),
formatNumberH,
runtimeCpp,
impls,
program,

View File

@@ -0,0 +1,60 @@
#include "catch.hpp"
#include "../platform/types.h"
#include "../platform/listViews.h"
#include "../platform/listFuncs.h"
#include "../platform/formatNumber.h"
#include <XStringFormat.inl>
xod::XStringCString convertAndTest(xod::Number val, unsigned int prec, char* sout) {
xod::formatNumber(val, prec, sout);
return xod::XStringCString(sout);
}
TEST_CASE("Number to XString conversion", "[list]") {
char str [16];
SECTION("Integer numbers") {
REQUIRE(convertAndTest((xod::Number) 0, 0, str) == xod::XStringCString("0"));
REQUIRE(convertAndTest((xod::Number) -1, 1, str) == xod::XStringCString("-1"));
REQUIRE(convertAndTest((xod::Number) 42, 5, str) == xod::XStringCString("42"));
REQUIRE(convertAndTest((xod::Number) -579, 1, str) == xod::XStringCString("-579"));
REQUIRE(convertAndTest((xod::Number) 21474836, 0, str) == xod::XStringCString("21474836"));
REQUIRE(convertAndTest((xod::Number) 21474836, 6, str) == xod::XStringCString("21474836"));
}
SECTION("Numbers with floating point") {
REQUIRE(convertAndTest((xod::Number) 0.25, 0, str) == xod::XStringCString("0"));
REQUIRE(convertAndTest((xod::Number) 0.51, 0, str) == xod::XStringCString("1"));
REQUIRE(convertAndTest((xod::Number) 0.451, 1, str) == xod::XStringCString("0.5"));
REQUIRE(convertAndTest((xod::Number) 0.451, 2, str) == xod::XStringCString("0.45"));
REQUIRE(convertAndTest((xod::Number) 0.7385, 4, str) == xod::XStringCString("0.7385"));
REQUIRE(convertAndTest((xod::Number) 0.73855, 4, str) == xod::XStringCString("0.7386"));
REQUIRE(convertAndTest((xod::Number) 123.456, 0, str) == xod::XStringCString("123"));
REQUIRE(convertAndTest((xod::Number) 123.456, 2, str) == xod::XStringCString("123.46"));
REQUIRE(convertAndTest((xod::Number) -0.25, 0, str) == xod::XStringCString("0"));
REQUIRE(convertAndTest((xod::Number) -0.5, 0, str) == xod::XStringCString("0"));
REQUIRE(convertAndTest((xod::Number) -0.451, 1, str) == xod::XStringCString("-0.5"));
REQUIRE(convertAndTest((xod::Number) -0.6, 0, str) == xod::XStringCString("-1"));
REQUIRE(convertAndTest((xod::Number) -0.7385, 4, str) == xod::XStringCString("-0.7385"));
REQUIRE(convertAndTest((xod::Number) -0.73855, 4, str) == xod::XStringCString("-0.7386"));
REQUIRE(convertAndTest((xod::Number) -123.456, 0, str) == xod::XStringCString("-123"));
REQUIRE(convertAndTest((xod::Number) -123.456, 2, str) == xod::XStringCString("-123.46"));
}
SECTION("NaNs") {
REQUIRE(convertAndTest((xod::Number) NAN, 0, str) == xod::XStringCString("NaN"));
REQUIRE(convertAndTest((xod::Number) NAN, 2, str) == xod::XStringCString("NaN"));
}
SECTION("Infinities") {
REQUIRE(convertAndTest((xod::Number) INFINITY, 0, str) == xod::XStringCString("Inf"));
REQUIRE(convertAndTest((xod::Number) INFINITY, 5, str) == xod::XStringCString("Inf"));
REQUIRE(convertAndTest((xod::Number) -INFINITY, 0, str) == xod::XStringCString("-Inf"));
REQUIRE(convertAndTest((xod::Number) -INFINITY, 5, str) == xod::XStringCString("-Inf"));
}
SECTION("Overflowing numbers") {
REQUIRE(convertAndTest((xod::Number) 99000000000, 0, str) == xod::XStringCString("OVF"));
REQUIRE(convertAndTest((xod::Number) 99000000000, 2, str) == xod::XStringCString("OVF"));
REQUIRE(convertAndTest((xod::Number) -99000000000, 0, str) == xod::XStringCString("-OVF"));
REQUIRE(convertAndTest((xod::Number) -99000000000, 2, str) == xod::XStringCString("-OVF"));
}
}

View File

@@ -25,7 +25,7 @@ AVR Memory Usage
----------------
Device: atmega328p
Program: 1684 bytes (5.1% Full)
Program: 1626 bytes (5.0% Full)
(.text + .data + .bootloader)
Data: 38 bytes (1.9% Full)

View File

@@ -7,12 +7,14 @@ RUNNER=$DIR/run-tests
g++ \
-I../../vendor/catch2 \
-I../../cpplib/catch2utils \
-std=c++11 \
-g \
-O0 \
-o $RUNNER \
$DIR/test.cpp \
$DIR/list.cpp
$DIR/list.cpp \
$DIR/formatNumber.cpp
if [[ $* == *--leak-check* ]]; then
valgrind --leak-check=yes $RUNNER

View File

@@ -73,7 +73,8 @@
"build:tabtestWorkspace": "cpx \"../xod-tabtest/workspace/**\" \"./bundle/tabtest-workspace\"",
"build:tabtestSrc": "cpx \"../xod-tabtest/cpp/**\" \"./bundle/tabtest-cpp\"",
"build:catch2": "cpx \"../../vendor/catch2/**\" \"./bundle/catch2\"",
"build:bundle": "yarn run build:workspace && yarn run build:tabtestWorkspace && yarn run build:tabtestSrc && yarn run build:catch2",
"build:catch2utils": "cpx \"../../cpplib/catch2utils/**\" \"./bundle/catch2utils\"",
"build:bundle": "yarn run build:workspace && yarn run build:tabtestWorkspace && yarn run build:tabtestSrc && yarn run build:catch2 && yarn run build:catch2utils",
"build:readme": "oclif-dev readme && sed -i -e 's/lib\\/commands\\//src\\/commands\\//g' README.md",
"build:src": "babel src/ -d lib/ --source-maps",
"build": "yarn run build:bundle && yarn run build:src",

View File

@@ -17,6 +17,7 @@ import * as myFlags from '../flags';
import { getListr } from '../listr';
import {
resolveBundledCatch2Path,
resolveBundledCatch2UtilsPath,
resolveBundledTabtestSrcPath,
resolveBundledTabtestWorkspacePath,
resolveBundledWorkspacePath,
@@ -102,6 +103,7 @@ class TabtestCommand extends BaseCommand {
allPromises,
append(fs.copy(resolveBundledTabtestSrcPath(), ctx.outDir)),
append(fs.copy(resolveBundledCatch2Path(), ctx.outDir)),
append(fs.copy(resolveBundledCatch2UtilsPath(), ctx.outDir)),
map(([filename, content]) =>
fs.outputFile(path.join(ctx.outDir, filename), content)
)

View File

@@ -11,3 +11,6 @@ export const resolveBundledTabtestSrcPath = () =>
export const resolveBundledCatch2Path = () =>
path.resolve(__dirname, '..', 'bundle', 'catch2');
export const resolveBundledCatch2UtilsPath = () =>
path.resolve(__dirname, '..', 'bundle', 'catch2utils');

View File

@@ -2,10 +2,11 @@
#ifndef ARDUINO_H
#define ARDUINO_H
#include <stddef.h> // for size_t
#include <stdint.h> // for uint32_t, etc
#include <string.h> // for strlen
#include <stdlib.h> // for fcvt
#include <algorithm> // for min/max
#include <stddef.h> // for size_t
#include <stdint.h> // for uint32_t, etc
#include <string.h> // for strlen
#include <stdlib.h> // to bring `abs` before its redefinition below
#include <math.h>
void setup();
@@ -13,6 +14,9 @@ void loop();
#define A0 14
using ::std::min;
using ::std::max;
// undefine stdlib's abs if encountered
// because all platforms' Arduino.h do it
#ifdef abs

View File

@@ -254,7 +254,7 @@ module TestCase = {
Cpp.(
source([
"#include \"catch.hpp\"",
"#include \"formatters.cpp\"",
"#include <XStringFormat.inl>",
"",
source(nodeAliases),
"",

View File

@@ -262,7 +262,7 @@ bool ESP8266::createTCP(const char* addr, uint32_t port) {
writeCmd(COMMA_2);
print(addr);
writeCmd(COMMA_1);
dtostrf(port, 0, 0, _port);
formatNumber(port, 0, _port);
println(_port);
bool ok = cmdOK(OK, ERROR, 5000);
@@ -286,7 +286,7 @@ bool ESP8266::send(char* message) {
size_t len = sprintf(message, "%s", message);
char reqLen[len];
dtostrf(len, 0, 0, reqLen);
formatNumber(len, 0, reqLen);
println(reqLen);
bool prompt = cmdOK(PROMPT, LINK_IS_NOT);

View File

@@ -12,6 +12,6 @@ struct State {
void evaluate(Context ctx) {
auto state = getState(ctx);
auto num = getValue<input_IN>(ctx);
dtostrf(num, 0, 2, state->str);
formatNumber(num, 2, state->str);
emitValue<output_OUT>(ctx, XString(&state->view));
}

View File

@@ -12,6 +12,6 @@ struct State {
void evaluate(Context ctx) {
auto state = getState(ctx);
auto num = getValue<input_IN>(ctx);
dtostrf(num, 0, 2, state->str);
formatNumber(num, 2, state->str);
emitValue<output_OUT>(ctx, XString(&state->view));
}

View File

@@ -13,6 +13,6 @@ void evaluate(Context ctx) {
auto state = getState(ctx);
auto num = getValue<input_NUM>(ctx);
auto dig = getValue<input_DIG>(ctx);
dtostrf(num, 0, dig, state->str);
formatNumber(num, dig, state->str);
emitValue<output_STR>(ctx, XString(&state->view));
}

View File

@@ -56,6 +56,24 @@ typename remove_reference<T>::type&& move(T&& a) {
} // namespace std
} // namespace xod
/*=============================================================================
*
*
* Basic XOD types
*
*
=============================================================================*/
namespace xod {
#if __SIZEOF_FLOAT__ == 4
typedef float Number;
#else
typedef double Number;
#endif
typedef bool Logic;
typedef unsigned long TimeMs;
typedef uint8_t DirtyFlags;
} // namespace xod
/*=============================================================================
*
*
@@ -573,10 +591,176 @@ template<typename T> bool equal(List<T> lhs, List<T> rhs) {
return !lhsIt && !rhsIt;
}
template<typename T> bool operator == (List<T> lhs, List<T> rhs) {
return equal(lhs, rhs);
}
} // namespace xod
#endif
/*=============================================================================
*
*
* Format Numbers
*
*
=============================================================================*/
/**
* Provide `formatNumber` cross-platform number to string converter function.
*
* Taken from here:
* https://github.com/client9/stringencoders/blob/master/src/modp_numtoa.c
* Original function name: `modp_dtoa2`.
*
* Modified:
* - `isnan` instead of tricky comparing and return "NaN"
* - handle Infinity values and return "Inf" or "-Inf"
* - return `OVF` and `-OVF` for numbers bigger than max possible, instead of using `sprintf`
* - use `Number` instead of double
* - if negative number rounds to zero, return just "0" instead of "-0"
*
* This is a replacement of `dtostrf`.
*/
#ifndef XOD_FORMAT_NUMBER_H
#define XOD_FORMAT_NUMBER_H
namespace xod {
/**
* Powers of 10
* 10^0 to 10^9
*/
static const Number powers_of_10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000,
10000000, 100000000, 1000000000 };
static void strreverse(char* begin, char* end) {
char aux;
while (end > begin)
aux = *end, *end-- = *begin, *begin++ = aux;
};
size_t formatNumber(Number value, int prec, char* str) {
if (isnan(value)) {
strcpy(str, "NaN");
return (size_t)3;
}
if (isinf(value)) {
bool isNegative = value < 0;
strcpy(str, isNegative ? "-Inf" : "Inf");
return (size_t)isNegative ? 4 : 3;
}
/* if input is larger than thres_max return "OVF" */
const Number thres_max = (Number)(0x7FFFFFFF);
Number diff = 0.0;
char* wstr = str;
if (prec < 0) {
prec = 0;
} else if (prec > 9) {
/* precision of >= 10 can lead to overflow errors */
prec = 9;
}
/* we'll work in positive values and deal with the
negative sign issue later */
int neg = 0;
if (value < 0) {
neg = 1;
value = -value;
}
int whole = (int)value;
Number tmp = (value - whole) * powers_of_10[prec];
uint32_t frac = (uint32_t)(tmp);
diff = tmp - frac;
if (diff > 0.5) {
++frac;
/* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
} else if (diff == 0.5 && prec > 0 && (frac & 1)) {
/* if halfway, round up if odd, OR
if last digit is 0. That last part is strange */
++frac;
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
} else if (diff == 0.5 && prec == 0 && (whole & 1)) {
++frac;
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
}
if (value > thres_max) {
if (neg) {
strcpy(str, "-OVF");
return (size_t)4;
}
strcpy(str, "OVF");
return (size_t)3;
}
int has_decimal = 0;
int count = prec;
bool notzero = frac > 0;
/* Remove ending zeros */
if (prec > 0) {
while (count > 0 && ((frac % 10) == 0)) {
count--;
frac /= 10;
}
}
while (count > 0) {
--count;
*wstr++ = (char)(48 + (frac % 10));
frac /= 10;
has_decimal = 1;
}
if (frac > 0) {
++whole;
}
/* add decimal */
if (has_decimal) {
*wstr++ = '.';
}
notzero = notzero || whole > 0;
/* do whole part
* Take care of sign conversion
* Number is reversed.
*/
do
*wstr++ = (char)(48 + (whole % 10));
while (whole /= 10);
if (neg && notzero) {
*wstr++ = '-';
}
*wstr = '\0';
strreverse(str, wstr - 1);
return (size_t)(wstr - str);
}
} // namespace xod
#endif
/*=============================================================================
*
@@ -621,79 +805,7 @@ template<typename T> bool equal(List<T> lhs, List<T> rhs) {
# define pgm_read_ptr(addr) (*(const void **)(addr))
#endif
//----------------------------------------------------------------------------
// Compatibilities
//----------------------------------------------------------------------------
#if !defined(ARDUINO_ARCH_AVR) && !defined(__DTOSTRF_H_)
/*
* Provide dtostrf function for non-AVR platforms. Although many platforms
* provide a stub many others do not. And the stub is based on `sprintf`
* which doesnt work with floating point formatters on some platforms
* (e.g. Arduino M0).
*
* This is an implementation based on `fcvt` standard function. Taken here:
* https://forum.arduino.cc/index.php?topic=368720.msg2542614#msg2542614
*/
char *dtostrf(double val, int width, unsigned int prec, char *sout) {
int decpt, sign, reqd, pad;
const char *s, *e;
char *p;
s = fcvt(val, prec, &decpt, &sign);
if (prec == 0 && decpt == 0) {
s = (*s < '5') ? "0" : "1";
reqd = 1;
} else {
reqd = strlen(s);
if (reqd > decpt) reqd++;
if (decpt == 0) reqd++;
}
if (sign) reqd++;
p = sout;
e = p + reqd;
pad = width - reqd;
if (pad > 0) {
e += pad;
while (pad-- > 0) *p++ = ' ';
}
if (sign) *p++ = '-';
if (decpt <= 0 && prec > 0) {
*p++ = '0';
*p++ = '.';
e++;
while ( decpt < 0 ) {
decpt++;
*p++ = '0';
}
}
while (p < e) {
*p++ = *s++;
if (p == e) break;
if (--decpt == 0) *p++ = '.';
}
if (width < 0) {
pad = (reqd + width) * -1;
while (pad-- > 0) *p++ = ' ';
}
*p = 0;
return sout;
}
#endif
namespace xod {
//----------------------------------------------------------------------------
// Type definitions
//----------------------------------------------------------------------------
#if __SIZEOF_FLOAT__ == 4
typedef float Number;
#else
typedef double Number;
#endif
typedef bool Logic;
typedef unsigned long TimeMs;
typedef uint8_t DirtyFlags;
//----------------------------------------------------------------------------
// Global variables
//----------------------------------------------------------------------------

View File

@@ -2,7 +2,7 @@
:100010000C9479000C9479000C9479000C9479007C
:100020000C9479000C9479000C9479000C9479006C
:100030000C9479000C9479000C9479000C9479005C
:100040000C9474020C9479000C9479000C9479004F
:100040000C9460020C9479000C9479000C94790063
:100050000C9479000C9479000C9479000C9479003C
:100060000C9479000C947900000000002400270013
:100070002A0000000000250028002B0004040404CE
@@ -10,97 +10,95 @@
:10009000010204081020408001020408102001021F
:1000A00004081020000000080002010000030407FB
:1000B000000000000000000011241FBECFEFD8E0B8
:1000C000DEBFCDBF11E0A0E0B1E0EAE6F6E002C09D
:1000C000DEBFCDBF11E0A0E0B1E0E2E4F6E002C0A7
:1000D00005900D92A831B107D9F721E0A8E1B1E070
:1000E00001C01D92A632B207E1F70E94BE020C9435
:1000F00033030C9400003FB7F89480912201909153
:1000E00001C01D92A632B207E1F70E94AA020C9449
:1000F0001F030C9400003FB7F89480912201909167
:100100002301A0912401B091250126B5A89B05C02B
:100110002F3F19F00196A11DB11D3FBFBA2FA92F86
:10012000982F8827820F911DA11DB11DBC01CD0103
:1001300042E0660F771F881F991F4A95D1F70895EF
:1001400080E090E0892B49F080E090E0892B29F055
:100150000E94000081110C94000008950F931F93DA
:100160002FB7F89480911E0190911F01A09120015A
:10017000B09121012FBF8093180190931901A09392
:100180001A01B0931B01409112015091130160912B
:10019000140170911501411551056105710531F08A
:1001A00021E0481759076A077B0708F020E0809193
:1001B000170181FB992790F9292B20FB81F9809366
:1001C000170140910B0150910C0160910D0170914C
:1001D0000E01411551056105710571F031E0809105
:1001E000180190911901A0911A01B0911B014817B3
:1001F00059076A077B0708F030E08091100181FB06
:10020000992790F9932B90FB81F980931001222379
:10021000E9F081E08093160180911701816080935D
:100220001701009118011091190120911A013091C4
:100230001B010093120110931301209314013093BA
:100240001501809106018460809306019923F1F1E4
:10025000E0911C01009118011091190120911A01DF
:1002600030911B01D901C80186509F4FAF4FBF4F3E
:10027000411551056105710531F0401751076207BD
:10028000730708F415C1EE23A1F0409107015091C6
:1002900008016091090170910A0140175107620736
:1002A000730708F4EBC084179507A607B70708F48F
:1002B000E5C09091100191708091020181FB22278D
:1002C00020F9922B90FB81F9809302018091020129
:1002D00081FF20C0909101018091100180FF03C037
:1002E00081E0892701C080E0891739F080930101FE
:1002F00080910201816080930201909102019170CE
:100300008091060182FB222720F9922B90FB82F933
:10031000809306018091060182FF6CC0609101010B
:100320008091170180FF66C04DE950E0FA01749199
:1003300089E890E0FC012491222399F030E0220F1B
:10034000331FF901E859FF4FA591B4912E583F4F43
:10035000F901059114912FB7F8943C91732B7C937C
:100360002FBFE1EBF0E02491FA014491FC0194915C
:10037000992309F43CC0222339F1233091F038F459
:100380002130A9F0223001F584B58F7D12C02730CD
:1003900091F02830A1F02430B9F4809180008F7D55
:1003A00003C0809180008F77809380000DC084B55A
:1003B0008F7784BD09C08091B0008F7703C0809192
:1003C000B0008F7D8093B000E92FF0E0EE0FFF1FAB
:1003D000EE58FF4FA591B4918FB7F8949C9161119D
:1003E00003C04095492301C0492B4C938FBF81E046
:1003F00080930401109217011092100110920201D3
:10040000109206018091120190911301A0911401A4
:10041000B09115010097A105B10569F0409118014F
:100420005091190160911A0170911B018417950771
:10043000A607B707A0F180910B0190910C01A09144
:100440000D01B0910E010097A105B10509F449C055
:10045000409118015091190160911A0170911B018E
:1004600084179507A607B707E0F510920B011092C5
:100470000C0110920D0110920E0133C08093070100
:1004800090930801A0930901B0930A0180930B0196
:1004900090930C01A0930D01B0930E010ACF10921E
:1004A0001201109213011092140110921501C3CF82
:1004B00041E040930F0140911001416040931001D1
:1004C0008093070190930801A0930901B0930A015A
:1004D00080930B0190930C01A0930D01B0930E013A
:1004E000D2CE1F910F9108951F920F920FB60F92C7
:1004F00011242F933F938F939F93AF93BF9380913A
:100500001E0190911F01A0912001B0912101309115
:100510001D0123E0230F2D3720F40196A11DB11DED
:1005200005C026E8230F0296A11DB11D20931D01D1
:1005300080931E0190931F01A0932001B09321018D
:100540008091220190912301A0912401B091250175
:100550000196A11DB11D8093220190932301A093C8
:100560002401B0932501BF91AF919F918F913F914D
:100570002F910F900FBE0F901F901895789484B50F
:10058000826084BD84B5816084BD85B5826085BD8F
:1005900085B5816085BD80916E00816080936E001D
:1005A000109281008091810082608093810080910F
:1005B00081008160809381008091800081608093C0
:1005C00080008091B10084608093B1008091B00080
:1005D00081608093B00080917A00846080937A007B
:1005E00080917A00826080937A0080917A008160A5
:1005F00080937A0080917A00806880937A001092CC
:10060000C1000E947B004B015C0184E6C82ED12C06
:10061000E12CF12C0E947B00DC01CB0188199909A7
:10062000AA09BB09883E9340A105B10558F021E015
:10063000C21AD108E108F10888EE880E83E0981EFE
:10064000A11CB11CC114D104E104F10419F781E02B
:1006500080931C010E94AE0010921C010E94AE000B
:0A0660000E94A000FBCFF894FFCF2A
:10066A000000030000000400000000000000000079
:08067A00020000000000000274
:10016000FC01448155816681778141155105610506
:10017000710571F081E00091180110911901209131
:100180001A0130911B01401751076207730708F0ED
:1001900080E01F910F9108958F929F92AF92BF922E
:1001A000CF92DF92EF92FF92CF93DF938FB7F894C5
:1001B00080901E0190901F01A0902001B09021011D
:1001C0008FBF8092180190921901A0921A01B092EB
:1001D0001B01409112015091130160911401709123
:1001E0001501411551056105710531F081E0481592
:1001F00059056A057B0508F080E0C0911701C1FB35
:10020000DD27D0F9D82BD0FBC1F9C093170187E0C7
:1002100091E00E94AE009091100191FB222720F9FD
:10022000822B80FB91F990931001DD2399F091E0EE
:1002300090931601C160C0931701809212019092B1
:100240001301A0921401B0921501909106019460DF
:1002500090930601882391F1C0911C0175016401FE
:100260008AEFC80ED11CE11CF11C87E091E00E94CE
:10027000AE008111E7C0CC23A1F0809107019091DD
:100280000801A0910901B0910A0188159905AA05F4
:10029000BB0508F4F0C0C816D906EA06FB0608F448
:1002A000EAC08091020181FB222720F99091100180
:1002B0009170922B90FB81F9809302018091020151
:1002C00081FF20C0909101018091100180FF03C047
:1002D00081E0892701C080E0981739F080930101FF
:1002E000809102018160809302018091060182FB6E
:1002F000222720F9909102019170922B90FB82F9B4
:10030000809306018091060182FF6CC0609101011B
:100310008091170180FF66C04DE950E0FA017491A9
:1003200089E890E0FC012491222399F030E0220F2B
:10033000331FF901E859FF4FA591B491F901EE5827
:10034000FF4F259134913FB7F8942C91272B2C9394
:100350003FBFE1EBF0E02491FA014491FC0194915C
:10036000992309F43CC0222339F1233091F038F469
:100370002130A9F0223001F584B58F7D12C02730DD
:1003800091F02830A1F02430B9F4809180008F7D65
:1003900003C0809180008F77809380000DC084B56A
:1003A0008F7784BD09C08091B0008F7703C08091A2
:1003B000B0008F7D8093B000E92FF0E0EE0FFF1FBB
:1003C000EE58FF4FA591B4919FB7F8948C916111AD
:1003D00003C04095482301C0482B4C939FBF81E048
:1003E00080930401109217011092100110920201E3
:1003F000109206018091120190911301A0911401B5
:10040000B09115010097A105B10569F0409118015F
:100410005091190160911A0170911B018417950781
:10042000A607B707C8F187E091E00E94AE008823D5
:10043000E1F110920B0110920C0110920D0110923B
:100440000E0133C081E080930F0180911001816023
:1004500080931001C0920701D0920801E092090137
:10046000F0920A01C0920B01D0920C01E0920D01B2
:10047000F0920E0100CFC0920701D0920801E092E5
:100480000901F0920A01C0920B01D0920C01E09296
:100490000D01F0920E0105CF10921201109213017E
:1004A0001092140110921501BECFDF91CF91FF90F1
:1004B000EF90DF90CF90BF90AF909F908F90089576
:1004C0001F920F920FB60F9211242F933F938F9389
:1004D0009F93AF93BF9380911E0190911F01A091B4
:1004E0002001B091210130911D0123E0230F2D3710
:1004F00020F40196A11DB11D05C026E8230F029628
:10050000A11DB11D20931D0180931E0190931F0119
:10051000A0932001B09321018091220190912301A9
:10052000A0912401B09125010196A11DB11D8093D8
:10053000220190932301A0932401B0932501BF9140
:10054000AF919F918F913F912F910F900FBE0F9080
:100550001F901895789484B5826084BD84B58160BD
:1005600084BD85B5826085BD85B5816085BD80917E
:100570006E00816080936E001092810080918100F6
:1005800082608093810080918100816080938100EE
:10059000809180008160809380008091B1008460B0
:1005A0008093B1008091B00081608093B000809111
:1005B0007A00846080937A0080917A0082608093D0
:1005C0007A0080917A00816080937A0080917A002D
:1005D000806880937A001092C1000E947B004B01DA
:1005E0005C0184E6C82ED12CE12CF12C0E947B000A
:1005F000DC01CB0188199909AA09BB09883E9340FF
:10060000A105B10598F321E0C21AD108E108F1086B
:1006100088EE880E83E0981EA11CB11CC114D10481
:10062000E104F10419F781E080931C010E94CC00E1
:1006300010921C010E94CC000E94A000FBCFF894F5
:02064000FFCFEA
:1006420000000300000004000000000000000000A1
:0806520002000000000000029C
:00000001FF

View File

@@ -56,6 +56,24 @@ typename remove_reference<T>::type&& move(T&& a) {
} // namespace std
} // namespace xod
/*=============================================================================
*
*
* Basic XOD types
*
*
=============================================================================*/
namespace xod {
#if __SIZEOF_FLOAT__ == 4
typedef float Number;
#else
typedef double Number;
#endif
typedef bool Logic;
typedef unsigned long TimeMs;
typedef uint8_t DirtyFlags;
} // namespace xod
/*=============================================================================
*
*
@@ -573,10 +591,176 @@ template<typename T> bool equal(List<T> lhs, List<T> rhs) {
return !lhsIt && !rhsIt;
}
template<typename T> bool operator == (List<T> lhs, List<T> rhs) {
return equal(lhs, rhs);
}
} // namespace xod
#endif
/*=============================================================================
*
*
* Format Numbers
*
*
=============================================================================*/
/**
* Provide `formatNumber` cross-platform number to string converter function.
*
* Taken from here:
* https://github.com/client9/stringencoders/blob/master/src/modp_numtoa.c
* Original function name: `modp_dtoa2`.
*
* Modified:
* - `isnan` instead of tricky comparing and return "NaN"
* - handle Infinity values and return "Inf" or "-Inf"
* - return `OVF` and `-OVF` for numbers bigger than max possible, instead of using `sprintf`
* - use `Number` instead of double
* - if negative number rounds to zero, return just "0" instead of "-0"
*
* This is a replacement of `dtostrf`.
*/
#ifndef XOD_FORMAT_NUMBER_H
#define XOD_FORMAT_NUMBER_H
namespace xod {
/**
* Powers of 10
* 10^0 to 10^9
*/
static const Number powers_of_10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000,
10000000, 100000000, 1000000000 };
static void strreverse(char* begin, char* end) {
char aux;
while (end > begin)
aux = *end, *end-- = *begin, *begin++ = aux;
};
size_t formatNumber(Number value, int prec, char* str) {
if (isnan(value)) {
strcpy(str, "NaN");
return (size_t)3;
}
if (isinf(value)) {
bool isNegative = value < 0;
strcpy(str, isNegative ? "-Inf" : "Inf");
return (size_t)isNegative ? 4 : 3;
}
/* if input is larger than thres_max return "OVF" */
const Number thres_max = (Number)(0x7FFFFFFF);
Number diff = 0.0;
char* wstr = str;
if (prec < 0) {
prec = 0;
} else if (prec > 9) {
/* precision of >= 10 can lead to overflow errors */
prec = 9;
}
/* we'll work in positive values and deal with the
negative sign issue later */
int neg = 0;
if (value < 0) {
neg = 1;
value = -value;
}
int whole = (int)value;
Number tmp = (value - whole) * powers_of_10[prec];
uint32_t frac = (uint32_t)(tmp);
diff = tmp - frac;
if (diff > 0.5) {
++frac;
/* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
} else if (diff == 0.5 && prec > 0 && (frac & 1)) {
/* if halfway, round up if odd, OR
if last digit is 0. That last part is strange */
++frac;
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
} else if (diff == 0.5 && prec == 0 && (whole & 1)) {
++frac;
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
}
if (value > thres_max) {
if (neg) {
strcpy(str, "-OVF");
return (size_t)4;
}
strcpy(str, "OVF");
return (size_t)3;
}
int has_decimal = 0;
int count = prec;
bool notzero = frac > 0;
/* Remove ending zeros */
if (prec > 0) {
while (count > 0 && ((frac % 10) == 0)) {
count--;
frac /= 10;
}
}
while (count > 0) {
--count;
*wstr++ = (char)(48 + (frac % 10));
frac /= 10;
has_decimal = 1;
}
if (frac > 0) {
++whole;
}
/* add decimal */
if (has_decimal) {
*wstr++ = '.';
}
notzero = notzero || whole > 0;
/* do whole part
* Take care of sign conversion
* Number is reversed.
*/
do
*wstr++ = (char)(48 + (whole % 10));
while (whole /= 10);
if (neg && notzero) {
*wstr++ = '-';
}
*wstr = '\0';
strreverse(str, wstr - 1);
return (size_t)(wstr - str);
}
} // namespace xod
#endif
/*=============================================================================
*
@@ -621,79 +805,7 @@ template<typename T> bool equal(List<T> lhs, List<T> rhs) {
# define pgm_read_ptr(addr) (*(const void **)(addr))
#endif
//----------------------------------------------------------------------------
// Compatibilities
//----------------------------------------------------------------------------
#if !defined(ARDUINO_ARCH_AVR) && !defined(__DTOSTRF_H_)
/*
* Provide dtostrf function for non-AVR platforms. Although many platforms
* provide a stub many others do not. And the stub is based on `sprintf`
* which doesnt work with floating point formatters on some platforms
* (e.g. Arduino M0).
*
* This is an implementation based on `fcvt` standard function. Taken here:
* https://forum.arduino.cc/index.php?topic=368720.msg2542614#msg2542614
*/
char *dtostrf(double val, int width, unsigned int prec, char *sout) {
int decpt, sign, reqd, pad;
const char *s, *e;
char *p;
s = fcvt(val, prec, &decpt, &sign);
if (prec == 0 && decpt == 0) {
s = (*s < '5') ? "0" : "1";
reqd = 1;
} else {
reqd = strlen(s);
if (reqd > decpt) reqd++;
if (decpt == 0) reqd++;
}
if (sign) reqd++;
p = sout;
e = p + reqd;
pad = width - reqd;
if (pad > 0) {
e += pad;
while (pad-- > 0) *p++ = ' ';
}
if (sign) *p++ = '-';
if (decpt <= 0 && prec > 0) {
*p++ = '0';
*p++ = '.';
e++;
while ( decpt < 0 ) {
decpt++;
*p++ = '0';
}
}
while (p < e) {
*p++ = *s++;
if (p == e) break;
if (--decpt == 0) *p++ = '.';
}
if (width < 0) {
pad = (reqd + width) * -1;
while (pad-- > 0) *p++ = ' ';
}
*p = 0;
return sout;
}
#endif
namespace xod {
//----------------------------------------------------------------------------
// Type definitions
//----------------------------------------------------------------------------
#if __SIZEOF_FLOAT__ == 4
typedef float Number;
#else
typedef double Number;
#endif
typedef bool Logic;
typedef unsigned long TimeMs;
typedef uint8_t DirtyFlags;
//----------------------------------------------------------------------------
// Global variables
//----------------------------------------------------------------------------
@@ -1685,7 +1797,7 @@ State* getState(Context ctx) {
void evaluate(Context ctx) {
auto state = getState(ctx);
auto num = getValue<input_IN>(ctx);
dtostrf(num, 0, 2, state->str);
formatNumber(num, 2, state->str);
emitValue<output_OUT>(ctx, XString(&state->view));
}

View File

@@ -56,6 +56,24 @@ typename remove_reference<T>::type&& move(T&& a) {
} // namespace std
} // namespace xod
/*=============================================================================
*
*
* Basic XOD types
*
*
=============================================================================*/
namespace xod {
#if __SIZEOF_FLOAT__ == 4
typedef float Number;
#else
typedef double Number;
#endif
typedef bool Logic;
typedef unsigned long TimeMs;
typedef uint8_t DirtyFlags;
} // namespace xod
/*=============================================================================
*
*
@@ -573,10 +591,176 @@ template<typename T> bool equal(List<T> lhs, List<T> rhs) {
return !lhsIt && !rhsIt;
}
template<typename T> bool operator == (List<T> lhs, List<T> rhs) {
return equal(lhs, rhs);
}
} // namespace xod
#endif
/*=============================================================================
*
*
* Format Numbers
*
*
=============================================================================*/
/**
* Provide `formatNumber` cross-platform number to string converter function.
*
* Taken from here:
* https://github.com/client9/stringencoders/blob/master/src/modp_numtoa.c
* Original function name: `modp_dtoa2`.
*
* Modified:
* - `isnan` instead of tricky comparing and return "NaN"
* - handle Infinity values and return "Inf" or "-Inf"
* - return `OVF` and `-OVF` for numbers bigger than max possible, instead of using `sprintf`
* - use `Number` instead of double
* - if negative number rounds to zero, return just "0" instead of "-0"
*
* This is a replacement of `dtostrf`.
*/
#ifndef XOD_FORMAT_NUMBER_H
#define XOD_FORMAT_NUMBER_H
namespace xod {
/**
* Powers of 10
* 10^0 to 10^9
*/
static const Number powers_of_10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000,
10000000, 100000000, 1000000000 };
static void strreverse(char* begin, char* end) {
char aux;
while (end > begin)
aux = *end, *end-- = *begin, *begin++ = aux;
};
size_t formatNumber(Number value, int prec, char* str) {
if (isnan(value)) {
strcpy(str, "NaN");
return (size_t)3;
}
if (isinf(value)) {
bool isNegative = value < 0;
strcpy(str, isNegative ? "-Inf" : "Inf");
return (size_t)isNegative ? 4 : 3;
}
/* if input is larger than thres_max return "OVF" */
const Number thres_max = (Number)(0x7FFFFFFF);
Number diff = 0.0;
char* wstr = str;
if (prec < 0) {
prec = 0;
} else if (prec > 9) {
/* precision of >= 10 can lead to overflow errors */
prec = 9;
}
/* we'll work in positive values and deal with the
negative sign issue later */
int neg = 0;
if (value < 0) {
neg = 1;
value = -value;
}
int whole = (int)value;
Number tmp = (value - whole) * powers_of_10[prec];
uint32_t frac = (uint32_t)(tmp);
diff = tmp - frac;
if (diff > 0.5) {
++frac;
/* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
} else if (diff == 0.5 && prec > 0 && (frac & 1)) {
/* if halfway, round up if odd, OR
if last digit is 0. That last part is strange */
++frac;
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
} else if (diff == 0.5 && prec == 0 && (whole & 1)) {
++frac;
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
}
if (value > thres_max) {
if (neg) {
strcpy(str, "-OVF");
return (size_t)4;
}
strcpy(str, "OVF");
return (size_t)3;
}
int has_decimal = 0;
int count = prec;
bool notzero = frac > 0;
/* Remove ending zeros */
if (prec > 0) {
while (count > 0 && ((frac % 10) == 0)) {
count--;
frac /= 10;
}
}
while (count > 0) {
--count;
*wstr++ = (char)(48 + (frac % 10));
frac /= 10;
has_decimal = 1;
}
if (frac > 0) {
++whole;
}
/* add decimal */
if (has_decimal) {
*wstr++ = '.';
}
notzero = notzero || whole > 0;
/* do whole part
* Take care of sign conversion
* Number is reversed.
*/
do
*wstr++ = (char)(48 + (whole % 10));
while (whole /= 10);
if (neg && notzero) {
*wstr++ = '-';
}
*wstr = '\0';
strreverse(str, wstr - 1);
return (size_t)(wstr - str);
}
} // namespace xod
#endif
/*=============================================================================
*
@@ -621,79 +805,7 @@ template<typename T> bool equal(List<T> lhs, List<T> rhs) {
# define pgm_read_ptr(addr) (*(const void **)(addr))
#endif
//----------------------------------------------------------------------------
// Compatibilities
//----------------------------------------------------------------------------
#if !defined(ARDUINO_ARCH_AVR) && !defined(__DTOSTRF_H_)
/*
* Provide dtostrf function for non-AVR platforms. Although many platforms
* provide a stub many others do not. And the stub is based on `sprintf`
* which doesnt work with floating point formatters on some platforms
* (e.g. Arduino M0).
*
* This is an implementation based on `fcvt` standard function. Taken here:
* https://forum.arduino.cc/index.php?topic=368720.msg2542614#msg2542614
*/
char *dtostrf(double val, int width, unsigned int prec, char *sout) {
int decpt, sign, reqd, pad;
const char *s, *e;
char *p;
s = fcvt(val, prec, &decpt, &sign);
if (prec == 0 && decpt == 0) {
s = (*s < '5') ? "0" : "1";
reqd = 1;
} else {
reqd = strlen(s);
if (reqd > decpt) reqd++;
if (decpt == 0) reqd++;
}
if (sign) reqd++;
p = sout;
e = p + reqd;
pad = width - reqd;
if (pad > 0) {
e += pad;
while (pad-- > 0) *p++ = ' ';
}
if (sign) *p++ = '-';
if (decpt <= 0 && prec > 0) {
*p++ = '0';
*p++ = '.';
e++;
while ( decpt < 0 ) {
decpt++;
*p++ = '0';
}
}
while (p < e) {
*p++ = *s++;
if (p == e) break;
if (--decpt == 0) *p++ = '.';
}
if (width < 0) {
pad = (reqd + width) * -1;
while (pad-- > 0) *p++ = ' ';
}
*p = 0;
return sout;
}
#endif
namespace xod {
//----------------------------------------------------------------------------
// Type definitions
//----------------------------------------------------------------------------
#if __SIZEOF_FLOAT__ == 4
typedef float Number;
#else
typedef double Number;
#endif
typedef bool Logic;
typedef unsigned long TimeMs;
typedef uint8_t DirtyFlags;
//----------------------------------------------------------------------------
// Global variables
//----------------------------------------------------------------------------
@@ -1065,7 +1177,7 @@ State* getState(Context ctx) {
void evaluate(Context ctx) {
auto state = getState(ctx);
auto num = getValue<input_IN>(ctx);
dtostrf(num, 0, 2, state->str);
formatNumber(num, 2, state->str);
emitValue<output_OUT>(ctx, XString(&state->view));
}

View File

@@ -56,6 +56,24 @@ typename remove_reference<T>::type&& move(T&& a) {
} // namespace std
} // namespace xod
/*=============================================================================
*
*
* Basic XOD types
*
*
=============================================================================*/
namespace xod {
#if __SIZEOF_FLOAT__ == 4
typedef float Number;
#else
typedef double Number;
#endif
typedef bool Logic;
typedef unsigned long TimeMs;
typedef uint8_t DirtyFlags;
} // namespace xod
/*=============================================================================
*
*
@@ -573,10 +591,176 @@ template<typename T> bool equal(List<T> lhs, List<T> rhs) {
return !lhsIt && !rhsIt;
}
template<typename T> bool operator == (List<T> lhs, List<T> rhs) {
return equal(lhs, rhs);
}
} // namespace xod
#endif
/*=============================================================================
*
*
* Format Numbers
*
*
=============================================================================*/
/**
* Provide `formatNumber` cross-platform number to string converter function.
*
* Taken from here:
* https://github.com/client9/stringencoders/blob/master/src/modp_numtoa.c
* Original function name: `modp_dtoa2`.
*
* Modified:
* - `isnan` instead of tricky comparing and return "NaN"
* - handle Infinity values and return "Inf" or "-Inf"
* - return `OVF` and `-OVF` for numbers bigger than max possible, instead of using `sprintf`
* - use `Number` instead of double
* - if negative number rounds to zero, return just "0" instead of "-0"
*
* This is a replacement of `dtostrf`.
*/
#ifndef XOD_FORMAT_NUMBER_H
#define XOD_FORMAT_NUMBER_H
namespace xod {
/**
* Powers of 10
* 10^0 to 10^9
*/
static const Number powers_of_10[] = { 1, 10, 100, 1000, 10000, 100000, 1000000,
10000000, 100000000, 1000000000 };
static void strreverse(char* begin, char* end) {
char aux;
while (end > begin)
aux = *end, *end-- = *begin, *begin++ = aux;
};
size_t formatNumber(Number value, int prec, char* str) {
if (isnan(value)) {
strcpy(str, "NaN");
return (size_t)3;
}
if (isinf(value)) {
bool isNegative = value < 0;
strcpy(str, isNegative ? "-Inf" : "Inf");
return (size_t)isNegative ? 4 : 3;
}
/* if input is larger than thres_max return "OVF" */
const Number thres_max = (Number)(0x7FFFFFFF);
Number diff = 0.0;
char* wstr = str;
if (prec < 0) {
prec = 0;
} else if (prec > 9) {
/* precision of >= 10 can lead to overflow errors */
prec = 9;
}
/* we'll work in positive values and deal with the
negative sign issue later */
int neg = 0;
if (value < 0) {
neg = 1;
value = -value;
}
int whole = (int)value;
Number tmp = (value - whole) * powers_of_10[prec];
uint32_t frac = (uint32_t)(tmp);
diff = tmp - frac;
if (diff > 0.5) {
++frac;
/* handle rollover, e.g. case 0.99 with prec 1 is 1.0 */
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
} else if (diff == 0.5 && prec > 0 && (frac & 1)) {
/* if halfway, round up if odd, OR
if last digit is 0. That last part is strange */
++frac;
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
} else if (diff == 0.5 && prec == 0 && (whole & 1)) {
++frac;
if (frac >= powers_of_10[prec]) {
frac = 0;
++whole;
}
}
if (value > thres_max) {
if (neg) {
strcpy(str, "-OVF");
return (size_t)4;
}
strcpy(str, "OVF");
return (size_t)3;
}
int has_decimal = 0;
int count = prec;
bool notzero = frac > 0;
/* Remove ending zeros */
if (prec > 0) {
while (count > 0 && ((frac % 10) == 0)) {
count--;
frac /= 10;
}
}
while (count > 0) {
--count;
*wstr++ = (char)(48 + (frac % 10));
frac /= 10;
has_decimal = 1;
}
if (frac > 0) {
++whole;
}
/* add decimal */
if (has_decimal) {
*wstr++ = '.';
}
notzero = notzero || whole > 0;
/* do whole part
* Take care of sign conversion
* Number is reversed.
*/
do
*wstr++ = (char)(48 + (whole % 10));
while (whole /= 10);
if (neg && notzero) {
*wstr++ = '-';
}
*wstr = '\0';
strreverse(str, wstr - 1);
return (size_t)(wstr - str);
}
} // namespace xod
#endif
/*=============================================================================
*
@@ -621,79 +805,7 @@ template<typename T> bool equal(List<T> lhs, List<T> rhs) {
# define pgm_read_ptr(addr) (*(const void **)(addr))
#endif
//----------------------------------------------------------------------------
// Compatibilities
//----------------------------------------------------------------------------
#if !defined(ARDUINO_ARCH_AVR) && !defined(__DTOSTRF_H_)
/*
* Provide dtostrf function for non-AVR platforms. Although many platforms
* provide a stub many others do not. And the stub is based on `sprintf`
* which doesnt work with floating point formatters on some platforms
* (e.g. Arduino M0).
*
* This is an implementation based on `fcvt` standard function. Taken here:
* https://forum.arduino.cc/index.php?topic=368720.msg2542614#msg2542614
*/
char *dtostrf(double val, int width, unsigned int prec, char *sout) {
int decpt, sign, reqd, pad;
const char *s, *e;
char *p;
s = fcvt(val, prec, &decpt, &sign);
if (prec == 0 && decpt == 0) {
s = (*s < '5') ? "0" : "1";
reqd = 1;
} else {
reqd = strlen(s);
if (reqd > decpt) reqd++;
if (decpt == 0) reqd++;
}
if (sign) reqd++;
p = sout;
e = p + reqd;
pad = width - reqd;
if (pad > 0) {
e += pad;
while (pad-- > 0) *p++ = ' ';
}
if (sign) *p++ = '-';
if (decpt <= 0 && prec > 0) {
*p++ = '0';
*p++ = '.';
e++;
while ( decpt < 0 ) {
decpt++;
*p++ = '0';
}
}
while (p < e) {
*p++ = *s++;
if (p == e) break;
if (--decpt == 0) *p++ = '.';
}
if (width < 0) {
pad = (reqd + width) * -1;
while (pad-- > 0) *p++ = ' ';
}
*p = 0;
return sout;
}
#endif
namespace xod {
//----------------------------------------------------------------------------
// Type definitions
//----------------------------------------------------------------------------
#if __SIZEOF_FLOAT__ == 4
typedef float Number;
#else
typedef double Number;
#endif
typedef bool Logic;
typedef unsigned long TimeMs;
typedef uint8_t DirtyFlags;
//----------------------------------------------------------------------------
// Global variables
//----------------------------------------------------------------------------