HikoGUI
A low latency retained GUI
Loading...
Searching...
No Matches
decimal.hpp
1// Copyright Take Vos 2019-2020.
2// Distributed under the Boost Software License, Version 1.0.
3// (See accompanying file LICENSE_1_0.txt or copy at https://www.boost.org/LICENSE_1_0.txt)
4
5#pragma once
6
7#include "exception.hpp"
8#include "int_overflow.hpp"
9#include "math.hpp"
10#include <fmt/ostream.h>
11#include <limits>
12#include <string_view>
13#include <string>
14#include <charconv>
15#include <ostream>
16
17namespace tt {
18
19class decimal {
20private:
21 /* Value contains an 8 bit signed exponent in the most-significant bits
22 * and a 56 bit signed mantissa in the least-significant bits.
23 */
24 uint64_t value;
25
26public:
27 constexpr static int mantissa_bits = 56;
28 constexpr static int exponent_bits = 8;
29 constexpr static int exponent_max = 127;
30 constexpr static int exponent_min = -128;
31
32 constexpr decimal() noexcept : value(0) {}
33 constexpr decimal(decimal const &other) noexcept = default;
34 constexpr decimal(decimal &&other) noexcept = default;
35 constexpr decimal &operator=(decimal const &other) noexcept = default;
36 constexpr decimal &operator=(decimal &&other) noexcept = default;
37
38 constexpr decimal(int exponent, long long mantissa) noexcept : value(decimal::pack(exponent, mantissa)) {}
39
40 constexpr decimal(std::pair<int, long long> exponent_mantissa) : decimal(exponent_mantissa.first, exponent_mantissa.second) {}
41
42 decimal(std::string_view str) : decimal(to_exponent_mantissa(str)) {}
43 decimal(double x) noexcept : decimal(to_exponent_mantissa(x)) {}
44 decimal(float x) noexcept : decimal(to_exponent_mantissa(x)) {}
45 constexpr decimal(signed long long x) : decimal(0, x) {}
46 constexpr decimal(signed long x) : decimal(0, static_cast<signed long long>(x)) {}
47 constexpr decimal(signed int x) : decimal(0, static_cast<signed long long>(x)) {}
48 constexpr decimal(signed short x) : decimal(0, static_cast<signed long long>(x)) {}
49 constexpr decimal(signed char x) : decimal(0, static_cast<signed long long>(x)) {}
50 constexpr decimal(unsigned long long x) : decimal(0, x) {}
51 constexpr decimal(unsigned long x) : decimal(0, static_cast<signed long long>(x)) {}
52 constexpr decimal(unsigned int x) : decimal(0, static_cast<signed long long>(x)) {}
53 constexpr decimal(unsigned short x) : decimal(0, static_cast<signed long long>(x)) {}
54 constexpr decimal(unsigned char x) : decimal(0, static_cast<signed long long>(x)) {}
55
56 constexpr decimal &operator=(std::pair<int, long long> other) noexcept
57 {
58 value = decimal::pack(other.first, other.second);
59 return *this;
60 }
61 decimal &operator=(std::string_view str) noexcept
62 {
63 return *this = to_exponent_mantissa(str);
64 }
65 constexpr decimal &operator=(double other) noexcept
66 {
67 return *this = to_exponent_mantissa(other);
68 }
69 constexpr decimal &operator=(float other) noexcept
70 {
71 return *this = to_exponent_mantissa(other);
72 }
73 constexpr decimal &operator=(signed long long other) noexcept
74 {
75 value = decimal::pack(0, other);
76 return *this;
77 }
78 constexpr decimal &operator=(signed long other) noexcept
79 {
80 return *this = static_cast<signed long long>(other);
81 }
82 constexpr decimal &operator=(signed int other) noexcept
83 {
84 return *this = static_cast<signed long long>(other);
85 }
86 constexpr decimal &operator=(signed short other) noexcept
87 {
88 return *this = static_cast<signed long long>(other);
89 }
90 constexpr decimal &operator=(signed char other) noexcept
91 {
92 return *this = static_cast<signed long long>(other);
93 }
94 constexpr decimal &operator=(unsigned long long other) noexcept
95 {
96 return *this = static_cast<signed long long>(other);
97 }
98 constexpr decimal &operator=(unsigned long other) noexcept
99 {
100 return *this = static_cast<signed long long>(other);
101 }
102 constexpr decimal &operator=(unsigned int other) noexcept
103 {
104 return *this = static_cast<signed long long>(other);
105 }
106 constexpr decimal &operator=(unsigned short other) noexcept
107 {
108 return *this = static_cast<signed long long>(other);
109 }
110 constexpr decimal &operator=(unsigned char other) noexcept
111 {
112 return *this = static_cast<signed long long>(other);
113 }
114
115 explicit operator signed long long()
116 {
117 auto e = exponent();
118 auto m = mantissa();
119
120 while (e < 0) {
121 m /= 10;
122 e++;
123 }
124
125 while (e > 0) {
126 m *= 10;
127 e--;
128 if (!is_valid_mantissa(m)) {
130 }
131 }
132 return m;
133 }
134
135 explicit operator signed long()
136 {
137 return narrow_cast<signed long>(static_cast<signed long long>(*this));
138 }
139 explicit operator signed int()
140 {
141 return narrow_cast<signed int>(static_cast<signed long long>(*this));
142 }
143 explicit operator signed short()
144 {
145 return narrow_cast<signed short>(static_cast<signed long long>(*this));
146 }
147 explicit operator signed char()
148 {
149 return narrow_cast<signed char>(static_cast<signed long long>(*this));
150 }
151 explicit operator unsigned long long()
152 {
153 return narrow_cast<unsigned int>(static_cast<signed long long>(*this));
154 }
155 explicit operator unsigned long()
156 {
157 return narrow_cast<unsigned long>(static_cast<signed long long>(*this));
158 }
159 explicit operator unsigned int()
160 {
161 return narrow_cast<unsigned int>(static_cast<signed long long>(*this));
162 }
163 explicit operator unsigned short()
164 {
165 return narrow_cast<unsigned short>(static_cast<signed long long>(*this));
166 }
167 explicit operator unsigned char()
168 {
169 return narrow_cast<unsigned char>(static_cast<signed long long>(*this));
170 }
171 explicit operator long double()
172 {
173 return static_cast<long double>(mantissa()) * powl(10.0, exponent());
174 }
175 explicit operator double()
176 {
177 return static_cast<double>(static_cast<long double>(*this));
178 }
179 explicit operator float()
180 {
181 return static_cast<float>(static_cast<long double>(*this));
182 }
183
184 size_t hash() const noexcept
185 {
186 auto v = this->normalize();
187 return std::hash<uint64_t>{}(v.value);
188 }
189
194 [[nodiscard]] constexpr int exponent() const noexcept
195 {
196 return static_cast<int8_t>(value);
197 }
198
203 [[nodiscard]] constexpr long long mantissa() const noexcept
204 {
205 return static_cast<int64_t>(value) >> 8;
206 }
207
208 [[nodiscard]] constexpr std::pair<int, long long> exponent_mantissa() const noexcept
209 {
210 return {exponent(), mantissa()};
211 }
212
218 [[nodiscard]] constexpr decimal normalize() const noexcept
219 {
220 ttlet[e, m] = exponent_mantissa();
221 ttlet[e_, m_] = decimal::normalize(e, m);
222 return {e_, m_};
223 }
224
225 decimal &operator+=(decimal rhs) noexcept
226 {
227 ttlet[e, lhs_m, rhs_m] = decimal::align(*this, rhs);
228 value = decimal::pack(e, lhs_m + rhs_m);
229 return *this;
230 }
231
232 decimal &operator-=(decimal rhs) noexcept
233 {
234 ttlet[e, lhs_m, rhs_m] = decimal::align(*this, rhs);
235 value = decimal::pack(e, lhs_m - rhs_m);
236 return *this;
237 }
238
239 decimal &operator*=(decimal rhs) noexcept
240 {
241 return *this = *this * rhs;
242 }
243
244 decimal &operator/=(decimal rhs) noexcept
245 {
246 return *this = *this / rhs;
247 }
248
249public:
250 [[nodiscard]] friend bool operator==(decimal lhs, decimal rhs) noexcept
251 {
252 ttlet[e, lhs_m, rhs_m] = decimal::align(lhs, rhs);
253 return lhs_m == rhs_m;
254 }
255
256 [[nodiscard]] friend bool operator<(decimal lhs, decimal rhs) noexcept
257 {
258 ttlet[e, lhs_m, rhs_m] = decimal::align(lhs, rhs);
259 return lhs_m < rhs_m;
260 }
261
262 [[nodiscard]] friend bool operator!=(decimal lhs, decimal rhs) noexcept
263 {
264 return !(lhs == rhs);
265 }
266 [[nodiscard]] friend bool operator>(decimal lhs, decimal rhs) noexcept
267 {
268 return rhs < lhs;
269 }
270 [[nodiscard]] friend bool operator<=(decimal lhs, decimal rhs) noexcept
271 {
272 return !(lhs > rhs);
273 }
274 [[nodiscard]] friend bool operator>=(decimal lhs, decimal rhs) noexcept
275 {
276 return !(lhs < rhs);
277 }
278
279 [[nodiscard]] friend constexpr decimal operator-(decimal rhs) noexcept
280 {
281 return {rhs.exponent(), -rhs.mantissa()};
282 }
283
284 [[nodiscard]] friend decimal operator+(decimal lhs, decimal rhs) noexcept
285 {
286 ttlet[e, lhs_m, rhs_m] = decimal::align(lhs, rhs);
287 return {e, lhs_m + rhs_m};
288 }
289
290 [[nodiscard]] friend decimal operator-(decimal lhs, decimal rhs) noexcept
291 {
292 ttlet[e, lhs_m, rhs_m] = decimal::align(lhs, rhs);
293 return {e, lhs_m - rhs_m};
294 }
295
296 [[nodiscard]] friend decimal operator*(decimal lhs, decimal rhs) noexcept
297 {
298 auto lhs_e = lhs.exponent();
299 auto lhs_m = lhs.mantissa();
300 auto rhs_e = rhs.exponent();
301 auto rhs_m = rhs.mantissa();
302
303 long long m = 0;
304 if (!mul_overflow(lhs_m, rhs_m, &m)) {
305 [[likely]] return {lhs_e + rhs_e, m};
306 }
307
308 // Try with normalized decimals, this is without loss of precision.
309 std::tie(lhs_e, lhs_m) = decimal::normalize(lhs_e, lhs_m);
310 std::tie(rhs_e, rhs_m) = decimal::normalize(rhs_e, rhs_m);
311 while (mul_overflow(lhs_m, rhs_m, &m)) {
312 // Reduce precision of largest mantissa until multiplication succeeds.
313 if (lhs_m > rhs_m) {
314 lhs_m += 5;
315 lhs_m /= 10;
316 lhs_e++;
317 } else {
318 rhs_m += 5;
319 rhs_m /= 10;
320 rhs_e++;
321 }
322 }
323 return {lhs_e + rhs_e, m};
324 }
325
326 [[nodiscard]] friend decimal operator/(decimal lhs, decimal rhs) noexcept
327 {
328 auto rhs_m = rhs.mantissa();
329 tt_axiom(rhs_m != 0);
330 auto rhs_e = rhs.exponent();
331 auto lhs_m = lhs.mantissa();
332 auto lhs_e = lhs.exponent();
333
334 std::tie(lhs_e, lhs_m) = decimal::denormalize(lhs_e, lhs_m);
335 return {lhs_e - rhs_e, lhs_m / rhs_m};
336 }
337
338 [[nodiscard]] friend decimal operator%(decimal lhs, decimal rhs) noexcept
339 {
340 auto rhs_m = rhs.mantissa();
341 tt_axiom(rhs_m != 0);
342 auto rhs_e = rhs.exponent();
343 auto lhs_m = lhs.mantissa();
344 auto lhs_e = lhs.exponent();
345
346 std::tie(lhs_e, lhs_m) = decimal::denormalize(lhs_e, lhs_m);
347 return {lhs_e - rhs_e, lhs_m % rhs_m};
348 }
349
350 [[nodiscard]] friend std::string to_string(decimal x) noexcept
351 {
352 auto [e, m] = x.exponent_mantissa();
353 auto s = std::to_string(std::abs(m));
354
355 auto decimal_position = -e;
356 auto leading_zeros = (decimal_position - std::ssize(s)) + 1;
357 if (leading_zeros > 0) {
358 s.insert(0, leading_zeros, '0');
359 }
360
361 auto trailing_zeros = e;
362 if (trailing_zeros > 0) {
363 s.append(trailing_zeros, '0');
364 }
365
366 if (decimal_position > 0) {
367 s.insert(s.size() - decimal_position, 1, '.');
368 }
369
370 if (m < 0) {
371 s.insert(0, 1, '-');
372 }
373
374 return s;
375 }
376
377 friend std::ostream &operator<<(std::ostream &lhs, decimal rhs)
378 {
379 return lhs << to_string(rhs);
380 }
381
382private:
385 [[nodiscard]] constexpr static std::pair<int, long long> normalize(int e, long long m) noexcept
386 {
387 if (m != 0) {
388 while (m % 10 == 0) {
389 m /= 10;
390 e++;
391 }
392 }
393 return {e, m};
394 }
395
398 [[nodiscard]] constexpr static std::pair<int, long long> denormalize(int e, long long m) noexcept
399 {
400 if (m != 0) {
401 // The mantissa is allowed to go slightly over the maximum; since it
402 // is used mostly for the rhs of a division, which means the result will,
403 // in all probability, make the mantissa smaller than the maximum.
404 while (is_valid_mantissa(m)) {
405 m *= 10;
406 e--;
407 }
408 }
409 return {e, m};
410 }
411
415 [[nodiscard]] constexpr static bool is_valid_mantissa(long long m) noexcept
416 {
417 m >>= (mantissa_bits - 1);
418 return m == 0 || m == -1;
419 }
420
424 [[nodiscard]] constexpr static bool is_valid_exponent(int e) noexcept
425 {
426 e >>= (exponent_bits - 1);
427 return e == 0 || e == -1;
428 }
429
430 [[nodiscard]] static std::tuple<int, long long, long long> align(decimal lhs, decimal rhs) noexcept
431 {
432 auto lhs_e = lhs.exponent();
433 auto lhs_m = lhs.mantissa();
434 auto rhs_e = rhs.exponent();
435 auto rhs_m = rhs.mantissa();
436
437 if (lhs_e == rhs_e) {
438 // No alignment needed.
439 } else if (lhs_e > rhs_e) {
440 do {
441 lhs_m *= 10;
442 lhs_e--;
443 if (!is_valid_mantissa(lhs_m)) {
444 while (lhs_e > rhs_e) {
445 rhs_m /= 10;
446 rhs_e++;
447 }
448 break;
449 }
450 } while (lhs_e > rhs_e);
451 } else {
452 do {
453 rhs_m *= 10;
454 rhs_e--;
455 if (!is_valid_mantissa(lhs_m)) {
456 while (lhs_e < rhs_e) {
457 lhs_m /= 10;
458 lhs_e++;
459 }
460 break;
461 }
462 } while (lhs_e < rhs_e);
463 }
464 return {lhs_e, lhs_m, rhs_m};
465 }
466
469 [[nodiscard]] constexpr static uint64_t pack(int e, long long m) noexcept
470 {
471 // Adjust an mantissa that is too large. Precision may be lost.
472 while (!is_valid_mantissa(m)) {
473 [[unlikely]] m /= 10;
474 e++;
475 tt_assert(e <= exponent_max);
476 }
477
478 while (e > exponent_max) {
479 [[unlikely]] if ((m *= 10) == 0)
480 {
481 e = exponent_max;
482 break;
483 }
484 e--;
485
486 // abort on overflow. This decimal does not support infinite.
487 tt_assert(is_valid_mantissa(m));
488 }
489
490 while (e < exponent_min) {
491 [[unlikely]] if ((m /= 10) == 0)
492 {
493 e = exponent_min;
494 break;
495 }
496 e++;
497 }
498
499 return static_cast<uint64_t>(m) << exponent_bits | static_cast<uint8_t>(e);
500 }
501
502 [[nodiscard]] static std::pair<int, long long> to_exponent_mantissa(double x) noexcept
503 {
504 uint64_t x_;
505 std::memcpy(&x_, &x, sizeof(x_));
506
507 auto e2 = static_cast<int>((x_ << 1) >> 53) - 1023 - 52;
508 auto m = static_cast<long long>((x_ << 12) >> 12);
509 if (e2 > (-1023 - 52)) {
510 // Add leading '1'.
511 m |= (1LL << 52);
512 }
513
514 if (static_cast<int64_t>(x_) < 0) {
515 m = -m;
516 }
517
518 if (m == 0) {
519 return {0, 0};
520 }
521
522 int e10 = 0;
523 while (e2 < 0) {
524 while (is_valid_mantissa(m)) {
525 m *= 10;
526 e10--;
527 }
528 m /= 2;
529 e2++;
530 }
531
532 while (e2 > 0) {
533 while (!is_valid_mantissa(m)) {
534 m /= 10;
535 e10++;
536 }
537 m *= 2;
538 e2--;
539 }
540
541 return {e10, m};
542 }
543
544 [[nodiscard]] std::pair<int, long long> to_exponent_mantissa(std::string_view str)
545 {
546 std::string mantissa_str;
547
548 int nr_digits = 0;
549 int nr_digits_in_front_of_point = -1;
550 for (ttlet c : str) {
551 if (c >= '0' && c <= '9') {
552 mantissa_str += c;
553 nr_digits++;
554 } else if (c == '.') {
555 nr_digits_in_front_of_point = nr_digits;
556 } else if (c == '\'' || c == ',') {
557 // Ignore thousand separators.
558 } else if (c == '-') {
559 mantissa_str += c;
560 } else {
561 throw parse_error("Unexpected character in decimal number '{}'", str);
562 }
563 }
564
565 int exponent = (nr_digits_in_front_of_point >= 0) ? (nr_digits_in_front_of_point - nr_digits) : 0;
566
567 auto first = mantissa_str.data();
568 auto last = first + mantissa_str.size();
569 long long mantissa;
570 auto result = std::from_chars(first, last, mantissa, 10);
571 if (result.ptr == first) {
572 throw parse_error("Could not parse mantissa '{}'", mantissa_str);
573 } else if (result.ec == std::errc::result_out_of_range) {
574 throw parse_error("Mantissa '{}' out of range ", mantissa_str);
575 } else {
576 return {exponent, mantissa};
577 }
578 }
579};
580
581} // namespace tt
582
583namespace std {
584
585template<>
586struct hash<tt::decimal> {
587 inline size_t operator()(tt::decimal const &value) const
588 {
589 return value.hash();
590 }
591};
592
593} // namespace std
STL namespace.
Definition decimal.hpp:19
constexpr decimal normalize() const noexcept
Return a normalized decimal.
Definition decimal.hpp:218
constexpr long long mantissa() const noexcept
Extract mantissa from value.
Definition decimal.hpp:203
constexpr int exponent() const noexcept
Extract exponent from value.
Definition decimal.hpp:194
T data(T... args)
T memcpy(T... args)
T operator()(T... args)
T size(T... args)
T terminate(T... args)
T tie(T... args)
T to_string(T... args)