libmongocrypt
Loading...
Searching...
No Matches
mc-dec128.h
1#ifndef MC_DEC128_H_INCLUDED
2#define MC_DEC128_H_INCLUDED
3
4#include <bson/bson.h>
5
6#include <mlib/macros.h>
7#include <mlib/int128.h>
8#include <mlib/endian.h>
9
10// Conditional preprocessor definition set by the usage of an intel_dfp from
11// the ImportDFP.cmake script:
12#ifndef MONGOCRYPT_INTELDFP
13// Notify includers that Decimal128 is not available:
14#define MONGOCRYPT_HAVE_DECIMAL128_SUPPORT 0
15
16#else // With IntelDFP:
17// Tell includers that Decimal128 is okay:
18#define MONGOCRYPT_HAVE_DECIMAL128_SUPPORT 1
19
20// Include the header that declares the DFP functions, which may be macros that
21// expand to renamed symbols:
22#include <bid_conf.h>
23#include <bid_functions.h>
24
25#include <inttypes.h>
26#include <string.h>
27#include <stdlib.h>
28#include <float.h>
29
30MLIB_C_LINKAGE_BEGIN
31
33typedef enum mc_dec128_rounding_mode {
34 MC_DEC128_ROUND_NEAREST_EVEN = 0,
35 MC_DEC128_ROUND_DOWNWARD = 1,
36 MC_DEC128_ROUND_UPWARD = 2,
37 MC_DEC128_ROUND_TOWARD_ZERO = 3,
38 MC_DEC128_ROUND_NEAREST_AWAY = 4,
39 MC_DEC128_ROUND_DEFAULT = MC_DEC128_ROUND_NEAREST_EVEN,
40} mc_dec128_rounding_mode;
41
42typedef struct mc_dec128_flagset {
43 _IDEC_flags bits;
44} mc_dec128_flagset;
45
46// This alignment conditional is the same conditions used in Intel's DFP
47// library, ensuring we match the ABI of the library without pulling the header
48#if defined _MSC_VER
49#if defined _M_IX86 && !defined __INTEL_COMPILER
50#define _mcDec128Align(n)
51#else
52#define _mcDec128Align(n) __declspec(align (n))
53#endif
54#else
55#if !defined HPUX_OS
56#define _mcDec128Align(n) __attribute__ ((aligned (n)))
57#else
58#define _mcDec128Align(n)
59#endif
60#endif
61
62typedef union _mcDec128Align (16)
63{
64 uint64_t _words[2];
65#if !defined(__INTELLISENSE__) && defined(__GNUC__) && defined(__amd64) && \
66 !defined(__APPLE__) && !defined(__clang__)
67 // If supported by the compiler, emit a field that can be used to visualize
68 // the value in a debugger.
69 float value_ __attribute__ ((mode (TD)));
70#endif
71}
72mc_dec128;
73
74#undef _mcDec128Align
75
77#ifdef __cplusplus
78#define MC_DEC128_C(N) \
79 mc_dec128 _mcDec128Const (((N) < 0 ? -(N) : (N)), ((N) < 0 ? 1 : 0))
80#else
81#define MC_DEC128_C(N) \
82 _mcDec128Const (((N) < 0 ? -(N) : (N)), ((N) < 0 ? 1 : 0))
83#endif
84
85#define MC_DEC128(N) MLIB_INIT (mc_dec128) MC_DEC128_C (N)
86
87#define _mcDec128Combination(Bits) ((uint64_t) (Bits) << (47))
88#define _mcDec128ZeroExpCombo _mcDec128Combination (1 << 7 | 1 << 13 | 1 << 14)
89#define _mcDec128Const(N, Negate) \
90 _mcDec128ConstFromParts ( \
91 N, (_mcDec128ZeroExpCombo | ((uint64_t) (Negate) << 63)))
92#define _mcDec128ConstFromParts(CoeffLow, HighWord) \
93 { \
94 { \
95 MLIB_IS_LITTLE_ENDIAN ? (uint64_t) (CoeffLow) \
96 : (uint64_t) (HighWord), \
97 MLIB_IS_LITTLE_ENDIAN ? (uint64_t) (HighWord) \
98 : (uint64_t) (CoeffLow), \
99 }, \
100 }
101
102static const mc_dec128 MC_DEC128_ZERO = MC_DEC128_C (0);
103static const mc_dec128 MC_DEC128_ONE = MC_DEC128_C (1);
104static const mc_dec128 MC_DEC128_MINUSONE = MC_DEC128_C (-1);
105
107#define MC_DEC128_LARGEST_NEGATIVE \
108 mc_dec128_from_string ("-9999999999999999999999999999999999E6111")
110#define MC_DEC128_SMALLEST_NEGATIVE mc_dec128_from_string ("-1E-6176")
112#define MC_DEC128_LARGEST_POSITIVE \
113 mc_dec128_from_string ("9999999999999999999999999999999999E6111")
115#define MC_DEC128_SMALLEST_POSITIVE mc_dec128_from_string ("1E-6176")
117#define MC_DEC128_NORMALIZED_ZERO MC_DEC128_C (0)
119#define MC_DEC128_NEGATIVE_EXPONENT_ZERO mc_dec128_from_string ("0E-6176")
120#define _mcDec128InfCombo \
121 _mcDec128Combination (1 << 15 | 1 << 14 | 1 << 13 | 1 << 12)
122#define _mcDec128QuietNaNCombo \
123 _mcDec128Combination (1 << 15 | 1 << 14 | 1 << 13 | 1 << 12 | 1 << 11)
124
126#define MC_DEC128_POSITIVE_INFINITY \
127 _mcDec128ConstFromParts (0, _mcDec128InfCombo)
129#define MC_DEC128_NEGATIVE_INFINITY \
130 _mcDec128ConstFromParts (0, _mcDec128InfCombo | 1ull << 63)
132#define MC_DEC128_POSITIVE_NAN \
133 _mcDec128ConstFromParts (0, _mcDec128QuietNaNCombo)
135#define MC_DEC128_NEGATIVE_NAN \
136 _mcDec128ConstFromParts (0, _mcDec128QuietNaNCombo | 1ull << 63)
137
139static inline BID_UINT128
140_mc_to_bid128 (mc_dec128 d)
141{
142 BID_UINT128 r;
143 memcpy (&r, &d, sizeof d);
144 return r;
145}
146
148static inline mc_dec128
149_bid128_to_mc (BID_UINT128 d)
150{
151 mc_dec128 r;
152 memcpy (&r, &d, sizeof d);
153 return r;
154}
155
164static inline mc_dec128
165mc_dec128_from_double_ex (double d,
166 mc_dec128_rounding_mode rnd,
167 mc_dec128_flagset *flags)
168{
169 mc_dec128_flagset zero_flags = {0};
170 return _bid128_to_mc (
171 binary64_to_bid128 (d, rnd, flags ? &flags->bits : &zero_flags.bits));
172}
173
178static inline mc_dec128
179mc_dec128_from_double (double d)
180{
181 return mc_dec128_from_double_ex (d, MC_DEC128_ROUND_DEFAULT, NULL);
182}
183
192static inline mc_dec128
193mc_dec128_from_string_ex (const char *s,
194 mc_dec128_rounding_mode rnd,
195 mc_dec128_flagset *flags)
196{
197 mc_dec128_flagset zero_flags = {0};
198 return _bid128_to_mc (bid128_from_string (
199 (char *) s, rnd, flags ? &flags->bits : &zero_flags.bits));
200}
201
206static inline mc_dec128
207mc_dec128_from_string (const char *s)
208{
209 return mc_dec128_from_string_ex (s, MC_DEC128_ROUND_DEFAULT, NULL);
210}
211
216typedef struct mc_dec128_string {
218 char str[48];
219} mc_dec128_string;
220
227static inline mc_dec128_string
228mc_dec128_to_string_ex (mc_dec128 d, mc_dec128_flagset *flags)
229{
230 mc_dec128_flagset zero_flags = {0};
231 mc_dec128_string out = {{0}};
232 bid128_to_string (
233 out.str, _mc_to_bid128 (d), flags ? &flags->bits : &zero_flags.bits);
234 return out;
235}
236
240static inline mc_dec128_string
241mc_dec128_to_string (mc_dec128 d)
242{
243 return mc_dec128_to_string_ex (d, NULL);
244}
245
247#define DECL_IDF_COMPARE_1(Oper) \
248 static inline bool mc_dec128_##Oper##_ex ( \
249 mc_dec128 left, mc_dec128 right, mc_dec128_flagset *flags) \
250 { \
251 mc_dec128_flagset zero_flags = {0}; \
252 return 0 != \
253 bid128_quiet_##Oper (_mc_to_bid128 (left), \
254 _mc_to_bid128 (right), \
255 flags ? &flags->bits : &zero_flags.bits); \
256 } \
257 \
258 static inline bool mc_dec128_##Oper (mc_dec128 left, mc_dec128 right) \
259 { \
260 return mc_dec128_##Oper##_ex (left, right, NULL); \
261 }
262
263#define DECL_IDF_COMPARE(Op) DECL_IDF_COMPARE_1 (Op)
264
265DECL_IDF_COMPARE (equal)
266DECL_IDF_COMPARE (not_equal)
267DECL_IDF_COMPARE (greater)
268DECL_IDF_COMPARE (greater_equal)
269DECL_IDF_COMPARE (less)
270DECL_IDF_COMPARE (less_equal)
271
272#undef DECL_IDF_COMPARE
273#undef DECL_IDF_COMPARE_1
274
276#define DECL_PREDICATE(Name, BIDName) \
277 static inline bool mc_dec128_##Name (mc_dec128 d) \
278 { \
279 return 0 != bid128_##BIDName (_mc_to_bid128 (d)); \
280 }
281
282DECL_PREDICATE (is_zero, isZero)
283DECL_PREDICATE (is_negative, isSigned)
284DECL_PREDICATE (is_inf, isInf)
285DECL_PREDICATE (is_finite, isFinite)
286DECL_PREDICATE (is_nan, isNaN)
287
288#undef DECL_PREDICATE
289
291#define DECL_IDF_BINOP_WRAPPER(Oper) \
292 static inline mc_dec128 mc_dec128_##Oper##_ex ( \
293 mc_dec128 left, \
294 mc_dec128 right, \
295 mc_dec128_rounding_mode mode, \
296 mc_dec128_flagset *flags) \
297 { \
298 mc_dec128_flagset zero_flags = {0}; \
299 return _bid128_to_mc ( \
300 bid128_##Oper (_mc_to_bid128 (left), \
301 _mc_to_bid128 (right), \
302 mode, \
303 flags ? &flags->bits : &zero_flags.bits)); \
304 } \
305 \
306 static inline mc_dec128 mc_dec128_##Oper (mc_dec128 left, mc_dec128 right) \
307 { \
308 return mc_dec128_##Oper##_ex ( \
309 left, right, MC_DEC128_ROUND_DEFAULT, NULL); \
310 }
311
312DECL_IDF_BINOP_WRAPPER (add)
313DECL_IDF_BINOP_WRAPPER (mul)
314DECL_IDF_BINOP_WRAPPER (div)
315DECL_IDF_BINOP_WRAPPER (sub)
316DECL_IDF_BINOP_WRAPPER (pow)
317
318#undef DECL_IDF_BINOP_WRAPPER
319
321#define DECL_IDF_UNOP_WRAPPER(Oper) \
322 static inline mc_dec128 mc_dec128_##Oper##_ex (mc_dec128 operand, \
323 mc_dec128_flagset *flags) \
324 { \
325 mc_dec128_flagset zero_flags = {0}; \
326 return _bid128_to_mc ( \
327 bid128_##Oper (_mc_to_bid128 (operand), \
328 MC_DEC128_ROUND_DEFAULT, \
329 flags ? &flags->bits : &zero_flags.bits)); \
330 } \
331 \
332 static inline mc_dec128 mc_dec128_##Oper (mc_dec128 operand) \
333 { \
334 return mc_dec128_##Oper##_ex (operand, NULL); \
335 }
336
337DECL_IDF_UNOP_WRAPPER (log2)
338DECL_IDF_UNOP_WRAPPER (log10)
339#undef DECL_IDF_UNOP_WRAPPER
340
341static inline mc_dec128
342mc_dec128_round_integral_ex (mc_dec128 value,
343 mc_dec128_rounding_mode direction,
344 mc_dec128_flagset *flags)
345{
346 BID_UINT128 bid = _mc_to_bid128 (value);
347 mc_dec128_flagset zero_flags = {0};
348 _IDEC_flags *fl = flags ? &flags->bits : &zero_flags.bits;
349 switch (direction) {
350 case MC_DEC128_ROUND_TOWARD_ZERO:
351 return _bid128_to_mc (bid128_round_integral_zero (bid, fl));
352 case MC_DEC128_ROUND_NEAREST_AWAY:
353 return _bid128_to_mc (bid128_round_integral_nearest_away (bid, fl));
354 case MC_DEC128_ROUND_NEAREST_EVEN:
355 return _bid128_to_mc (bid128_round_integral_nearest_even (bid, fl));
356 case MC_DEC128_ROUND_DOWNWARD:
357 return _bid128_to_mc (bid128_round_integral_negative (bid, fl));
358 case MC_DEC128_ROUND_UPWARD:
359 return _bid128_to_mc (bid128_round_integral_positive (bid, fl));
360 default:
361 abort ();
362 }
363}
364
365static inline mc_dec128
366mc_dec128_negate (mc_dec128 operand)
367{
368 return _bid128_to_mc (bid128_negate (_mc_to_bid128 (operand)));
369}
370
371static inline mc_dec128
372mc_dec128_abs (mc_dec128 operand)
373{
374 return _bid128_to_mc (bid128_abs (_mc_to_bid128 (operand)));
375}
376
386static inline mc_dec128
387mc_dec128_scale_ex (mc_dec128 fac,
388 long int exp,
389 mc_dec128_rounding_mode rounding,
390 mc_dec128_flagset *flags)
391{
392 mc_dec128_flagset zero_flags = {0};
393 return _bid128_to_mc (
394 bid128_scalbln (_mc_to_bid128 (fac),
395 exp,
396 rounding,
397 flags ? &flags->bits : &zero_flags.bits));
398}
399
407static inline mc_dec128
408mc_dec128_scale (mc_dec128 fac, long int exp)
409{
410 return mc_dec128_scale_ex (fac, exp, MC_DEC128_ROUND_DEFAULT, NULL);
411}
412
414typedef struct mc_dec128_modf_result {
416 mc_dec128 whole;
418 mc_dec128 frac;
419} mc_dec128_modf_result;
420
430static inline mc_dec128_modf_result
431mc_dec128_modf_ex (mc_dec128 d, mc_dec128_flagset *flags)
432{
433 mc_dec128_flagset zero_flags = {0};
434 mc_dec128_modf_result res;
435 BID_UINT128 whole;
436 res.frac = _bid128_to_mc (bid128_modf (
437 _mc_to_bid128 (d), &whole, flags ? &flags->bits : &zero_flags.bits));
438 res.whole = _bid128_to_mc (whole);
439 return res;
440}
441
450static inline mc_dec128_modf_result
451mc_dec128_modf (mc_dec128 d)
452{
453 return mc_dec128_modf_ex (d, NULL);
454}
455
464static inline mc_dec128
465mc_dec128_fmod_ex (mc_dec128 numer, mc_dec128 denom, mc_dec128_flagset *flags)
466{
467 mc_dec128_flagset zero_flags = {0};
468 return _bid128_to_mc (bid128_fmod (_mc_to_bid128 (numer),
469 _mc_to_bid128 (denom),
470 flags ? &flags->bits : &zero_flags.bits));
471}
472
480static inline mc_dec128
481mc_dec128_fmod (mc_dec128 numer, mc_dec128 denom)
482{
483 return mc_dec128_fmod_ex (numer, denom, NULL);
484}
485
493static inline int64_t
494mc_dec128_to_int64_ex (mc_dec128 d, mc_dec128_flagset *flags)
495{
496 mc_dec128_flagset zero_flags = {0};
497 return bid128_to_int64_int (_mc_to_bid128 (d),
498 flags ? &flags->bits : &zero_flags.bits);
499}
500
507static inline int64_t
508mc_dec128_to_int64 (mc_dec128 d)
509{
510 return mc_dec128_to_int64_ex (d, NULL);
511}
512
514enum {
516 MC_DEC128_COMBO_NONCANONICAL = 3 << 15,
518 MC_DEC128_COMBO_INFINITY = 0x1e << 12,
520 MC_DEC128_MAX_BIASED_EXPONENT = 6143 + 6144,
522 MC_DEC128_EXPONENT_BIAS = 6143 + 33, // +33 to include the 34 decimal digits
524 MC_DEC_MIN_EXPONENT = -6143,
526 MC_DEC_MAX_EXPONENT = 6144,
527};
528
530static inline uint32_t
531mc_dec128_combination (mc_dec128 d)
532{
533 // Grab the high 64 bits:
534 uint64_t hi = d._words[MLIB_IS_LITTLE_ENDIAN ? 1 : 0];
535 // Sign is the 64th bit:
536 int signpos = 64 - 1;
537 // Combo is the next 16 bits:
538 int fieldpos = signpos - 17;
539 int fieldmask = (1 << 17) - 1;
540 return (uint32_t) ((hi >> fieldpos) & (uint32_t) fieldmask);
541}
542
546static inline uint64_t
547mc_dec128_coeff_high (mc_dec128 d)
548{
549 uint64_t hi_field_mask = (1ull << 49) - 1;
550 uint32_t combo = mc_dec128_combination (d);
551 if (combo < MC_DEC128_COMBO_NONCANONICAL) {
552 uint64_t hi = d._words[MLIB_IS_LITTLE_ENDIAN ? 1 : 0];
553 return hi & hi_field_mask;
554 } else {
555 return 0;
556 }
557}
558
562static inline uint64_t
563mc_dec128_coeff_low (mc_dec128 d)
564{
565 uint32_t combo = mc_dec128_combination (d);
566 if (combo < MC_DEC128_COMBO_NONCANONICAL) {
567 uint64_t lo = d._words[MLIB_IS_LITTLE_ENDIAN ? 0 : 1];
568 return lo;
569 } else {
570 return 0;
571 }
572}
573
578static inline mlib_int128
579mc_dec128_coeff (mc_dec128 d)
580{
581 // Hi bits
582 uint64_t hi = mc_dec128_coeff_high (d);
583 // Lo bits
584 uint64_t lo = mc_dec128_coeff_low (d);
585 // Shift and add
586 mlib_int128 hi_128 = mlib_int128_lshift (MLIB_INT128_CAST (hi), 64);
587 return mlib_int128_add (hi_128, MLIB_INT128_CAST (lo));
588}
589
598static inline uint32_t
599mc_dec128_get_biased_exp (mc_dec128 d)
600{
601 uint32_t combo = mc_dec128_combination (d);
602 if (combo < MC_DEC128_COMBO_NONCANONICAL) {
603 return combo >> 3;
604 }
605 if (combo >= MC_DEC128_COMBO_INFINITY) {
606 return MC_DEC128_MAX_BIASED_EXPONENT + 1;
607 } else {
608 return (combo >> 1) & ((1 << 14) - 1);
609 }
610}
611
613static inline char *
614mc_dec128_to_new_decimal_string (mc_dec128 d)
615{
616 if (mc_dec128_is_zero (d)) {
617 // Just return "0"
618 char *s = (char *) calloc (2, 1);
619 if (s) {
620 s[0] = '0';
621 }
622 return s;
623 }
624
625 if (mc_dec128_is_negative (d)) {
626 // Negate the result, return a string with a '-' prefix
627 d = mc_dec128_negate (d);
628 char *s = mc_dec128_to_new_decimal_string (d);
629 if (!s) {
630 return NULL;
631 }
632 char *s1 = (char *) calloc (strlen (s) + 2, 1);
633 if (s1) {
634 s1[0] = '-';
635 strcpy (s1 + 1, s);
636 }
637 free (s);
638 return s1;
639 }
640
641 if (mc_dec128_is_inf (d) || mc_dec128_is_nan (d)) {
642 const char *r = mc_dec128_is_inf (d) ? "Infinity" : "NaN";
643 char *c = (char *) calloc (strlen (r) + 1, 1);
644 if (c) {
645 strcpy (c, r);
646 }
647 return c;
648 }
649
650 const char DIGITS[] = "0123456789";
651 const mc_dec128 TEN = MC_DEC128_C (10);
652
653 // Format the whole and fractional part separately.
654 mc_dec128_modf_result modf = mc_dec128_modf (d);
655
656 if (mc_dec128_is_zero (modf.frac)) {
657 // This is a non-zero integer
658 // Allocate enough digits:
659 mc_dec128 log10 = mc_dec128_modf (mc_dec128_log10 (d)).whole;
660 int64_t ndigits = mc_dec128_to_int64 (log10) + 1;
661 // +1 for null
662 char *strbuf = (char *) calloc ((size_t) (ndigits + 1), 1);
663 if (strbuf) {
664 // Write the string backwards:
665 char *optr = strbuf + ndigits - 1;
666 while (!mc_dec128_is_zero (modf.whole)) {
667 mc_dec128 rem = mc_dec128_fmod (modf.whole, TEN);
668 int64_t remi = mc_dec128_to_int64 (rem);
669 *optr-- = DIGITS[remi];
670 // Divide ten
671 modf = mc_dec128_modf (mc_dec128_div (modf.whole, TEN));
672 }
673 }
674 return strbuf;
675 } else if (mc_dec128_is_zero (modf.whole)) {
676 // This is only a fraction (less than one, but more than zero)
677 while (!mc_dec128_is_zero (mc_dec128_modf (d).frac)) {
678 d = mc_dec128_mul (d, TEN);
679 }
680 // 'd' is now a whole number
681 char *part = mc_dec128_to_new_decimal_string (d);
682 if (!part) {
683 return NULL;
684 }
685 char *buf = (char *) calloc (strlen (part) + 3, 1);
686 if (buf) {
687 buf[0] = '0';
688 buf[1] = '.';
689 strcpy (buf + 2, part);
690 }
691 free (part);
692 return buf;
693 } else {
694 // We have both a whole part and a fractional part
695 char *whole = mc_dec128_to_new_decimal_string (modf.whole);
696 if (!whole) {
697 return NULL;
698 }
699 char *frac = mc_dec128_to_new_decimal_string (modf.frac);
700 if (!frac) {
701 free (whole);
702 return NULL;
703 }
704 char *ret = (char *) calloc (strlen (whole) + strlen (frac) + 1, 1);
705 if (ret) {
706 char *out = ret;
707 strcpy (out, whole);
708 out += strlen (whole);
709 // "frac" contains a leading zero, which we don't want
710 strcpy (out, frac + 1);
711 }
712 free (whole);
713 free (frac);
714 return ret;
715 }
716}
717
718static inline mc_dec128
719mc_dec128_from_bson_iter (bson_iter_t *it)
720{
721 bson_decimal128_t b;
722 if (!bson_iter_decimal128 (it, &b)) {
723 mc_dec128 nan = MC_DEC128_POSITIVE_NAN;
724 return nan;
725 }
726 mc_dec128 ret;
727 memcpy (&ret, &b, sizeof b);
728 return ret;
729}
730
731static inline bson_decimal128_t
732mc_dec128_to_bson_decimal128 (mc_dec128 v)
733{
734 bson_decimal128_t ret;
735 memcpy (&ret, &v, sizeof ret);
736 return ret;
737}
738
739MLIB_C_LINKAGE_END
740
741#endif
742
743#endif // MC_DEC128_H_INCLUDED