SST 15.0
Structural Simulation Toolkit
decimal_fixedpoint.h
1// -*- c++ -*-
2
3// Copyright 2009-2025 NTESS. Under the terms
4// of Contract DE-NA0003525 with NTESS, the U.S.
5// Government retains certain rights in this software.
6//
7// Copyright (c) 2009-2025, NTESS
8// All rights reserved.
9//
10// This file is part of the SST software package. For license
11// information, see the LICENSE file in the top level directory of the
12// distribution.
13
14#ifndef SST_CORE_DECIMAL_FIXEDPOINT_H
15#define SST_CORE_DECIMAL_FIXEDPOINT_H
16
17#include "sst/core/from_string.h"
18
19#include <cstdint>
20#include <iomanip>
21#include <limits>
22#include <sstream>
23#include <type_traits>
24
25namespace SST {
26/**
27 Class that implements a decimal fixed-point number.
28
29 Fixed point class that stores digits in radix-10. Size is
30 specified in words, and each word represents 8 digits.
31
32 @tparam whole_words Number of words used to represent the digits to
33 the left of the decimal point. Each word represents 8 decimal
34 digits.
35
36 @tparam fraction_words Number of words used to represent the digits
37 to the right of the decimal point. Each word represents 8 decimal
38 digits.
39*/
40template <int whole_words, int fraction_words>
42{
43
44public:
45 static constexpr uint32_t storage_radix = 100000000;
46 static constexpr uint64_t storage_radix_long = 100000000l;
47 static constexpr int32_t digits_per_word = 8;
48
49 /**
50 Get the value of whole_words template parameter.
51 */
52 constexpr int getWholeWords() const { return whole_words; }
53
54 /**
55 Get the value of fraction_words template parameter.
56 */
57 constexpr int getFractionWords() const { return fraction_words; }
58
59private:
60 // I should be a friend of all versions of this class
61 template <int A, int B>
62 friend class sst_dec_fixed;
63
64 /**
65 Data representing the digits of the number.
66
67 Represents 8 decimal digits per 32-bits. Each digit will not
68 be represented independently, but rather all 8 digits will be
69 represented as a number of parts out of 100,000,000.
70 Essentially, we're storing as radix 100,000,000. Index 0 holds
71 the least significant digits
72 */
73 uint32_t data[whole_words + fraction_words];
74
75 /**
76 Represents if the number is negative or not.
77 */
78 bool negative;
79
80 /**
81 initialize a decimal_fixedpoint using a string initializer
82
83 @param init Initialization string. The format is similar to
84 the c++ double precision strings. For example 1.234, -1.234,
85 0.234, 1.234e14, 1.234e14, etc.
86 */
87 void from_string(const std::string& init_str)
88 {
89 std::string init(init_str);
90 negative = false;
91 for ( int i = 0; i < whole_words + fraction_words; ++i ) {
92 data[i] = 0;
93 }
94
95 // Look for a negative sign
96 if ( init[0] == '-' ) {
97 negative = true;
98 init = init.substr(1, init.npos);
99 }
100
101 // See if we have an exponent
102 size_t exponent_pos = init.find_last_of("eE");
103 int32_t exponent = 0;
104 if ( exponent_pos != init.npos ) {
105 exponent = static_cast<int32_t>(SST::Core::from_string<double>(init.substr(exponent_pos + 1, init.npos)));
106 init = init.substr(0, exponent_pos);
107 }
108
109 int dp = init.length();
110 for ( size_t i = 0; i < init.length(); ++i ) {
111 if ( init[i] == '.' ) {
112 dp = i;
113 }
114 }
115
116 // get rid of the decimal point
117 init.erase(dp, 1);
118
119 // pos of decimal pt digits after dec pt
120 int start_of_digits = (fraction_words * digits_per_word) - (init.length() - dp) + exponent;
121 // Convert digits to numbers
122
123 // Compute the first mult
124 int start_pos_word = start_of_digits % digits_per_word;
125 uint32_t mult = 1;
126 for ( int i = 0; i < start_pos_word; i++ ) {
127 mult *= 10;
128 }
129
130 for ( int i = init.length() - 1; i >= 0; --i ) {
131 int digit = start_of_digits + (init.length() - 1 - i);
132 int word = (digit / digits_per_word);
133
134 data[word] += (SST::Core::from_string<uint32_t>(init.substr(i, 1)) * mult);
135 mult *= 10;
136 if ( mult == storage_radix ) mult = 1;
137 }
138 }
139
140 /**
141 Initialize a decimal_fixedpoint using a 64-bit unsigned number.
142
143 @param init Initialization value.
144 */
145 void from_uint64(uint64_t init)
146 {
147 negative = false;
148
149 for ( int i = 0; i < whole_words + fraction_words; ++i ) {
150 data[i] = 0;
151 }
152
153 // A 64-bit integer can use at most two of the words
154 // uint64_t factor = ( storage_radix_long * storage_radix_long );
155 // for ( int i = 2; i > 0; --i ) {
156 // data[i+fraction_words] = init / factor;
157 // init -= data[i+fraction_words] * factor;
158 // factor /= storage_radix_long;
159 // }
160 // data[fraction_words] = init;
161
162 for ( int i = fraction_words; i < whole_words + fraction_words; ++i ) {
163 data[i] = init % storage_radix_long;
164 init /= storage_radix_long;
165 }
166 }
167
168 /**
169 Initialize a decimal_fixedpoint using a double.
170
171 @param init Initialization value.
172 */
173 void from_double(double init)
174 {
175 negative = false;
176
177 for ( int i = 0; i < whole_words + fraction_words; ++i ) {
178 data[i] = 0;
179 }
180 double factor = 1;
181
182 for ( int i = 0; i < whole_words - 1; ++i ) {
183 factor *= storage_radix;
184 }
185
186 for ( int i = whole_words + fraction_words - 1; i >= 0; --i ) {
187 data[i] = static_cast<uint32_t>(init / factor);
188 init -= (data[i] * factor);
189 factor /= storage_radix;
190 }
191 }
192
193public:
194 /**
195 Default constructor.
196
197 Builds a decimal_fixedpoint with the value 0;
198 */
200 {
201 negative = false;
202 for ( int i = 0; i < whole_words + fraction_words; ++i ) {
203 data[i] = 0;
204 }
205 }
206
207 /**
208 Build a decimal_fixedpoint using a string initializer.
209
210 @param init Initialization string. The format is similar to
211 the c++ double precision strings. For example 1.234, -1.234,
212 0.234, 1.234e14, 1.234e14, etc.
213 */
214 explicit decimal_fixedpoint(const std::string& init) { from_string(init); }
215
216 /**
217 Build a decimal_fixedpoint using a 64-bit unsigned number.
218
219 @param init Initialization value.
220 */
221 template <class T>
222 decimal_fixedpoint(T init, std::enable_if_t<std::is_unsigned_v<T>>* = nullptr)
223 {
224 from_uint64(init);
225 }
226
227 /**
228 Build a decimal_fixedpoint using a 64-bit signed number.
229
230 @param init Initialization value.
231 */
232 template <class T>
233 decimal_fixedpoint(T init, std::enable_if_t<std::is_signed_v<T> && std::is_integral_v<T>>* = nullptr)
234 {
235 if ( init < 0 ) {
236 from_uint64(-init);
237 negative = true;
238 }
239 else {
240 from_uint64(init);
241 }
242 }
243
244 /**
245 Build a decimal_fixedpoint using a double.
246
247 @param init Initialization value.
248 */
249 template <class T>
250 decimal_fixedpoint(const T init, std::enable_if_t<std::is_floating_point_v<T>>* = nullptr)
251 {
252 from_double(init);
253 }
254
255 /**
256 Build a decimal_fixedpoint using another decimal_fixedpoint.
257
258 @param init Initialization value.
259 */
261 {
262 negative = init.negative;
263 for ( int i = 0; i < whole_words + fraction_words; ++i ) {
264 data[i] = init.data[i];
265 }
266 }
267
268 /**
269 Equal operator for other decimal_fixedpoint objects.
270 */
272 {
273 negative = v.negative;
274 for ( int i = 0; i < whole_words + fraction_words; ++i ) {
275 data[i] = v.data[i];
276 }
277 return *this;
278 }
279
280 /**
281 Equal operator for 64-bit unsigned int.
282 */
284 {
285 from_uint64(v);
286 return *this;
287 }
288
289 /**
290 Equal operator for 64-bit signed int.
291 */
293 {
294 if ( v < 0 ) {
295 from_uint64(-v);
296 negative = true;
297 }
298 else {
299 from_uint64(v);
300 }
301 return *this;
302 }
303
304 /**
305 Equal operator for double.
306 */
308 {
309 from_double(v);
310 return *this;
311 }
312
313 /**
314 Equal operator for string.
315 */
316 decimal_fixedpoint& operator=(const std::string& v)
317 {
318 from_string(v);
319 return *this;
320 }
321
322 /**
323 Negate the value (change the sign bit).
324 */
325 void negate() { negative = negative ^ 0x1; }
326
327 /**
328 Return a double precision version of the decimal_fixedpoint.
329 There is possible precision loss in this conversion.
330 */
331 double toDouble() const
332 {
333 double ret = 0;
334 double factor = 1;
335 for ( int i = 0; i < fraction_words; ++i ) {
336 factor /= storage_radix;
337 }
338
339 for ( int i = 0; i < whole_words + fraction_words; ++i ) {
340 ret += (static_cast<double>(data[i]) * factor);
341 factor *= storage_radix;
342 }
343
344 return ret;
345 }
346
347 /**
348 Return a int64_t version of the decimal_fixedpoint. There is
349 possible precision loss in this conversion.
350 */
351 int64_t toLong() const
352 {
353 int64_t ret = 0;
354 int64_t factor = 1;
355 for ( int i = 0; i < whole_words; ++i ) {
356 ret += (static_cast<int64_t>(data[fraction_words + i]) * factor);
357 factor *= storage_radix;
358 }
359
360 // Check to see if we need to round
361 bool round = false;
362 if ( data[fraction_words - 1] > (storage_radix / 2) ) {
363 round = true;
364 }
365 else if ( data[fraction_words - 1] == (storage_radix / 2) ) {
366 for ( int i = fraction_words - 2; i >= 0; --i ) {
367 if ( data[i] != 0 ) {
368 round = true;
369 break;
370 }
371 }
372
373 if ( !round ) {
374 // There were no extra digits, so we need to do a
375 // round to nearest even.
376 if ( ret % 2 == 1 ) round = true;
377 }
378 }
379 if ( round ) ++ret;
380 if ( negative ) ret = -ret;
381 return ret;
382 }
383
384 /**
385 Return a uint64_t version of the decimal_fixedpoint. There is
386 possible precision loss in this conversion.
387 */
388 uint64_t toUnsignedLong() const
389 {
390 uint64_t ret = 0;
391 uint64_t factor = 1;
392 for ( int i = 0; i < whole_words; ++i ) {
393 ret += (static_cast<int64_t>(data[i]) * factor);
394 factor *= storage_radix;
395 }
396 return ret;
397 }
398
399 /**
400 Return true if value is zero, otherwise return false.
401 */
402 bool isZero() const
403 {
404 for ( int i = whole_words + fraction_words - 1; i >= 0; --i ) {
405 if ( data[i] != 0 ) return false;
406 }
407 return true;
408 }
409
410 /**
411 Templated conversion function for unsigned types.
412 */
413 template <typename T>
414 T convert_to(std::enable_if_t<std::is_unsigned_v<T>>* = nullptr) const
415 {
416 return static_cast<T>(toUnsignedLong());
417 }
418
419 /**
420 Templated conversion function for signed integral types.
421 */
422 template <typename T>
423 T convert_to(std::enable_if_t<std::is_signed_v<T> && std::is_integral_v<T>>* = nullptr) const
424 {
425 return static_cast<T>(toLong());
426 }
427
428 /**
429 Templated conversion function for floating point types.
430 */
431 template <typename T>
432 T convert_to(std::enable_if_t<std::is_floating_point_v<T>>* = nullptr) const
433 {
434 return static_cast<T>(toDouble());
435 }
436
437 /**
438 Create a string representation of this decimal_fixedpoint.
439
440 @param precision Precision to use when printing number
441
442 */
443 std::string toString(int32_t precision = 6) const
444 {
445
446 std::stringstream stream;
447 if ( precision <= 0 || precision > ((whole_words + fraction_words) * digits_per_word) )
448 precision = (whole_words + fraction_words) * digits_per_word;
449
450 // First create a digit by digit representation so we can
451 // reason about what to print based on the precision.
452 constexpr int num_digits = (whole_words + fraction_words) * digits_per_word;
453
454 unsigned char digits[num_digits];
455 for ( int i = 0; i < whole_words + fraction_words; ++i ) {
456 uint32_t value = data[i];
457 for ( int j = 0; j < digits_per_word; ++j ) {
458 digits[i * digits_per_word + j] = value % 10;
459 value /= 10;
460 }
461 }
462
463 // Find the first non-zero
464 int first_non_zero = -1;
465 for ( int i = num_digits - 1; i >= 0; --i ) {
466 if ( digits[i] != 0 ) {
467 first_non_zero = i;
468 break;
469 }
470 }
471
472 // If no non-zeros, return 0
473 if ( first_non_zero == -1 ) return "0";
474
475 // Now, we will round to the precision asked for
476 int round_position = first_non_zero - precision;
477 bool round = false;
478
479 // If round_position < 0 then we have already gotten all the
480 // digits that exist, so no need to round
481 if ( round_position >= 0 ) {
482 if ( digits[round_position] > 5 )
483 round = true;
484 else if ( digits[round_position] < 5 )
485 round = false;
486 else { // Round if the rest aren't zeros
487
488 for ( int i = round_position - 1; i >= 0; --i ) {
489 if ( digits[i] != 0 ) {
490 round = true;
491 break;
492 }
493 }
494 if ( !round ) {
495 // There were no extra zeros, so we need to do round
496 // to nearest even.
497 if ( digits[round_position + 1] % 2 == 1 ) round = true;
498 }
499 }
500
501 if ( round ) {
502 // Do the round
503 unsigned char carry = 1;
504 for ( int i = round_position + 1; i < num_digits; ++i ) {
505 digits[i] += carry;
506 carry = digits[i] / 10;
507 digits[i] = digits[i] % 10;
508 }
509 }
510
511 // Zero out all the bits beyond the precision
512 for ( int i = 0; i <= round_position; ++i ) {
513 digits[i] = 0;
514 }
515 }
516
517 // print possible negative sign
518 if ( negative ) stream << '-';
519
520 // Find the first non-zero
521 first_non_zero = -1;
522 for ( int i = num_digits - 1; i >= 0; --i ) {
523 if ( digits[i] != 0 ) {
524 first_non_zero = i;
525 break;
526 }
527 }
528
529 // Check for overflow in the round
530 if ( first_non_zero == -1 ) {
531 // This means we rounded to a number bigger than we can
532 // support
533 stream << "1e+" << (whole_words * digits_per_word);
534 return stream.str();
535 }
536
537 // There are several cases to cover:
538
539 // Need to switch to exponent notation for numbers > 1
540 if ( first_non_zero >= ((fraction_words * digits_per_word) + precision) ) {
541 // Need to use exponent notation
542 int exponent = first_non_zero - (fraction_words * digits_per_word);
543 stream << static_cast<uint32_t>(digits[first_non_zero]) << ".";
544 int zeros = 0;
545 for ( int i = first_non_zero - 1; i >= first_non_zero - precision; --i ) {
546 // Avoid printing trailing zeros. Keep track of runs
547 // of zeros and only print them if we hit another
548 // non-xero
549 if ( digits[i] == 0 )
550 zeros++;
551 else {
552 for ( int j = 0; j < zeros; ++j ) {
553 stream << "0";
554 }
555 stream << static_cast<uint32_t>(digits[i]);
556 zeros = 0;
557 }
558 }
559 std::string ret = stream.str();
560 if ( ret[ret.length() - 1] == '.' ) {
561 ret = ret.substr(0, ret.length() - 1);
562 stream.str(std::string(""));
563 stream << ret;
564 }
565 stream << "e+" << std::setfill('0') << std::setw(2) << exponent;
566 // return stream.str();
567 }
568
569 // Decimal point is within the string of digits to print
570 else if ( first_non_zero >= (fraction_words * digits_per_word) ) {
571 int zeros = 0;
572 for ( int i = first_non_zero; i >= (fraction_words * digits_per_word); --i ) {
573 // Digits before the decimal point
574 stream << static_cast<uint32_t>(digits[i]);
575 }
576
577 stream << ".";
578
579 for ( int i = (fraction_words * digits_per_word) - 1; i >= first_non_zero - precision && (i >= 0); --i ) {
580 // Avoid printing trailing zeros. Keep track of runs
581 // of zeros and only print them if we hit another
582 // non-xero
583 if ( digits[i] == 0 )
584 zeros++;
585 else {
586 for ( int j = 0; j < zeros; ++j ) {
587 stream << "0";
588 }
589 stream << static_cast<uint32_t>(digits[i]);
590 zeros = 0;
591 }
592 }
593 std::string ret = stream.str();
594 if ( ret[ret.length() - 1] == '.' ) {
595 ret = ret.substr(0, ret.length() - 1);
596 stream.str(std::string(""));
597 stream << ret;
598 }
599 }
600
601 // No whole digits, but not switching to exponent notation
602 // yet. We are willing to print three leading zeros before
603 // switching to exponent notation
604 else if ( first_non_zero > (fraction_words * digits_per_word) - 5 ) {
605 stream << "0.";
606 for ( int i = (fraction_words * digits_per_word) - 1; i > first_non_zero; --i ) {
607 stream << "0";
608 }
609 int zeros = 0;
610 for ( int i = first_non_zero; (i >= first_non_zero - precision) && (i >= 0); --i ) {
611 // Avoid printing trailing zeros. Keep track of runs
612 // of zeros and only print them if we hit another
613 // non-xero
614 if ( digits[i] == 0 )
615 zeros++;
616 else {
617 for ( int j = 0; j < zeros; ++j ) {
618 stream << "0";
619 }
620 stream << static_cast<uint32_t>(digits[i]);
621 zeros = 0;
622 }
623 }
624 }
625 // Switch to exponent notation
626 else {
627 // Need to use exponent notation
628 int exponent = first_non_zero - (fraction_words * digits_per_word);
629 exponent = -exponent;
630 stream << static_cast<uint32_t>(digits[first_non_zero]) << ".";
631 int zeros = 0;
632 for ( int i = first_non_zero - 1; (i >= first_non_zero - precision) && (i >= 0); --i ) {
633 // Avoid printing trailing zeros. Keep track of runs
634 // of zeros and only print them if we hit another
635 // non-xero
636 if ( digits[i] == 0 )
637 zeros++;
638 else {
639 for ( int j = 0; j < zeros; ++j ) {
640 stream << "0";
641 }
642 stream << static_cast<uint32_t>(digits[i]);
643 zeros = 0;
644 }
645 }
646 std::string ret = stream.str();
647 if ( ret[ret.length() - 1] == '.' ) {
648 ret = ret.substr(0, ret.length() - 1);
649 stream.str(std::string(""));
650 stream << ret;
651 }
652 stream << "e-" << std::setfill('0') << std::setw(2) << exponent;
653 }
654
655 return stream.str();
656 }
657
658 /**
659 Adds another number to this one and sets it equal to the
660 result.
661
662 @param v Number to add to this one
663 */
665 {
666 // Depending on the signs, this may be a subtract
667 if ( (negative ^ v.negative) == 0 ) {
668 // Signs match, just do an add
669 // Calculate the result for each digit.
670 uint64_t carry_over = 0;
671 for ( int i = 0; i < whole_words + fraction_words; i++ ) {
672 uint64_t value = static_cast<uint64_t>(data[i]) + static_cast<uint64_t>(v.data[i]) + carry_over;
673 carry_over = value / storage_radix;
674 // data[i] = static_cast<uint32_t>(value % storage_radix);
675 data[i] = value % storage_radix;
676 }
677 return *this;
678 }
679
680 // Signs don't match, so we need to sort
681 if ( operator>=(v) ) {
682 // Calculate the result for each digit. Need to negate the
683 // second operand and pass a 1 in as carry-in (carry-over).
684 uint64_t carry_over = 1;
685 for ( int i = 0; i < whole_words + fraction_words; i++ ) {
686 uint64_t negate = static_cast<uint64_t>(storage_radix - 1) - static_cast<uint64_t>(v.data[i]);
687
688 uint64_t value = static_cast<uint64_t>(data[i]) + negate + carry_over;
689 carry_over = value / storage_radix;
690 data[i] = static_cast<uint32_t>(value % storage_radix);
691 }
692 return *this;
693 }
694 else {
695 // Calculate the result for each digit. Need to negate the
696 // second operand and pass a 1 in as carry-in (carry-over).
697 uint64_t carry_over = 1;
698 for ( int i = 0; i < whole_words + fraction_words; i++ ) {
699 uint64_t negate = static_cast<uint64_t>(storage_radix - 1) - static_cast<uint64_t>(data[i]);
700
701 uint64_t value = static_cast<uint64_t>(v.data[i]) + negate + carry_over;
702 carry_over = value / storage_radix;
703 data[i] = static_cast<uint32_t>(value % storage_radix);
704 }
705 // Since the other one is bigger, I take his sign
706 negative = v.negative;
707 return *this;
708 }
709 }
710
711 /**
712 Subtracts another number from this one and sets it equal to the
713 result.
714
715 @param v Number to subtract from this one
716 */
718 {
719 decimal_fixedpoint ret(v);
720 ret.negate();
721 return operator+=(ret);
722 }
723
724 /**
725 Multiplies another number to this one and sets it equal to the
726 result.
727
728 @param v Number to multiply to this one
729 */
731 {
732 // Need to do the multiply accumulate for each digit.
734
735 // The first "fraction_words" digits only matter as far as
736 // their carries go. They get dropped in the final output
737 // because they are less than the least significant digit.
738 uint64_t carry_over = 0;
739 for ( int i = 0; i < fraction_words; ++i ) {
740 uint64_t sum = carry_over;
741 for ( int j = 0; j <= i; ++j ) {
742 sum += static_cast<uint64_t>(me.data[j]) * static_cast<uint64_t>(v.data[i - j]);
743 }
744 carry_over = sum / storage_radix_long;
745 }
746
747 // Calculate the digits that we'll keep
748 for ( int i = fraction_words; i < whole_words + fraction_words; ++i ) {
749 uint64_t sum = carry_over;
750 for ( int j = 0; j <= i; ++j ) {
751 sum += static_cast<uint64_t>(me.data[j]) * static_cast<uint64_t>(v.data[i - j]);
752 }
753 carry_over = sum / storage_radix_long;
754 data[i - fraction_words] = static_cast<uint32_t>(sum % storage_radix_long);
755 }
756
757 for ( int i = 0; i < fraction_words; ++i ) {
758 uint64_t sum = carry_over;
759 for ( int j = i + 1; j < whole_words + fraction_words; ++j ) {
760 sum += static_cast<uint64_t>(me.data[j]) *
761 static_cast<uint64_t>(v.data[whole_words + fraction_words + i - j]);
762 }
763 carry_over = sum / storage_radix_long;
764 data[i + whole_words] = static_cast<uint32_t>(sum % storage_radix_long);
765 }
766 // Get the sign
767 negative = negative ^ v.negative;
768 return *this;
769 }
770
771 /**
772 Divides another number from this one and sets it equal to the
773 result.
774
775 @param v Number to divide from this one
776 */
778 {
779 decimal_fixedpoint inv(v);
780 inv.inverse();
781 operator*=(inv);
782 return *this;
783 }
784
785 /**
786 Inverts the number (1 divided by this number)
787 */
788
790 {
791 // We will use the Newton-Raphson method to compute the
792 // inverse
793
794 // First, get an estimate of the inverse by converting to a
795 // double and inverting
796 decimal_fixedpoint me(*this);
798 // decimal_fixedpoint<whole_words,fraction_words> inv("400");
799
801
802 // Since we converted to double to get an estimate of the
803 // inverse, we have approximated a double's worth of precision
804 // in our answer. We'll divide that by 2 for now, just to be
805 // save.
806 int digits_of_prec = std::numeric_limits<double>::digits10 / 2;
807
808 // Number of digits of precision doubles with each iteration
809 for ( int i = digits_of_prec; i <= (whole_words + fraction_words) * digits_per_word; i *= 2 ) {
810 decimal_fixedpoint temp(inv);
811 temp *= me;
812 temp -= two;
813 temp.negate();
814 inv *= temp;
815 }
816
817 *this = inv;
818 return *this;
819 }
820
821 /**
822 Checks to see if two numbers are equal
823
824 @param v Number to check equality against
825 */
826 bool operator==(const decimal_fixedpoint& v) const
827 {
828 for ( int i = whole_words + fraction_words - 1; i >= 0; --i ) {
829 if ( data[i] != v.data[i] ) return false;
830 }
831 return true;
832 }
833
834 /**
835 Checks to see if two numbers are not equal
836
837 @param v Number to check equality against
838 */
839 bool operator!=(const decimal_fixedpoint& v) const
840 {
841 for ( int i = whole_words + fraction_words - 1; i >= 0; --i ) {
842 if ( data[i] != v.data[i] ) return true;
843 }
844 return false;
845 }
846
847 /**
848 Checks to see if this number is greater than another number
849
850 @param v Number to compare to
851 */
852 bool operator>(const decimal_fixedpoint& v) const
853 {
854 for ( int i = whole_words + fraction_words - 1; i >= 0; --i ) {
855 if ( data[i] > v.data[i] ) return true;
856 if ( data[i] < v.data[i] ) return false;
857 }
858 return false;
859 }
860
861 /**
862 Checks to see if this number is greater than or equal to
863 another number
864
865 @param v Number to compare to
866 */
867 bool operator>=(const decimal_fixedpoint& v) const
868 {
869 for ( int i = whole_words + fraction_words - 1; i >= 0; --i ) {
870 if ( data[i] > v.data[i] ) return true;
871 if ( data[i] < v.data[i] ) return false;
872 }
873 return true;
874 }
875
876 /**
877 Checks to see if this number is less than another number
878
879 @param v Number to compare to
880 */
881 bool operator<(const decimal_fixedpoint& v) const
882 {
883 for ( int i = whole_words + fraction_words - 1; i >= 0; --i ) {
884 if ( data[i] < v.data[i] ) return true;
885 if ( data[i] > v.data[i] ) return false;
886 }
887 return false;
888 }
889
890 /**
891 Checks to see if this number is less than or equal to another
892 number
893
894 @param v Number to compare to
895 */
896 bool operator<=(const decimal_fixedpoint& v) const
897 {
898 for ( int i = whole_words + fraction_words - 1; i >= 0; --i ) {
899 if ( data[i] < v.data[i] ) return true;
900 if ( data[i] > v.data[i] ) return false;
901 }
902 return true;
903 }
904};
905
906template <int whole_words, int fraction_words>
907decimal_fixedpoint<whole_words, fraction_words>
908operator+(decimal_fixedpoint<whole_words, fraction_words> lhs, decimal_fixedpoint<whole_words, fraction_words> rhs)
909{
910 decimal_fixedpoint<whole_words, fraction_words> ret(lhs);
911 return ret += rhs;
912}
913
914template <int whole_words, int fraction_words>
917{
919 return ret -= rhs;
920}
921
922template <int whole_words, int fraction_words>
925{
927 return ret *= rhs;
928}
929
930template <int whole_words, int fraction_words>
933{
935 return ret /= rhs;
936}
937
938template <int whole_words, int fraction_words, typename T>
939bool
940operator==(const T& lhs, const decimal_fixedpoint<whole_words, fraction_words>& rhs)
941{
943}
944
945template <int whole_words, int fraction_words, typename T>
946bool
947operator!=(const T& lhs, const decimal_fixedpoint<whole_words, fraction_words>& rhs)
948{
950}
951
952template <int whole_words, int fraction_words>
953std::ostream&
954operator<<(std::ostream& os, const decimal_fixedpoint<whole_words, fraction_words>& rhs)
955{
956 os << rhs.toString(os.precision());
957 return os;
958}
959
960} // namespace SST
961
962#endif // SST_CORE_DECIMAL_FIXEDPOINT_H
Class that implements a decimal fixed-point number.
Definition decimal_fixedpoint.h:42
decimal_fixedpoint(const std::string &init)
Build a decimal_fixedpoint using a string initializer.
Definition decimal_fixedpoint.h:214
std::string toString(int32_t precision=6) const
Create a string representation of this decimal_fixedpoint.
Definition decimal_fixedpoint.h:443
decimal_fixedpoint & inverse()
Inverts the number (1 divided by this number)
Definition decimal_fixedpoint.h:789
bool operator>(const decimal_fixedpoint &v) const
Checks to see if this number is greater than another number.
Definition decimal_fixedpoint.h:852
decimal_fixedpoint(T init, std::enable_if_t< std::is_signed_v< T > &&std::is_integral_v< T > > *=nullptr)
Build a decimal_fixedpoint using a 64-bit signed number.
Definition decimal_fixedpoint.h:233
decimal_fixedpoint & operator=(const std::string &v)
Equal operator for string.
Definition decimal_fixedpoint.h:316
decimal_fixedpoint & operator=(const decimal_fixedpoint &v)
Equal operator for other decimal_fixedpoint objects.
Definition decimal_fixedpoint.h:271
decimal_fixedpoint(const decimal_fixedpoint &init)
Build a decimal_fixedpoint using another decimal_fixedpoint.
Definition decimal_fixedpoint.h:260
T convert_to(std::enable_if_t< std::is_signed_v< T > &&std::is_integral_v< T > > *=nullptr) const
Templated conversion function for signed integral types.
Definition decimal_fixedpoint.h:423
bool operator!=(const decimal_fixedpoint &v) const
Checks to see if two numbers are not equal.
Definition decimal_fixedpoint.h:839
constexpr int getWholeWords() const
Get the value of whole_words template parameter.
Definition decimal_fixedpoint.h:52
int64_t toLong() const
Return a int64_t version of the decimal_fixedpoint.
Definition decimal_fixedpoint.h:351
T convert_to(std::enable_if_t< std::is_unsigned_v< T > > *=nullptr) const
Templated conversion function for unsigned types.
Definition decimal_fixedpoint.h:414
decimal_fixedpoint & operator=(int64_t v)
Equal operator for 64-bit signed int.
Definition decimal_fixedpoint.h:292
double toDouble() const
Return a double precision version of the decimal_fixedpoint.
Definition decimal_fixedpoint.h:331
decimal_fixedpoint & operator+=(const decimal_fixedpoint &v)
Adds another number to this one and sets it equal to the result.
Definition decimal_fixedpoint.h:664
decimal_fixedpoint(T init, std::enable_if_t< std::is_unsigned_v< T > > *=nullptr)
Build a decimal_fixedpoint using a 64-bit unsigned number.
Definition decimal_fixedpoint.h:222
decimal_fixedpoint & operator=(double v)
Equal operator for double.
Definition decimal_fixedpoint.h:307
bool isZero() const
Return true if value is zero, otherwise return false.
Definition decimal_fixedpoint.h:402
decimal_fixedpoint()
Default constructor.
Definition decimal_fixedpoint.h:199
uint64_t toUnsignedLong() const
Return a uint64_t version of the decimal_fixedpoint.
Definition decimal_fixedpoint.h:388
decimal_fixedpoint & operator/=(const decimal_fixedpoint &v)
Divides another number from this one and sets it equal to the result.
Definition decimal_fixedpoint.h:777
bool operator<(const decimal_fixedpoint &v) const
Checks to see if this number is less than another number.
Definition decimal_fixedpoint.h:881
bool operator==(const decimal_fixedpoint &v) const
Checks to see if two numbers are equal.
Definition decimal_fixedpoint.h:826
void negate()
Negate the value (change the sign bit).
Definition decimal_fixedpoint.h:325
decimal_fixedpoint(const T init, std::enable_if_t< std::is_floating_point_v< T > > *=nullptr)
Build a decimal_fixedpoint using a double.
Definition decimal_fixedpoint.h:250
decimal_fixedpoint & operator=(uint64_t v)
Equal operator for 64-bit unsigned int.
Definition decimal_fixedpoint.h:283
decimal_fixedpoint & operator-=(const decimal_fixedpoint &v)
Subtracts another number from this one and sets it equal to the result.
Definition decimal_fixedpoint.h:717
T convert_to(std::enable_if_t< std::is_floating_point_v< T > > *=nullptr) const
Templated conversion function for floating point types.
Definition decimal_fixedpoint.h:432
bool operator<=(const decimal_fixedpoint &v) const
Checks to see if this number is less than or equal to another number.
Definition decimal_fixedpoint.h:896
bool operator>=(const decimal_fixedpoint &v) const
Checks to see if this number is greater than or equal to another number.
Definition decimal_fixedpoint.h:867
constexpr int getFractionWords() const
Get the value of fraction_words template parameter.
Definition decimal_fixedpoint.h:57
decimal_fixedpoint & operator*=(const decimal_fixedpoint &v)
Multiplies another number to this one and sets it equal to the result.
Definition decimal_fixedpoint.h:730