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