1 /*
2 * Copyright (c) 2020-2024 Stefan Krah. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 * notice, this list of conditions and the following disclaimer in the
12 * documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24 * SUCH DAMAGE.
25 */
26
27
28 #ifndef LIBMPDECXX_DECIMAL_HH_
29 #define LIBMPDECXX_DECIMAL_HH_
30
31
32 #include <cassert>
33 #include <cstddef>
34 #include <cstdint>
35
36 #include <algorithm>
37 #include <atomic>
38 #include <iostream>
39 #include <limits>
40 #include <memory>
41 #include <stdexcept>
42 #include <string>
43 #include <type_traits>
44 #include <utility>
45
46 #include <mpdecimal.h>
47
48 #undef iscanonical /* math.h */
49 #undef isfinite /* math.h */
50 #undef isinf /* math.h */
51 #undef isnan /* math.h */
52 #undef isnormal /* math.h */
53 #undef issubnormal /* math.h */
54 #undef iszero /* math.h */
55 #undef isspecial /* ctype.h */
56
57 #undef IMPORTEXPORT
58 #ifdef _MSC_VER
59 #if defined (BUILD_LIBMPDECXX)
60 #define IMPORTEXPORT __declspec(dllexport)
61 #elif defined(_DLL)
62 #define IMPORTEXPORT __declspec(dllimport)
63 #else
64 #define IMPORTEXPORT
65 #endif
66 #define ALWAYS_INLINE __forceinline
67 #else
68 #define IMPORTEXPORT
69 #define ALWAYS_INLINE inline
70 #endif
71
72
73 namespace decimal {
74
75 /******************************************************************************/
76 /* Constants from libmpdec */
77 /******************************************************************************/
78
79 enum round {
80 ROUND_UP = MPD_ROUND_UP, /* round away from 0 */
81 ROUND_DOWN = MPD_ROUND_DOWN, /* round toward 0 (truncate) */
82 ROUND_CEILING = MPD_ROUND_CEILING, /* round toward +infinity */
83 ROUND_FLOOR = MPD_ROUND_FLOOR, /* round toward -infinity */
84 ROUND_HALF_UP = MPD_ROUND_HALF_UP, /* 0.5 is rounded up */
85 ROUND_HALF_DOWN = MPD_ROUND_HALF_DOWN, /* 0.5 is rounded down */
86 ROUND_HALF_EVEN = MPD_ROUND_HALF_EVEN, /* 0.5 is rounded to even */
87 ROUND_05UP = MPD_ROUND_05UP, /* round zero or five away from 0 */
88 ROUND_TRUNC = MPD_ROUND_TRUNC, /* truncate, but set infinity */
89 ROUND_GUARD = MPD_ROUND_GUARD
90 };
91
92 /*
93 * Aliases for a spelling that is consistent with the exceptions below.
94 * Use whichever form you prefer.
95 */
96 constexpr uint32_t DecClamped = MPD_Clamped;
97 constexpr uint32_t DecConversionSyntax = MPD_Conversion_syntax;
98 constexpr uint32_t DecDivisionByZero = MPD_Division_by_zero;
99 constexpr uint32_t DecDivisionImpossible = MPD_Division_impossible;
100 constexpr uint32_t DecDivisionUndefined = MPD_Division_undefined;
101 constexpr uint32_t DecFpuError = MPD_Fpu_error; /* unused */
102 constexpr uint32_t DecInexact = MPD_Inexact;
103 constexpr uint32_t DecInvalidContext = MPD_Invalid_context;
104 constexpr uint32_t DecInvalidOperation = MPD_Invalid_operation;
105 constexpr uint32_t DecMallocError = MPD_Malloc_error;
106 constexpr uint32_t DecNotImplemented = MPD_Not_implemented; /* unused */
107 constexpr uint32_t DecOverflow = MPD_Overflow;
108 constexpr uint32_t DecRounded = MPD_Rounded;
109 constexpr uint32_t DecSubnormal = MPD_Subnormal;
110 constexpr uint32_t DecUnderflow = MPD_Underflow;
111
112 /* Flag sets */
113 constexpr uint32_t DecIEEEInvalidOperation = MPD_IEEE_Invalid_operation;
114 /* DecConversionSyntax */
115 /* DecDivisionImpossible */
116 /* DecDivisionUndefined */
117 /* DecFpuError */
118 /* DecInvalidContext */
119 /* DecInvalidOperation */
120 /* DecMallocError */
121
122 constexpr uint32_t DecErrors = MPD_Errors;
123 /* DecIEEEInvalidOperation */
124 /* DecDivisionByZero */
125
126 constexpr uint32_t DecMaxStatus = MPD_Max_status;
127 /* All flags */
128
129 /* IEEEContext(): common arguments */
130 constexpr int DECIMAL32 = MPD_DECIMAL32;
131 constexpr int DECIMAL64 = MPD_DECIMAL64;
132 constexpr int DECIMAL128 = MPD_DECIMAL128;
133
134 /* IEEEContext(): maximum argument value */
135 constexpr int IEEE_CONTEXT_MAX_BITS = MPD_IEEE_CONTEXT_MAX_BITS;
136
137
138 /******************************************************************************/
139 /* Decimal Exceptions */
140 /******************************************************************************/
141
142 class DecimalException : public std::runtime_error {
143 using std::runtime_error::runtime_error;
144 };
145
146 /* Signals */
147 class IEEEInvalidOperation : public DecimalException {
148 using DecimalException::DecimalException;
149 };
150
151 class DivisionByZero : public DecimalException {
152 using DecimalException::DecimalException;
153 };
154
155 class Overflow : public DecimalException {
156 using DecimalException::DecimalException;
157 };
158
159 class Underflow : public DecimalException {
160 using DecimalException::DecimalException;
161 };
162
163 class Subnormal : public DecimalException {
164 using DecimalException::DecimalException;
165 };
166
167 class Inexact : public DecimalException {
168 using DecimalException::DecimalException;
169 };
170
171 class Rounded : public DecimalException {
172 using DecimalException::DecimalException;
173 };
174
175 class Clamped : public DecimalException {
176 using DecimalException::DecimalException;
177 };
178
179 /* Conditions */
180 class InvalidOperation : public IEEEInvalidOperation {
181 using IEEEInvalidOperation::IEEEInvalidOperation;
182 };
183
184 class ConversionSyntax : public IEEEInvalidOperation {
185 using IEEEInvalidOperation::IEEEInvalidOperation;
186 };
187
188 class DivisionImpossible : public IEEEInvalidOperation {
189 using IEEEInvalidOperation::IEEEInvalidOperation;
190 };
191
192 class DivisionUndefined : public IEEEInvalidOperation {
193 using IEEEInvalidOperation::IEEEInvalidOperation;
194 };
195
196
197 /******************************************************************************/
198 /* Other Exceptions */
199 /******************************************************************************/
200
201 class MallocError : public DecimalException {
202 using DecimalException::DecimalException;
203 };
204
205 class RuntimeError : public DecimalException {
206 using DecimalException::DecimalException;
207 };
208
209 class ValueError : public DecimalException {
210 using DecimalException::DecimalException;
211 };
212
213
214 /******************************************************************************/
215 /* Context object */
216 /******************************************************************************/
217
218 class Context;
219
220 IMPORTEXPORT extern Context context_template;
221 #if defined(__OpenBSD__) || defined(__sun) || defined(_MSC_VER) && defined(_DLL)
222 IMPORTEXPORT Context& getcontext();
223 static thread_local Context& context{getcontext()};
224 #else
225 extern thread_local Context context;
226 #endif
227
228 class Context {
229 private:
230 mpd_context_t ctx;
231 IMPORTEXPORT static void raiseit(uint32_t status);
232
233 public:
234 /***********************************************************************/
235 /* Constructors */
236 /***********************************************************************/
237 Context(const Context& c) = default;
238 Context(Context&& c) = default;
239
Context(const mpd_context_t & m)240 explicit Context(const mpd_context_t &m) noexcept : ctx(m) {}
241
Context(mpd_ssize_t prec=context_template.prec (),mpd_ssize_t emax=context_template.emax (),mpd_ssize_t emin=context_template.emin (),int round=context_template.round (),uint32_t traps=context_template.traps (),int clamp=context_template.clamp (),int allcr=context_template.allcr ())242 explicit Context(mpd_ssize_t prec=context_template.prec(),
243 mpd_ssize_t emax=context_template.emax(),
244 mpd_ssize_t emin=context_template.emin(),
245 int round=context_template.round(),
246 uint32_t traps=context_template.traps(),
247 int clamp=context_template.clamp(),
248 int allcr=context_template.allcr()) {
249 this->prec(prec);
250 this->emax(emax);
251 this->emin(emin);
252 this->traps(traps);
253 this->round(round);
254 this->clamp(clamp);
255 this->allcr(allcr);
256 this->ctx.status = 0;
257 this->ctx.newtrap = 0;
258 }
259
260 /***********************************************************************/
261 /* Destructor */
262 /***********************************************************************/
263 ~Context() noexcept = default;
264
265 /***********************************************************************/
266 /* Assignment operators */
267 /***********************************************************************/
268 Context& operator= (const Context& c) = default;
269 Context& operator= (Context&& c) = default;
270
271 /***********************************************************************/
272 /* Comparison operators */
273 /***********************************************************************/
operator ==(const Context & other) const274 bool operator== (const Context& other) const noexcept {
275 return ctx.prec == other.ctx.prec &&
276 ctx.emax == other.ctx.emax &&
277 ctx.emin == other.ctx.emin &&
278 ctx.traps == other.ctx.traps &&
279 ctx.status == other.ctx.status &&
280 ctx.round == other.ctx.round &&
281 ctx.clamp == other.ctx.clamp &&
282 ctx.allcr == other.ctx.allcr &&
283 ctx.newtrap == other.ctx.newtrap;
284 }
285
operator !=(const Context & other) const286 bool operator!= (const Context& other) const noexcept {
287 return !(*this == other);
288 }
289
290 /***********************************************************************/
291 /* Accessors */
292 /***********************************************************************/
293 /* Get pointers to the full context */
get()294 ALWAYS_INLINE mpd_context_t *get() { return &ctx; }
getconst() const295 ALWAYS_INLINE const mpd_context_t *getconst() const { return &ctx; }
296
297 /* Get individual fields */
prec() const298 ALWAYS_INLINE mpd_ssize_t prec() const { return ctx.prec; }
emax() const299 ALWAYS_INLINE mpd_ssize_t emax() const { return ctx.emax; }
emin() const300 ALWAYS_INLINE mpd_ssize_t emin() const { return ctx.emin; }
round() const301 ALWAYS_INLINE int round() const { return ctx.round; }
status() const302 ALWAYS_INLINE uint32_t status() const { return ctx.status; }
traps() const303 ALWAYS_INLINE uint32_t traps() const { return ctx.traps; }
clamp() const304 ALWAYS_INLINE int clamp() const { return ctx.clamp; }
allcr() const305 ALWAYS_INLINE int allcr() const { return ctx.allcr; }
etiny() const306 ALWAYS_INLINE int64_t etiny() const { return mpd_etiny(&ctx); }
etop() const307 ALWAYS_INLINE int64_t etop() const { return mpd_etop(&ctx); }
308
309 /* Set individual fields */
prec(mpd_ssize_t v)310 ALWAYS_INLINE void prec(mpd_ssize_t v) {
311 if (!mpd_qsetprec(&ctx, v)) {
312 throw ValueError("valid range for prec is [1, MAX_PREC]");
313 }
314 }
315
emax(mpd_ssize_t v)316 ALWAYS_INLINE void emax(mpd_ssize_t v) {
317 if (!mpd_qsetemax(&ctx, v)) {
318 throw ValueError("valid range for emax is [0, MAX_EMAX]");
319 }
320 }
321
emin(mpd_ssize_t v)322 ALWAYS_INLINE void emin(mpd_ssize_t v) {
323 if (!mpd_qsetemin(&ctx, v)) {
324 throw ValueError("valid range for emin is [MIN_EMIN, 0]");
325 }
326 }
327
round(int v)328 ALWAYS_INLINE void round(int v) {
329 if (!mpd_qsetround(&ctx, v)) {
330 throw ValueError("valid values for rounding are:\n"
331 " [ROUND_CEILING, ROUND_FLOOR, ROUND_UP, ROUND_DOWN,\n"
332 " ROUND_HALF_UP, ROUND_HALF_DOWN, ROUND_HALF_EVEN,\n"
333 " ROUND_05UP]");
334 }
335 }
336
status(uint32_t v)337 ALWAYS_INLINE void status(uint32_t v) {
338 if (!mpd_qsetstatus(&ctx, v)) {
339 throw ValueError("invalid status flag");
340 }
341 }
342
traps(uint32_t v)343 ALWAYS_INLINE void traps(uint32_t v) {
344 if (!mpd_qsettraps(&ctx, v)) {
345 throw ValueError("invalid status flag");
346 }
347 }
348
clamp(int v)349 ALWAYS_INLINE void clamp(int v) {
350 if (!mpd_qsetclamp(&ctx, v)) {
351 throw ValueError("invalid value for clamp");
352 }
353 }
354
allcr(int v)355 ALWAYS_INLINE void allcr(int v) {
356 if (!mpd_qsetcr(&ctx, v)) {
357 throw ValueError("invalid value for allcr");
358 }
359 }
360
361 /***********************************************************************/
362 /* Status and exception handling */
363 /***********************************************************************/
364 /* Add flags to status and raise an exception if a relevant trap is active */
raise(uint32_t flags)365 ALWAYS_INLINE void raise(uint32_t flags) {
366 ctx.status |= (flags & ~MPD_Malloc_error);
367 const uint32_t active_traps = flags & (ctx.traps|MPD_Malloc_error);
368 if (active_traps) {
369 raiseit(active_traps);
370 }
371 }
372
373 /* Same, but with cleanup for constructors */
raise(uint32_t flags,mpd_t * a,bool isstatic)374 ALWAYS_INLINE void raise(uint32_t flags, mpd_t *a, bool isstatic) {
375 ctx.status |= (flags & ~MPD_Malloc_error);
376 const uint32_t active_traps = flags & (ctx.traps|MPD_Malloc_error);
377 if (active_traps) {
378 if (!isstatic) {
379 mpd_del(a);
380 }
381 raiseit(active_traps);
382 }
383 }
384
385 /* Add selected status flags */
add_status(uint32_t flags)386 ALWAYS_INLINE void add_status(uint32_t flags) {
387 if (flags > MPD_Max_status) {
388 throw ValueError("invalid flags");
389 }
390 ctx.status |= flags;
391 }
392
393 /* Clear all status flags */
clear_status()394 ALWAYS_INLINE void clear_status() {
395 ctx.status = 0;
396 }
397
398 /* Clear selected status flags */
clear_status(uint32_t flags)399 ALWAYS_INLINE void clear_status(uint32_t flags) {
400 if (flags > MPD_Max_status) {
401 throw ValueError("invalid flags");
402 }
403 ctx.status &= ~flags;
404 }
405
406 /* Add selected traps */
add_traps(uint32_t flags)407 ALWAYS_INLINE void add_traps(uint32_t flags) {
408 if (flags > MPD_Max_status) {
409 throw ValueError("invalid flags");
410 }
411 ctx.traps |= flags;
412 }
413
414 /* Clear all traps */
clear_traps()415 ALWAYS_INLINE void clear_traps() {
416 ctx.traps = 0;
417 }
418
419 /* Clear selected traps */
clear_traps(uint32_t flags)420 ALWAYS_INLINE void clear_traps(uint32_t flags) {
421 if (flags > MPD_Max_status) {
422 throw ValueError("invalid flags");
423 }
424 ctx.traps &= ~flags;
425 }
426
427 /***********************************************************************/
428 /* String conversion */
429 /***********************************************************************/
430 IMPORTEXPORT std::string repr() const;
431 IMPORTEXPORT friend std::ostream& operator<<(std::ostream& os, const Context& c);
432 };
433
434 IMPORTEXPORT Context MaxContext();
435 IMPORTEXPORT Context IEEEContext(int bits);
436
437
438 /******************************************************************************/
439 /* Util */
440 /******************************************************************************/
441
442 namespace util {
443
444 template <typename dest_t, typename src_t>
445 inline dest_t
safe_downcast(src_t v)446 safe_downcast(src_t v) {
447 if (v < std::numeric_limits<dest_t>::min() ||
448 v > std::numeric_limits<dest_t>::max()) {
449 throw ValueError("cast changes the original value");
450 }
451
452 return static_cast<dest_t>(v);
453 }
454
455 inline std::shared_ptr<const char>
shared_cp(const char * cp)456 shared_cp(const char *cp) {
457 if (cp == nullptr) {
458 throw RuntimeError("util::shared_cp: invalid nullptr argument");
459 }
460
461 return std::shared_ptr<const char>(cp, [](const char *s){ mpd_free(const_cast<char *>(s)); });
462 }
463
464 inline std::string
string_from_cp(const char * cp)465 string_from_cp(const char *cp) {
466 const auto p = shared_cp(cp);
467 return std::string(p.get());
468 }
469
470 template <typename T>
471 struct int64_compat {
472 #define INT64_SUBSET(T) \
473 (INT64_MIN <= std::numeric_limits<T>::min() && std::numeric_limits<T>::max() <= INT64_MAX)
474
475 static const bool value = std::is_same<T, int8_t>::value ||
476 std::is_same<T, int16_t>::value ||
477 std::is_same<T, int32_t>::value ||
478 std::is_same<T, int64_t>::value ||
479 (std::is_same<T, signed char>::value && INT64_SUBSET(signed char)) ||
480 (std::is_same<T, short>::value && INT64_SUBSET(short)) ||
481 (std::is_same<T, int>::value && INT64_SUBSET(int)) ||
482 (std::is_same<T, long>::value && INT64_SUBSET(long)) ||
483 (std::is_same<T, long long>::value && INT64_SUBSET(long long));
484 };
485
486 template <typename T>
487 struct uint64_compat {
488 #define UINT64_SUBSET(T) (std::numeric_limits<T>::max() <= UINT64_MAX)
489
490 static const bool value = std::is_same<T, uint8_t>::value ||
491 std::is_same<T, uint16_t>::value ||
492 std::is_same<T, uint32_t>::value ||
493 std::is_same<T, uint64_t>::value ||
494 (std::is_same<T, unsigned char>::value && UINT64_SUBSET(unsigned char)) ||
495 (std::is_same<T, unsigned short>::value && UINT64_SUBSET(unsigned short)) ||
496 (std::is_same<T, unsigned int>::value && UINT64_SUBSET(unsigned int)) ||
497 (std::is_same<T, unsigned long>::value && UINT64_SUBSET(unsigned long)) ||
498 (std::is_same<T, unsigned long long>::value && UINT64_SUBSET(unsigned long long));
499 };
500
501 #define ENABLE_IF_SIGNED(T) \
502 template<typename T, \
503 typename = typename std::enable_if<util::int64_compat<T>::value>::type>
504
505 #define ENABLE_IF_UNSIGNED(T) \
506 template<typename T, \
507 typename = typename std::enable_if<util::uint64_compat<T>::value>::type, typename = void>
508
509 #define ENABLE_IF_INTEGRAL(T) \
510 template<typename T, \
511 typename = typename std::enable_if<util::int64_compat<T>::value || util::uint64_compat<T>::value>::type>
512
513 #define ASSERT_SIGNED(T) \
514 static_assert(util::int64_compat<T>::value, \
515 "internal error: selected type is not int64 compatible")
516
517 #define ASSERT_UNSIGNED(T) \
518 static_assert(util::uint64_compat<T>::value, \
519 "internal error: selected type is not uint64 compatible")
520
521 #define ASSERT_INTEGRAL(T) \
522 static_assert(util::int64_compat<T>::value || util::uint64_compat<T>::value, \
523 "internal error: selected type is not a suitable integer type")
524 } // namespace util
525
526
527 /******************************************************************************/
528 /* Decimal object */
529 /******************************************************************************/
530
531 constexpr mpd_ssize_t MINALLOC = 4;
532
533 class Decimal {
534 private:
535 mpd_uint_t data[MINALLOC] = {0};
536
537 mpd_t value {
538 MPD_STATIC|MPD_STATIC_DATA|MPD_SNAN, /* flags */
539 0, /* exp */
540 0, /* digits */
541 0, /* len */
542 MINALLOC, /* alloc */
543 data /* data */
544 };
545
546 /* mpd_t accessors */
isstatic() const547 ALWAYS_INLINE bool isstatic() const { return value.data == data; }
548
549 /* Reset rhs to snan after moving data to lhs */
reset()550 ALWAYS_INLINE void reset() {
551 value = {
552 MPD_STATIC|MPD_STATIC_DATA|MPD_SNAN, /* flags */
553 0, /* exp */
554 0, /* digits */
555 0, /* len */
556 MINALLOC, /* alloc */
557 data /* data */
558 };
559 }
560
561 /* Copy flags, preserving memory attributes of result */
562 ALWAYS_INLINE uint8_t
copy_flags(const uint8_t rflags,const uint8_t aflags)563 copy_flags(const uint8_t rflags, const uint8_t aflags) {
564 return (rflags & (MPD_STATIC|MPD_DATAFLAGS)) |
565 (aflags & ~(MPD_STATIC|MPD_DATAFLAGS));
566 }
567
568 ALWAYS_INLINE void
copy_value(const mpd_t * const src,const bool fastcopy)569 copy_value(const mpd_t *const src, const bool fastcopy) {
570 assert(mpd_isstatic(&value));
571 assert(mpd_isstatic(src));
572 assert(value.alloc >= MINALLOC);
573 assert(src->alloc >= MINALLOC);
574 assert(MPD_MINALLOC == MINALLOC);
575 if (fastcopy) {
576 value.data[0] = src->data[0];
577 value.data[1] = src->data[1];
578 value.data[2] = src->data[2];
579 value.data[3] = src->data[3];
580 value.flags = copy_flags(value.flags, src->flags);
581 value.exp = src->exp;
582 value.digits = src->digits;
583 value.len = src->len;
584 } else {
585 if (!mpd_qcopy_cxx(&value, src)) {
586 context.raise(MPD_Malloc_error);
587 }
588 }
589 }
590
591 ALWAYS_INLINE void
move_value(const mpd_t * const src,const bool fastcopy)592 move_value(const mpd_t *const src, const bool fastcopy) {
593 assert(mpd_isstatic(&value));
594 assert(mpd_isstatic(src));
595 assert(value.alloc >= MINALLOC);
596 assert(src->alloc >= MINALLOC);
597 assert(MPD_MINALLOC == MINALLOC);
598 if (fastcopy) {
599 value.data[0] = src->data[0];
600 value.data[1] = src->data[1];
601 value.data[2] = src->data[2];
602 value.data[3] = src->data[3];
603 value.flags = copy_flags(value.flags, src->flags);
604 value.exp = src->exp;
605 value.digits = src->digits;
606 value.len = src->len;
607 }
608 else {
609 assert(mpd_isdynamic_data(src));
610 if (mpd_isdynamic_data(&value)) {
611 mpd_free(value.data);
612 }
613 value = *src;
614 assert(mpd_isstatic(&value));
615 assert(mpd_isdynamic_data(&value));
616 }
617 }
618
unary_func_status(int (* func)(mpd_t *,const mpd_t *,uint32_t *)) const619 ALWAYS_INLINE Decimal unary_func_status(
620 int (* func)(mpd_t *, const mpd_t *, uint32_t *)) const {
621 Decimal result;
622 uint32_t status = 0;
623 if (!func(result.get(), getconst(), &status)) {
624 throw MallocError("out of memory");
625 }
626 return result;
627 }
628
unary_func(void (* func)(mpd_t *,const mpd_t *,const mpd_context_t *,uint32_t *),Context & c=context) const629 ALWAYS_INLINE Decimal unary_func(
630 void (* func)(mpd_t *, const mpd_t *, const mpd_context_t *, uint32_t *),
631 Context& c=context) const {
632 Decimal result;
633 uint32_t status = 0;
634 func(result.get(), getconst(), c.getconst(), &status);
635 c.raise(status);
636 return result;
637 }
638
binary_func_noctx(int (* func)(mpd_t *,const mpd_t *,const mpd_t *),const Decimal & other) const639 ALWAYS_INLINE Decimal binary_func_noctx(
640 int (* func)(mpd_t *, const mpd_t *, const mpd_t *),
641 const Decimal& other) const {
642 Decimal result;
643 (void)func(result.get(), getconst(), other.getconst());
644 return result;
645 }
646
int_binary_func(int (* func)(mpd_t *,const mpd_t *,const mpd_t *,const mpd_context_t *,uint32_t *),const Decimal & other,Context & c=context) const647 ALWAYS_INLINE Decimal int_binary_func(
648 int (* func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_context_t *, uint32_t *),
649 const Decimal& other,
650 Context& c=context) const {
651 Decimal result;
652 uint32_t status = 0;
653 (void)func(result.get(), getconst(), other.getconst(), c.getconst(), &status);
654 c.raise(status);
655 return result;
656 }
657
binary_func(void (* func)(mpd_t *,const mpd_t *,const mpd_t *,const mpd_context_t *,uint32_t *),const Decimal & other,Context & c=context) const658 ALWAYS_INLINE Decimal binary_func(
659 void (* func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_context_t *, uint32_t *),
660 const Decimal& other,
661 Context& c=context) const {
662 Decimal result;
663 uint32_t status = 0;
664 func(result.get(), getconst(), other.getconst(), c.getconst(), &status);
665 c.raise(status);
666 return result;
667 }
668
inplace_binary_func(void (* func)(mpd_t *,const mpd_t *,const mpd_t *,const mpd_context_t *,uint32_t *),const Decimal & other,Context & c=context)669 ALWAYS_INLINE Decimal& inplace_binary_func(
670 void (* func)(mpd_t *, const mpd_t *, const mpd_t *, const mpd_context_t *, uint32_t *),
671 const Decimal& other,
672 Context& c=context) {
673 uint32_t status = 0;
674 func(get(), getconst(), other.getconst(), c.getconst(), &status);
675 c.raise(status);
676 return *this;
677 }
678
inplace_shiftl(const int64_t n,Context & c=context)679 ALWAYS_INLINE Decimal inplace_shiftl(const int64_t n, Context& c=context) {
680 uint32_t status = 0;
681 mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
682 mpd_qshiftl(get(), getconst(), nn, &status);
683 c.raise(status);
684 return *this;
685 }
686
inplace_shiftr(const int64_t n,Context & c=context)687 ALWAYS_INLINE Decimal inplace_shiftr(const int64_t n, Context& c=context) {
688 uint32_t status = 0;
689 mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
690 mpd_qshiftr(get(), getconst(), nn, &status);
691 c.raise(status);
692 return *this;
693 }
694
695 public:
696 /***********************************************************************/
697 /* Exact conversions regardless of context */
698 /***********************************************************************/
699
700 /* Implicit */
701 Decimal() noexcept = default;
Decimal(const Decimal & other)702 Decimal(const Decimal& other) { *this = other; }
Decimal(Decimal && other)703 Decimal(Decimal&& other) noexcept { *this = std::move(other); }
704
705 ENABLE_IF_SIGNED(T)
Decimal(const T & other)706 Decimal(const T& other) {
707 ASSERT_SIGNED(T);
708 uint32_t status = 0;
709 mpd_qset_i64_exact(&value, other, &status);
710 context.raise(status, &value, isstatic());
711 }
712
713 ENABLE_IF_UNSIGNED(T)
Decimal(const T & other)714 Decimal(const T& other) {
715 ASSERT_UNSIGNED(T);
716 uint32_t status = 0;
717 mpd_qset_u64_exact(&value, other, &status);
718 context.raise(status, &value, isstatic());
719 }
720
721 /* Explicit */
Decimal(const char * const s)722 explicit Decimal(const char * const s) {
723 uint32_t status = 0;
724 if (s == nullptr) {
725 throw ValueError("Decimal: string argument in constructor is NULL");
726 }
727 mpd_qset_string_exact(&value, s, &status);
728 context.raise(status, &value, isstatic());
729 }
730
Decimal(const std::string & s)731 explicit Decimal(const std::string& s) {
732 uint32_t status = 0;
733 mpd_qset_string_exact(&value, s.c_str(), &status);
734 context.raise(status, &value, isstatic());
735 }
736
Decimal(const mpd_uint128_triple_t & triple)737 explicit Decimal(const mpd_uint128_triple_t& triple) {
738 uint32_t status = 0;
739 if (mpd_from_uint128_triple(&value, &triple, &status) < 0) {
740 context.raise(status, &value, isstatic());
741 }
742 }
743
744 /***********************************************************************/
745 /* Inexact conversions that use a context */
746 /***********************************************************************/
Decimal(const Decimal & other,Context & c)747 explicit Decimal(const Decimal& other, Context& c) {
748 const mpd_context_t *ctx = c.getconst();
749
750 *this = other;
751
752 if (mpd_isnan(&value) && value.digits > ctx->prec - ctx->clamp) {
753 /* Special case: too many NaN payload digits */
754 mpd_setspecial(&value, MPD_POS, MPD_NAN);
755 c.raise(MPD_Conversion_syntax, &value, isstatic());
756 }
757 else {
758 uint32_t status = 0;
759 mpd_qfinalize(&value, ctx, &status);
760 c.raise(status, &value, isstatic());
761 }
762 }
763
764 ENABLE_IF_SIGNED(T)
Decimal(const T & other,Context & c)765 explicit Decimal(const T& other, Context& c) {
766 ASSERT_SIGNED(T);
767 uint32_t status = 0;
768 mpd_qset_i64(&value, other, c.getconst(), &status);
769 c.raise(status, &value, isstatic());
770 }
771
772 ENABLE_IF_UNSIGNED(T)
Decimal(const T & other,Context & c)773 explicit Decimal(const T& other, Context& c) {
774 ASSERT_UNSIGNED(T);
775 uint32_t status = 0;
776 mpd_qset_u64(&value, other, c.getconst(), &status);
777 c.raise(status, &value, isstatic());
778 }
779
Decimal(const char * const s,Context & c)780 explicit Decimal(const char * const s, Context& c) {
781 uint32_t status = 0;
782 if (s == nullptr) {
783 throw ValueError("Decimal: string argument in constructor is NULL");
784 }
785 mpd_qset_string(&value, s, c.getconst(), &status);
786 c.raise(status, &value, isstatic());
787 }
788
Decimal(const std::string & s,Context & c)789 explicit Decimal(const std::string& s, Context& c) {
790 uint32_t status = 0;
791 mpd_qset_string(&value, s.c_str(), c.getconst(), &status);
792 c.raise(status, &value, isstatic());
793 }
794
795 /***********************************************************************/
796 /* Accessors */
797 /***********************************************************************/
get()798 ALWAYS_INLINE mpd_t *get() { return &value; }
getconst() const799 ALWAYS_INLINE const mpd_t *getconst() const { return &value; }
sign() const800 ALWAYS_INLINE int sign() const { return mpd_isnegative(&value) ? -1 : 1; }
801
coeff() const802 ALWAYS_INLINE Decimal coeff() const {
803 if (isspecial()) {
804 throw ValueError("coefficient is undefined for special values");
805 }
806
807 Decimal result = *this;
808 mpd_set_positive(&result.value);
809 result.value.exp = 0;
810 return result;
811 }
812
exponent() const813 ALWAYS_INLINE int64_t exponent() const {
814 if (isspecial()) {
815 throw ValueError("exponent is undefined for special values");
816 }
817
818 return value.exp;
819 }
820
payload() const821 ALWAYS_INLINE Decimal payload() const {
822 if (!isnan()) {
823 throw ValueError("payload is only defined for NaNs");
824 }
825 if (value.len == 0) {
826 return Decimal(0);
827 }
828
829 Decimal result = *this;
830 mpd_set_flags(&result.value, 0);
831 result.value.exp = 0;
832 return result;
833 }
834
835 /***********************************************************************/
836 /* Destructor */
837 /***********************************************************************/
~Decimal()838 ~Decimal() { if (value.data != data) mpd_del(&value); }
839
840 /***********************************************************************/
841 /* Assignment operators */
842 /***********************************************************************/
operator =(const Decimal & other)843 ALWAYS_INLINE Decimal& operator= (const Decimal& other) {
844 copy_value(other.getconst(), other.isstatic());
845 return *this;
846 }
847
operator =(Decimal && other)848 ALWAYS_INLINE Decimal& operator= (Decimal&& other) noexcept {
849 if (this != &other) {
850 move_value(other.getconst(), other.isstatic());
851 other.reset();
852 }
853 return *this;
854 }
855
operator +=(const Decimal & other)856 ALWAYS_INLINE Decimal& operator+= (const Decimal& other) { return inplace_binary_func(mpd_qadd, other); }
operator -=(const Decimal & other)857 ALWAYS_INLINE Decimal& operator-= (const Decimal& other) { return inplace_binary_func(mpd_qsub, other); }
operator *=(const Decimal & other)858 ALWAYS_INLINE Decimal& operator*= (const Decimal& other) { return inplace_binary_func(mpd_qmul, other); }
operator /=(const Decimal & other)859 ALWAYS_INLINE Decimal& operator/= (const Decimal& other) { return inplace_binary_func(mpd_qdiv, other); }
operator %=(const Decimal & other)860 ALWAYS_INLINE Decimal& operator%= (const Decimal& other) { return inplace_binary_func(mpd_qrem, other); }
861
862 /***********************************************************************/
863 /* Comparison operators */
864 /***********************************************************************/
operator ==(const Decimal & other) const865 ALWAYS_INLINE bool operator== (const Decimal& other) const {
866 uint32_t status = 0;
867 const int r = mpd_qcmp(getconst(), other.getconst(), &status);
868 if (r == INT_MAX) {
869 if (issnan() || other.issnan()) {
870 context.raise(status);
871 }
872 return false;
873 }
874 return r == 0;
875 }
876
operator !=(const Decimal & other) const877 ALWAYS_INLINE bool operator!= (const Decimal& other) const {
878 uint32_t status = 0;
879 const int r = mpd_qcmp(getconst(), other.getconst(), &status);
880 if (r == INT_MAX) {
881 if (issnan() || other.issnan()) {
882 context.raise(status);
883 }
884 return true;
885 }
886 return r != 0;
887 }
888
operator <(const Decimal & other) const889 ALWAYS_INLINE bool operator< (const Decimal& other) const {
890 uint32_t status = 0;
891 const int r = mpd_qcmp(getconst(), other.getconst(), &status);
892 if (r == INT_MAX) {
893 context.raise(status);
894 return false;
895 }
896 return r < 0;
897 }
898
operator <=(const Decimal & other) const899 ALWAYS_INLINE bool operator<= (const Decimal& other) const {
900 uint32_t status = 0;
901 const int r = mpd_qcmp(getconst(), other.getconst(), &status);
902 if (r == INT_MAX) {
903 context.raise(status);
904 return false;
905 }
906 return r <= 0;
907 }
908
operator >=(const Decimal & other) const909 ALWAYS_INLINE bool operator>= (const Decimal& other) const {
910 uint32_t status = 0;
911 const int r = mpd_qcmp(getconst(), other.getconst(), &status);
912 if (r == INT_MAX) {
913 context.raise(status);
914 return false;
915 }
916 return r >= 0;
917 }
918
operator >(const Decimal & other) const919 ALWAYS_INLINE bool operator> (const Decimal& other) const {
920 uint32_t status = 0;
921 const int r = mpd_qcmp(getconst(), other.getconst(), &status);
922 if (r == INT_MAX) {
923 context.raise(status);
924 return false;
925 }
926 return r > 0;
927 }
928
929 /***********************************************************************/
930 /* Unary arithmetic operators */
931 /***********************************************************************/
operator -() const932 ALWAYS_INLINE Decimal operator- () const { return unary_func(mpd_qminus); }
operator +() const933 ALWAYS_INLINE Decimal operator+ () const { return unary_func(mpd_qplus); }
934
935 /***********************************************************************/
936 /* Binary arithmetic operators */
937 /***********************************************************************/
operator +(const Decimal & other) const938 ALWAYS_INLINE Decimal operator+ (const Decimal& other) const { return binary_func(mpd_qadd, other); }
operator -(const Decimal & other) const939 ALWAYS_INLINE Decimal operator- (const Decimal& other) const { return binary_func(mpd_qsub, other); }
operator *(const Decimal & other) const940 ALWAYS_INLINE Decimal operator* (const Decimal& other) const { return binary_func(mpd_qmul, other); }
operator /(const Decimal & other) const941 ALWAYS_INLINE Decimal operator/ (const Decimal& other) const { return binary_func(mpd_qdiv, other); }
operator %(const Decimal & other) const942 ALWAYS_INLINE Decimal operator% (const Decimal& other) const { return binary_func(mpd_qrem, other); }
943
944 /***********************************************************************/
945 /* Predicates */
946 /***********************************************************************/
947 /* Predicates, no context arg */
iscanonical() const948 ALWAYS_INLINE bool iscanonical() const { return mpd_iscanonical(getconst()); }
isfinite() const949 ALWAYS_INLINE bool isfinite() const { return mpd_isfinite(getconst()); }
isinfinite() const950 ALWAYS_INLINE bool isinfinite() const { return mpd_isinfinite(getconst()); }
isspecial() const951 ALWAYS_INLINE bool isspecial() const { return mpd_isspecial(getconst()); }
isnan() const952 ALWAYS_INLINE bool isnan() const { return mpd_isnan(getconst()); }
isqnan() const953 ALWAYS_INLINE bool isqnan() const { return mpd_isqnan(getconst()); }
issnan() const954 ALWAYS_INLINE bool issnan() const { return mpd_issnan(getconst()); }
issigned() const955 ALWAYS_INLINE bool issigned() const { return mpd_issigned(getconst()); }
iszero() const956 ALWAYS_INLINE bool iszero() const { return mpd_iszero(getconst()); }
isinteger() const957 ALWAYS_INLINE bool isinteger() const { return mpd_isinteger(getconst()); }
958
959 /* Predicates, optional context arg */
isnormal(const Context & c=context) const960 ALWAYS_INLINE bool isnormal(const Context& c=context) const { return mpd_isnormal(getconst(), c.getconst()); }
issubnormal(const Context & c=context) const961 ALWAYS_INLINE bool issubnormal(const Context& c=context) const { return mpd_issubnormal(getconst(), c.getconst()); }
962
963 /***********************************************************************/
964 /* Unary functions */
965 /***********************************************************************/
966 /* Unary functions, no context arg */
adjexp() const967 ALWAYS_INLINE int64_t adjexp() const {
968 if (isspecial()) {
969 throw ValueError("adjusted exponent undefined for special values");
970 }
971 return mpd_adjexp(getconst());
972 }
973
canonical() const974 ALWAYS_INLINE Decimal canonical() const { return *this; }
copy() const975 ALWAYS_INLINE Decimal copy() const { return unary_func_status(mpd_qcopy); }
copy_abs() const976 ALWAYS_INLINE Decimal copy_abs() const { return unary_func_status(mpd_qcopy_abs); }
copy_negate() const977 ALWAYS_INLINE Decimal copy_negate() const { return unary_func_status(mpd_qcopy_negate); }
978
979 /* Unary functions, optional context arg */
number_class(Context & c=context) const980 ALWAYS_INLINE std::string number_class(Context& c=context) const { return mpd_class(getconst(), c.getconst()); }
981
abs(Context & c=context) const982 ALWAYS_INLINE Decimal abs(Context& c=context) const { return unary_func(mpd_qabs, c); }
ceil(Context & c=context) const983 ALWAYS_INLINE Decimal ceil(Context& c=context) const { return unary_func(mpd_qceil, c); }
exp(Context & c=context) const984 ALWAYS_INLINE Decimal exp(Context& c=context) const { return unary_func(mpd_qexp, c); }
floor(Context & c=context) const985 ALWAYS_INLINE Decimal floor(Context& c=context) const { return unary_func(mpd_qfloor, c); }
invroot(Context & c=context) const986 ALWAYS_INLINE Decimal invroot(Context& c=context) const { return unary_func(mpd_qinvroot, c); }
logical_invert(Context & c=context) const987 ALWAYS_INLINE Decimal logical_invert(Context& c=context) const { return unary_func(mpd_qinvert, c); }
ln(Context & c=context) const988 ALWAYS_INLINE Decimal ln(Context& c=context) const { return unary_func(mpd_qln, c); }
log10(Context & c=context) const989 ALWAYS_INLINE Decimal log10(Context& c=context) const { return unary_func(mpd_qlog10, c); }
logb(Context & c=context) const990 ALWAYS_INLINE Decimal logb(Context& c=context) const { return unary_func(mpd_qlogb, c); }
minus(Context & c=context) const991 ALWAYS_INLINE Decimal minus(Context& c=context) const { return unary_func(mpd_qminus, c); }
next_minus(Context & c=context) const992 ALWAYS_INLINE Decimal next_minus(Context& c=context) const { return unary_func(mpd_qnext_minus, c); }
next_plus(Context & c=context) const993 ALWAYS_INLINE Decimal next_plus(Context& c=context) const { return unary_func(mpd_qnext_plus, c); }
plus(Context & c=context) const994 ALWAYS_INLINE Decimal plus(Context& c=context) const { return unary_func(mpd_qplus, c); }
reduce(Context & c=context) const995 ALWAYS_INLINE Decimal reduce(Context& c=context) const { return unary_func(mpd_qreduce, c); }
to_integral(Context & c=context) const996 ALWAYS_INLINE Decimal to_integral(Context& c=context) const { return unary_func(mpd_qround_to_int, c); }
to_integral_exact(Context & c=context) const997 ALWAYS_INLINE Decimal to_integral_exact(Context& c=context) const { return unary_func(mpd_qround_to_intx, c); }
sqrt(Context & c=context) const998 ALWAYS_INLINE Decimal sqrt(Context& c=context) const { return unary_func(mpd_qsqrt, c); }
trunc(Context & c=context) const999 ALWAYS_INLINE Decimal trunc(Context& c=context) const { return unary_func(mpd_qtrunc, c); }
1000
1001 /***********************************************************************/
1002 /* Binary functions */
1003 /***********************************************************************/
1004 /* Binary functions, no context arg */
compare_total(const Decimal & other) const1005 ALWAYS_INLINE Decimal compare_total(const Decimal& other) const { return binary_func_noctx(mpd_compare_total, other); }
compare_total_mag(const Decimal & other) const1006 ALWAYS_INLINE Decimal compare_total_mag(const Decimal& other) const { return binary_func_noctx(mpd_compare_total_mag, other); }
1007
1008 /* Binary arithmetic functions, optional context arg */
add(const Decimal & other,Context & c=context) const1009 ALWAYS_INLINE Decimal add(const Decimal& other, Context& c=context) const { return binary_func(mpd_qadd, other, c); }
div(const Decimal & other,Context & c=context) const1010 ALWAYS_INLINE Decimal div(const Decimal& other, Context& c=context) const { return binary_func(mpd_qdiv, other, c); }
divint(const Decimal & other,Context & c=context) const1011 ALWAYS_INLINE Decimal divint(const Decimal& other, Context& c=context) const { return binary_func(mpd_qdivint, other, c); }
compare(const Decimal & other,Context & c=context) const1012 ALWAYS_INLINE Decimal compare(const Decimal& other, Context& c=context) const { return int_binary_func(mpd_qcompare, other, c); }
compare_signal(const Decimal & other,Context & c=context) const1013 ALWAYS_INLINE Decimal compare_signal(const Decimal& other, Context& c=context) const { return int_binary_func(mpd_qcompare_signal, other, c); }
logical_and(const Decimal & other,Context & c=context) const1014 ALWAYS_INLINE Decimal logical_and(const Decimal& other, Context& c=context) const { return binary_func(mpd_qand, other, c); }
logical_or(const Decimal & other,Context & c=context) const1015 ALWAYS_INLINE Decimal logical_or(const Decimal& other, Context& c=context) const { return binary_func(mpd_qor, other, c); }
logical_xor(const Decimal & other,Context & c=context) const1016 ALWAYS_INLINE Decimal logical_xor(const Decimal& other, Context& c=context) const { return binary_func(mpd_qxor, other, c); }
max(const Decimal & other,Context & c=context) const1017 ALWAYS_INLINE Decimal max(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmax, other, c); }
max_mag(const Decimal & other,Context & c=context) const1018 ALWAYS_INLINE Decimal max_mag(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmax_mag, other, c); }
min(const Decimal & other,Context & c=context) const1019 ALWAYS_INLINE Decimal min(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmin, other, c); }
min_mag(const Decimal & other,Context & c=context) const1020 ALWAYS_INLINE Decimal min_mag(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmin_mag, other, c); }
mul(const Decimal & other,Context & c=context) const1021 ALWAYS_INLINE Decimal mul(const Decimal& other, Context& c=context) const { return binary_func(mpd_qmul, other, c); }
next_toward(const Decimal & other,Context & c=context) const1022 ALWAYS_INLINE Decimal next_toward(const Decimal& other, Context& c=context) const { return binary_func(mpd_qnext_toward, other, c); }
pow(const Decimal & other,Context & c=context) const1023 ALWAYS_INLINE Decimal pow(const Decimal& other, Context& c=context) const { return binary_func(mpd_qpow, other, c); }
quantize(const Decimal & other,Context & c=context) const1024 ALWAYS_INLINE Decimal quantize(const Decimal& other, Context& c=context) const { return binary_func(mpd_qquantize, other, c); }
rem(const Decimal & other,Context & c=context) const1025 ALWAYS_INLINE Decimal rem(const Decimal& other, Context& c=context) const { return binary_func(mpd_qrem, other, c); }
rem_near(const Decimal & other,Context & c=context) const1026 ALWAYS_INLINE Decimal rem_near(const Decimal& other, Context& c=context) const { return binary_func(mpd_qrem_near, other, c); }
rotate(const Decimal & other,Context & c=context) const1027 ALWAYS_INLINE Decimal rotate(const Decimal& other, Context& c=context) const { return binary_func(mpd_qrotate, other, c); }
scaleb(const Decimal & other,Context & c=context) const1028 ALWAYS_INLINE Decimal scaleb(const Decimal& other, Context& c=context) const { return binary_func(mpd_qscaleb, other, c); }
shift(const Decimal & other,Context & c=context) const1029 ALWAYS_INLINE Decimal shift(const Decimal& other, Context& c=context) const { return binary_func(mpd_qshift, other, c); }
sub(const Decimal & other,Context & c=context) const1030 ALWAYS_INLINE Decimal sub(const Decimal& other, Context& c=context) const { return binary_func(mpd_qsub, other, c); }
1031
1032 /* Binary arithmetic function, two return values */
divmod(const Decimal & other,Context & c=context) const1033 ALWAYS_INLINE std::pair<Decimal, Decimal> divmod(const Decimal& other, Context& c=context) const {
1034 std::pair<Decimal, Decimal> result;
1035 uint32_t status = 0;
1036 mpd_qdivmod(result.first.get(), result.second.get(), getconst(), other.getconst(), c.getconst(), &status);
1037 c.raise(status);
1038 return result;
1039 }
1040
1041 /***********************************************************************/
1042 /* Ternary functions */
1043 /***********************************************************************/
fma(const Decimal & other,const Decimal & third,Context & c=context) const1044 ALWAYS_INLINE Decimal fma(const Decimal& other, const Decimal& third, Context& c=context) const {
1045 Decimal result;
1046 uint32_t status = 0;
1047 mpd_qfma(result.get(), getconst(), other.getconst(), third.getconst(), c.getconst(), &status);
1048 c.raise(status);
1049 return result;
1050 }
1051
powmod(const Decimal & other,const Decimal & third,Context & c=context) const1052 ALWAYS_INLINE Decimal powmod(const Decimal& other, const Decimal& third, Context& c=context) const {
1053 Decimal result;
1054 uint32_t status = 0;
1055 mpd_qpowmod(result.get(), getconst(), other.getconst(), third.getconst(), c.getconst(), &status);
1056 c.raise(status);
1057 return result;
1058 }
1059
1060 /***********************************************************************/
1061 /* Irregular functions */
1062 /***********************************************************************/
apply(Context & c=context) const1063 ALWAYS_INLINE Decimal apply(Context& c=context) const {
1064 Decimal result = *this;
1065 uint32_t status = 0;
1066
1067 mpd_qfinalize(result.get(), c.getconst(), &status);
1068 c.raise(status);
1069 return result;
1070 }
1071
cmp(const Decimal & other) const1072 ALWAYS_INLINE int cmp(const Decimal& other) const {
1073 uint32_t status = 0;
1074 return mpd_qcmp(getconst(), other.getconst(), &status);
1075 }
1076
cmp_total(const Decimal & other) const1077 ALWAYS_INLINE int cmp_total(const Decimal& other) const {
1078 return mpd_cmp_total(getconst(), other.getconst());
1079 }
1080
cmp_total_mag(const Decimal & other) const1081 ALWAYS_INLINE int cmp_total_mag(const Decimal& other) const {
1082 return mpd_cmp_total_mag(getconst(), other.getconst());
1083 }
1084
copy_sign(const Decimal & other) const1085 ALWAYS_INLINE Decimal copy_sign(const Decimal& other) const {
1086 Decimal result;
1087 uint32_t status = 0;
1088 if (!mpd_qcopy_sign(result.get(), getconst(), other.getconst(), &status)) {
1089 throw MallocError("out of memory");
1090 }
1091 return result;
1092 }
1093
rescale(const int64_t exp,Context & c=context) const1094 ALWAYS_INLINE Decimal rescale(const int64_t exp, Context& c=context) const {
1095 Decimal result;
1096 uint32_t status = 0;
1097 mpd_ssize_t xexp = util::safe_downcast<mpd_ssize_t, int64_t>(exp);
1098 mpd_qrescale(result.get(), getconst(), xexp, c.getconst(), &status);
1099 c.raise(status);
1100 return result;
1101 }
1102
same_quantum(const Decimal & other) const1103 ALWAYS_INLINE bool same_quantum(const Decimal& other) const {
1104 return mpd_same_quantum(getconst(), other.getconst());
1105 }
1106
shiftn(const int64_t n,Context & c=context) const1107 ALWAYS_INLINE Decimal shiftn(const int64_t n, Context& c=context) const {
1108 Decimal result;
1109 uint32_t status = 0;
1110 mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
1111 mpd_qshiftn(result.get(), getconst(), nn, c.getconst(), &status);
1112 c.raise(status);
1113 return result;
1114 }
1115
shiftl(const int64_t n,Context & c=context) const1116 ALWAYS_INLINE Decimal shiftl(const int64_t n, Context& c=context) const {
1117 Decimal result;
1118 uint32_t status = 0;
1119 if (isspecial()) {
1120 throw ValueError("shiftl: cannot handle special numbers");
1121 }
1122 if (n < 0 || n > MPD_MAX_PREC - getconst()->digits) {
1123 throw ValueError("shiftl: shift is negative or too large");
1124 }
1125 mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
1126 mpd_qshiftl(result.get(), getconst(), nn, &status);
1127 c.raise(status);
1128 return result;
1129 }
1130
shiftr(const int64_t n,Context & c=context) const1131 ALWAYS_INLINE Decimal shiftr(const int64_t n, Context& c=context) const {
1132 Decimal result;
1133 uint32_t status = 0;
1134 if (isspecial()) {
1135 throw ValueError("shiftr: cannot handle special numbers");
1136 }
1137 if (n < 0) {
1138 throw ValueError("shiftr: shift is negative");
1139 }
1140 mpd_ssize_t nn = util::safe_downcast<mpd_ssize_t, int64_t>(n);
1141 mpd_qshiftr(result.get(), getconst(), nn, &status);
1142 c.raise(status);
1143 return result;
1144 }
1145
1146 IMPORTEXPORT static Decimal exact(const char *s, Context& c);
1147 IMPORTEXPORT static Decimal exact(const std::string& s, Context& c);
1148 IMPORTEXPORT static Decimal ln10(int64_t n, Context& c=context);
1149 IMPORTEXPORT static int32_t radix();
1150
1151 /***********************************************************************/
1152 /* Integer conversion */
1153 /***********************************************************************/
i64() const1154 ALWAYS_INLINE int64_t i64() const {
1155 uint32_t status = 0;
1156 const int64_t result = mpd_qget_i64(getconst(), &status);
1157 if (status) {
1158 throw ValueError("cannot convert to int64_t");
1159 }
1160 return result;
1161 }
1162
i32() const1163 ALWAYS_INLINE int32_t i32() const {
1164 uint32_t status = 0;
1165 const int32_t result = mpd_qget_i32(getconst(), &status);
1166 if (status) {
1167 throw ValueError("cannot convert to int32_t");
1168 }
1169 return result;
1170 }
1171
u64() const1172 ALWAYS_INLINE uint64_t u64() const {
1173 uint32_t status = 0;
1174 const uint64_t result = mpd_qget_u64(getconst(), &status);
1175 if (status) {
1176 throw ValueError("cannot convert to uint64_t");
1177 }
1178 return result;
1179 }
1180
u32() const1181 ALWAYS_INLINE uint32_t u32() const {
1182 uint32_t status = 0;
1183 const uint32_t result = mpd_qget_u32(getconst(), &status);
1184 if (status) {
1185 throw ValueError("cannot convert to uint32_t");
1186 }
1187 return result;
1188 }
1189
1190 /***********************************************************************/
1191 /* Triple conversion */
1192 /***********************************************************************/
as_uint128_triple() const1193 ALWAYS_INLINE mpd_uint128_triple_t as_uint128_triple() const {
1194 return mpd_as_uint128_triple(getconst());
1195 }
1196
1197 /***********************************************************************/
1198 /* String conversion */
1199 /***********************************************************************/
1200 /* String representations */
1201 IMPORTEXPORT std::string repr(bool capitals=true) const;
1202
to_sci(bool capitals=true) const1203 inline std::string to_sci(bool capitals=true) const {
1204 const char *cp = mpd_to_sci(getconst(), capitals);
1205 if (cp == nullptr) {
1206 throw MallocError("out of memory");
1207 }
1208
1209 return util::string_from_cp(cp);
1210 }
1211
to_eng(bool capitals=true) const1212 inline std::string to_eng(bool capitals=true) const {
1213 const char *cp = mpd_to_eng(getconst(), capitals);
1214 if (cp == nullptr) {
1215 throw MallocError("out of memory");
1216 }
1217
1218 return util::string_from_cp(cp);
1219 }
1220
format(const char * fmt,const Context & c=context) const1221 inline std::string format(const char *fmt, const Context& c=context) const {
1222 uint32_t status = 0;
1223 mpd_context_t ctx;
1224
1225 if (fmt == nullptr) {
1226 throw ValueError("Decimal.format: fmt argument is NULL");
1227 }
1228
1229 mpd_maxcontext(&ctx);
1230 ctx.round = c.getconst()->round;
1231 ctx.traps = 0;
1232
1233 const char *cp = mpd_qformat(getconst(), fmt, &ctx, &status);
1234 if (cp == nullptr) {
1235 if (status & MPD_Malloc_error) {
1236 throw MallocError("out of memory");
1237 }
1238 else if (status & MPD_Invalid_operation) {
1239 throw ValueError("invalid format string");
1240 }
1241 else {
1242 throw RuntimeError("internal error: unexpected status");
1243 }
1244 }
1245
1246 return util::string_from_cp(cp);
1247 }
1248
format(const std::string & s,const Context & c=context) const1249 inline std::string format(const std::string& s, const Context& c=context) const {
1250 return format(s.c_str(), c);
1251 }
1252
1253 IMPORTEXPORT friend std::ostream& operator<< (std::ostream& os, const Decimal& dec);
1254 };
1255
1256 /***********************************************************************/
1257 /* Reverse comparison operators */
1258 /***********************************************************************/
1259 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator==(const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) == self; }
1260 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator!= (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) != self; }
1261 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator< (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) < self; }
1262 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator<= (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) <= self; }
ENABLE_IF_INTEGRAL(T)1263 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator>= (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) >= self; }
ENABLE_IF_INTEGRAL(T)1264 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE bool operator> (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) > self; }
1265
1266 /***********************************************************************/
1267 /* Reverse arithmetic operators */
1268 /***********************************************************************/
1269 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator+ (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) + self; }
1270 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator- (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) - self; }
ENABLE_IF_INTEGRAL(T)1271 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator* (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) * self; }
1272 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator/ (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) / self; }
1273 ENABLE_IF_INTEGRAL(T) ALWAYS_INLINE Decimal operator% (const T& other, const Decimal& self) { ASSERT_INTEGRAL(T); return Decimal(other) % self; }
1274
1275
1276 #undef IMPORTEXPORT
1277 #undef ALWAYS_INLINE
1278 #undef INT64_SUBSET
1279 #undef UINT64_SUBSET
1280 #undef ENABLE_IF_SIGNED
1281 #undef ENABLE_IF_UNSIGNED
1282 #undef ENABLE_IF_INTEGRAL
1283 #undef ASSERT_SIGNED
1284 #undef ASSERT_UNSIGNED
1285 #undef ASSERT_INTEGRAL
1286 } // namespace decimal
1287
1288
1289 #endif // LIBMPDECXX_DECIMAL_HH_
1290