Number.h
Go to the documentation of this file.
1 /****
2  * ConfigDB/Number.h
3  *
4  * Copyright 2024 mikee47 <mike@sillyhouse.net>
5  *
6  * This file is part of the ConfigDB Library
7  *
8  * This library is free software: you can redistribute it and/or modify it under the terms of the
9  * GNU General Public License as published by the Free Software Foundation, version 3 or later.
10  *
11  * This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
12  * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13  * See the GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License along with this library.
16  * If not, see <https://www.gnu.org/licenses/>.
17  *
18  ****/
19 
20 #pragma once
21 
22 #include <WString.h>
23 #include <Print.h>
24 #include <Data/BitSet.h>
25 #include <cmath>
26 
27 namespace ConfigDB
28 {
91 struct number_t {
92  int32_t mantissa : 26;
93  int32_t exponent : 6;
94 
95  static constexpr unsigned maxMantissa{0x1ffffff}; // 33554431
96  static constexpr int maxExponent{0x1f}; // 31
97  static constexpr unsigned maxSignificantDigits{8};
98  static constexpr unsigned minBufferSize{17};
99 
103  static constexpr const number_t min()
104  {
105  return {1, -maxExponent};
106  }
107 
111  static constexpr const number_t max()
112  {
113  return {maxMantissa, maxExponent};
114  }
115 
119  static constexpr const number_t lowest()
120  {
121  return {-int(maxMantissa), maxExponent};
122  }
123 
124  bool operator==(const number_t& other) const
125  {
126  return mantissa == other.mantissa && exponent == other.exponent;
127  }
128 
129  bool operator!=(const number_t& other) const
130  {
131  return !operator==(other);
132  }
133 
134  bool sign() const
135  {
136  return mantissa < 0;
137  }
138 
139  bool operator<(const number_t& other) const
140  {
141  return compare(*this, other) < 0;
142  }
143 
144  bool operator>(const number_t& other) const
145  {
146  return compare(*this, other) > 0;
147  }
148 
149  static double asFloat(number_t number);
150 
151  static int64_t asInt64(number_t number);
152 
153  size_t printTo(Print& p) const
154  {
155  char buf[minBufferSize];
156  return p.print(format(buf, *this));
157  }
158 
159  static int compare(number_t num1, number_t num2);
160 
168  static const char* format(char* buf, number_t number);
169 
170  static bool parse(const char* value, unsigned length, number_t& number);
171 
172  static number_t parse(const char* value, unsigned length)
173  {
174  number_t num{};
175  parse(value, length, num);
176  return num;
177  }
178 
196  static constexpr number_t normalise(unsigned mantissa, int exponent, bool isNeg)
197  {
198  // A non-zero exponent indicates rounding occurred, so 0 isn't zero!
199  if(mantissa == 0 && exponent != 0) {
200  return number_t{isNeg ? -1 : 1, -number_t::maxExponent};
201  }
202 
203  // Discard non-significant digits
204  while(mantissa > number_t::maxMantissa * 10) {
205  mantissa /= 10;
206  ++exponent;
207  }
208 
209  // Round down
211  auto newMantissa = (mantissa + 5) / 10;
212  if(newMantissa > mantissa) {
213  // Number was rounded up and gained a digit
214  newMantissa /= 10;
215  }
216  mantissa = newMantissa;
217  ++exponent;
218  }
219 
220  // Drop any trailing 0's from mantissa
221  while(mantissa >= 10 && mantissa % 10 == 0 && exponent < number_t::maxExponent) {
222  mantissa /= 10;
223  ++exponent;
224  }
225 
226  // Adjust exponent to keep it in range (without losing precision)
228  mantissa *= 10;
230  break;
231  }
232  --exponent;
233  }
234 
238  } else if(exponent < -number_t::maxExponent) {
239  mantissa = 1;
241  } else {
243  }
244 
245  return number_t{isNeg ? -int32_t(mantissa) : int32_t(mantissa), exponent};
246  }
247 
248  static constexpr number_t normalise(double mantissa)
249  {
250  // Check for special values. NB. NaN values always fail self-compare
251  if(mantissa == 0 || mantissa != mantissa) {
252  return {};
253  }
254  if(mantissa == std::numeric_limits<double>::infinity()) {
255  return max();
256  }
257  if(mantissa == -std::numeric_limits<double>::infinity()) {
258  return lowest();
259  }
260 
261  // Pull significant digits into integer part
262  int exponent = 0;
263  while(mantissa > -double(number_t::maxMantissa) && mantissa < double(number_t::maxMantissa) &&
265  mantissa *= 10.0;
266  --exponent;
267  }
268  // Reduce integer part to int32 range
269  while(mantissa < -0x7fffffff || mantissa > 0x7fffffff) {
270  mantissa /= 10;
271  ++exponent;
272  }
273 
274  return (mantissa < 0) ? normalise(unsigned(-mantissa), exponent, true)
275  : normalise(unsigned(mantissa), exponent, false);
276  }
277 
278  static constexpr number_t normalise(int mantissa, int exponent)
279  {
280  return normalise(abs(mantissa), exponent, mantissa < 0);
281  }
282 
283  static constexpr number_t normalise(int64_t value)
284  {
285  bool isNeg{false};
286  if(value < 0) {
287  isNeg = true;
288  value = -value;
289  }
290  int exponent = 0;
291  while(value > 0xffffffffll) {
292  value /= 10;
293  ++exponent;
294  }
295  return normalise(unsigned(value), exponent, isNeg);
296  }
297 };
298 
299 static_assert(sizeof(number_t) == 4, "Bad number_t size");
300 
319 struct const_number_t : public number_t {
320  const_number_t() = default;
321 
322  constexpr const_number_t(const number_t& value) : number_t(value)
323  {
324  }
325 
329  constexpr const_number_t(double value) : number_t(normalise(value))
330  {
331  }
332 };
333 
342 class __attribute__((packed)) Number
343 {
344 public:
345  Number() = default;
346 
347  constexpr Number(const number_t& number) : number(number)
348  {
349  }
350 
351  constexpr Number(const const_number_t& number) : number(number)
352  {
353  }
354 
355  constexpr Number(const Number& number) = default;
356 
357  constexpr Number(double value) : number(number_t::normalise(value))
358  {
359  }
360 
361  constexpr Number(int64_t value) : number(number_t::normalise(value))
362  {
363  }
364 
365  constexpr Number(int value) : Number(int64_t(value))
366  {
367  }
368 
369  constexpr Number(unsigned int value) : Number(int64_t(value))
370  {
371  }
372 
376  Number(const char* value, unsigned length) : number(number_t::parse(value, length))
377  {
378  }
379 
380  Number(const char* value) : Number(value, value ? strlen(value) : 0)
381  {
382  }
383 
384  Number(const String& str) : Number(str.c_str(), str.length())
385  {
386  }
387 
388  bool operator<(const Number& other) const
389  {
390  return number < other.number;
391  }
392 
393  bool operator>(const Number& other) const
394  {
395  return number > other.number;
396  }
397 
398  bool operator==(const number_t& other) const
399  {
400  return number == other;
401  }
402 
403  bool operator!=(const number_t& other) const
404  {
405  return number != other;
406  }
407 
408  int compare(const Number& other) const
409  {
410  return number_t::compare(number, other.number);
411  }
412 
413  size_t printTo(Print& p) const
414  {
415  char buf[number_t::minBufferSize];
416  return p.print(number_t::format(buf, number));
417  }
418 
419  double asFloat() const
420  {
421  return number_t::asFloat(number);
422  }
423 
424  int64_t asInt64() const
425  {
426  return number_t::asInt64(number);
427  }
428 
429  String toString() const
430  {
431  char buf[number_t::minBufferSize];
432  return number_t::format(buf, number);
433  }
434 
435  explicit operator String() const
436  {
437  return toString();
438  }
439 
440  constexpr operator number_t() const
441  {
442  return number;
443  }
444 
445 private:
446  number_t number;
447 };
448 
449 } // namespace ConfigDB
450 
453 
454 inline String toString(number_t number)
455 {
456  return ConfigDB::Number(number).toString();
457 }
#define abs(x)
< sprintf()
Definition: ArduinoCompat.h:22
String toString(number_t number)
Definition: Number.h:454
ConfigDB::const_number_t const_number_t
Definition: Number.h:452
ConfigDB::number_t number_t
Definition: Number.h:451
Base-10 floating-point storage format.
Definition: Number.h:343
Number(const char *value)
Definition: Number.h:380
double asFloat() const
Definition: Number.h:419
int compare(const Number &other) const
Definition: Number.h:408
constexpr Number(const Number &number)=default
bool operator<(const Number &other) const
Definition: Number.h:388
Number()=default
Number(const char *value, unsigned length)
Parse a number from a string.
Definition: Number.h:376
constexpr Number(const number_t &number)
Definition: Number.h:347
constexpr Number(int64_t value)
Definition: Number.h:361
bool operator>(const Number &other) const
Definition: Number.h:393
String toString() const
Definition: Number.h:429
constexpr Number(int value)
Definition: Number.h:365
size_t printTo(Print &p) const
Definition: Number.h:413
Number(const String &str)
Definition: Number.h:384
bool operator!=(const number_t &other) const
Definition: Number.h:403
int64_t asInt64() const
Definition: Number.h:424
constexpr Number(double value)
Definition: Number.h:357
constexpr Number(unsigned int value)
Definition: Number.h:369
bool operator==(const number_t &other) const
Definition: Number.h:398
constexpr Number(const const_number_t &number)
Definition: Number.h:351
Provides formatted output to stream.
Definition: Print.h:37
size_t print(char c)
Prints a single character to output stream.
Definition: Print.h:103
The String class.
Definition: WString.h:133
Definition: Array.h:26
Compile-time constant number.
Definition: Number.h:319
constexpr const_number_t(const number_t &value)
Definition: Number.h:322
constexpr const_number_t(double value)
Computer number from a compile-time constant value.
Definition: Number.h:329
Basic definition of base-10 floating point value.
Definition: Number.h:91
static constexpr const number_t lowest()
Most negative value.
Definition: Number.h:119
static number_t parse(const char *value, unsigned length)
Definition: Number.h:172
static constexpr const number_t min()
Smallest positive value.
Definition: Number.h:103
static constexpr number_t normalise(int mantissa, int exponent)
Definition: Number.h:278
static constexpr number_t normalise(unsigned mantissa, int exponent, bool isNeg)
Produce a normalised number_t from component values.
Definition: Number.h:196
static constexpr number_t normalise(double mantissa)
Definition: Number.h:248
bool sign() const
Definition: Number.h:134
static int compare(number_t num1, number_t num2)
size_t printTo(Print &p) const
Definition: Number.h:153
bool operator<(const number_t &other) const
Definition: Number.h:139
static constexpr unsigned minBufferSize
Definition: Number.h:98
static constexpr const number_t max()
Largest positive value.
Definition: Number.h:111
static const char * format(char *buf, number_t number)
Convert number to string.
bool operator!=(const number_t &other) const
Definition: Number.h:129
static bool parse(const char *value, unsigned length, number_t &number)
static constexpr number_t normalise(int64_t value)
Definition: Number.h:283
bool operator>(const number_t &other) const
Definition: Number.h:144
static constexpr unsigned maxSignificantDigits
Definition: Number.h:97
static constexpr int maxExponent
Definition: Number.h:96
static int64_t asInt64(number_t number)
static constexpr unsigned maxMantissa
Definition: Number.h:95
int32_t mantissa
Definition: Number.h:92
bool operator==(const number_t &other) const
Definition: Number.h:124
static double asFloat(number_t number)
int32_t exponent
Definition: Number.h:93
#define str(s)
Definition: testrunner.h:124