bcd.h

View source code here on GitHub!

Includes

This library implements a Binary Coded Decimal object in C. Mostly this is done to prove that I could, but also because it allows for incredibly easy printing of arbitrary-sized integers. It was also a good exercise in x86 assembly, as several portions are accellerated by handcrafted assembly.

typedef uint8_t packed_BCD_pair

This is an alias to distinguish BCD digit-pairs from normal characters.

type BCD_int
packed_BCD_pair *digits

This array holds the digit-pairs that encode the number. Because of this, you must use free_bcd() to destroy these objects without leaking memory.

size_t bcd_digits

This field indicates the size of the array. It DOES NOT match the number of decimal digits.

size_t decimal_digits

This field indicates the number of decimal digits encoded. It should be within 1 of 2 * bcd_digits.

bool negative : 1

Since digits are encoded by absolute value, this field tells you if the number is negative.

bool zero : 1

Shortcut value to determine if the encoded integer is 0.

void free_BCD_int(BCD_int x)
BCD_int new_BCD_int(uintmax_t a, bool negative)
BCD_int BCD_from_bytes(const uint8_t *str, size_t chars, bool negative, bool little_endian)

Takes in an arbitrary-sized encoded integer (like in Python's int.from_bytes()) to a BCD_int.

BCD_int BCD_from_ascii(const char *str, size_t digits, bool negative)

From an ASCII-encoded integer to a BCD_int().

BCD_int add_bcd(BCD_int x, BCD_int y)

Returns x + y.

BCD_int sub_bcd(BCD_int x, BCD_int y)

Returns x - y.

BCD_int mul_bcd_cuint(BCD_int x, uintmax_t y)

Returns x * y, handling type conversion for you.

BCD_int pow_cuint_cuint(uintmax_t x, uintmax_t y)

Returns pow(x, y), handling type conversion for you.

BCD_int mul_bcd(BCD_int x, BCD_int y)

Returns x * y.

BCD_int pow_bcd(BCD_int x, BCD_int y)

Returns pow(x, y).

BCD_int mul_bcd_pow_10(BCD_int x, uintmax_t tens)
BCD_int shift_bcd_left(BCD_int x, uintmax_t tens)

Returns x * pow(10, tens).

BCD_int div_bcd_pow_10(BCD_int a, uintmax_t tens)
BCD_int shift_bcd_right(BCD_int a, uintmax_t tens)

Returns x / pow(10, tens).

void iadd_bcd(BCD_int *const x, const BCD_int y)

Transforms x to be x + y without needing to make a new assignment.

signed char cmp_bcd(BCD_int x, BCD_int y)

Returns 1 if x > y, -1 if y > x, and otherwise 0.

void print_bcd(BCD_int x)
void print_bcd_ln(BCD_int x)

Writes a BCD_int to stdout, and optionally adds a newline.

uintmax_t bcd_to_unsigned(BCD_int a)

Converts a BCD_int to an uint32_teger.

Warning

This method DOES NOT guard against overflow.

intmax_t bcd_to_signed(BCD_int a)

Converts a BCD_int to a signed integer.

Warning

This method DOES NOT guard against overflow.

uint16_t mul_dig_pair(packed_BCD_pair ab, packed_BCD_pair cd)

Convenience method to multiply a digit pair.

  1#pragma once
  2#include <stdio.h>
  3#include <stdbool.h>
  4#include <stdint.h>
  5#include <string.h>
  6#include "macros.h"
  7
  8#if !PCC_COMPILER
  9    #include <stdlib.h>
 10    #include <math.h>
 11#else
 12    #include "math.h"
 13#endif
 14
 15typedef uint8_t packed_BCD_pair;
 16typedef struct {
 17    // a little-endian, arbitrary-precision, binary-coded decimal number
 18    packed_BCD_pair *digits;
 19    size_t bcd_digits;
 20    size_t decimal_digits;
 21    bool negative : 1;
 22    bool zero : 1;
 23    bool even : 1;
 24    bool constant : 1;
 25} BCD_int;
 26
 27
 28const BCD_int BCD_two = {
 29    .digits = (packed_BCD_pair[]) {2},
 30    .bcd_digits = 1,
 31    .decimal_digits = 1,
 32    .even = true,
 33    .constant = true
 34};  // note that non-specified values are initialized to NULL or 0
 35
 36
 37const BCD_int BCD_one = {
 38    .digits = (packed_BCD_pair[]) {1},
 39    .bcd_digits = 1,
 40    .decimal_digits = 1,
 41    .constant = true
 42};  // note that non-specified values are initialized to NULL or 0
 43
 44
 45const BCD_int BCD_zero = {
 46    .zero = true,
 47    .even = true,
 48    .constant = true
 49};  // note that non-specified values are initialized to NULL or 0
 50
 51void free_BCD_int(BCD_int x);
 52inline void free_BCD_int(BCD_int x) {
 53    if (!x.constant) {
 54        free(x.digits);
 55        x.digits = NULL;
 56        x.bcd_digits = x.decimal_digits = x.negative = x.zero = 0;
 57    }
 58}
 59
 60BCD_int new_BCD_int(uintmax_t a, bool negative) {
 61    BCD_int c;
 62    #if !PCC_COMPILER
 63        c.decimal_digits = ceil(log10(a + 1));
 64    #else
 65        c.decimal_digits = imprecise_log10(a + 1);
 66    #endif
 67    c.bcd_digits = (c.decimal_digits + 1) / 2;
 68    c.digits = (packed_BCD_pair *) malloc(sizeof(packed_BCD_pair) * c.bcd_digits);
 69    c.negative = negative;
 70    c.zero = !a;
 71    for (size_t i = 0; i < c.bcd_digits; i++) {
 72        c.digits[i] = (((a % 100) / 10) << 4) | (a % 10);
 73        a /= 100;
 74    }
 75    return c;
 76}
 77
 78BCD_int copy_BCD_int(BCD_int a);
 79inline BCD_int copy_BCD_int(BCD_int a) {
 80    BCD_int b = a;
 81    b.digits = (packed_BCD_pair *) malloc(sizeof(packed_BCD_pair) * b.bcd_digits);
 82    memcpy(b.digits, a.digits, b.bcd_digits);
 83    return b;
 84}
 85
 86BCD_int BCD_from_bytes(const uint8_t *str, size_t chars, bool negative, bool little_endian) {
 87    // converts a bytestring to a little-endian BCD int
 88    BCD_int c;
 89    if (!chars || str == NULL) {
 90        c.zero = true;
 91        c.bcd_digits = c.decimal_digits = c.negative = 0;
 92        c.digits = NULL;
 93        return c;
 94    }
 95    size_t i;
 96    c.zero = false;
 97    c.negative = negative;
 98    c.digits = (packed_BCD_pair *) malloc(sizeof(packed_BCD_pair) * chars);
 99    if (little_endian)
100        for (i = 0; i < chars; i++)
101            c.digits[i] = str[i];
102    else    {
103        size_t j;
104        for (i = 0, j = chars - 1; i < chars; i++, j--)
105            c.digits[i] = str[j];
106    }
107    for (i = chars - 1; i != -1; i--) {
108        if (c.digits[i] & 0xF0) {
109            c.decimal_digits = i * 2 + 1;
110            c.bcd_digits = i + 1;
111            break;
112        }
113        if (c.digits[i] & 0x0F) {
114            c.decimal_digits = i * 2;
115            c.bcd_digits = i + 1;
116            break;
117        }
118    }
119    if (unlikely(i == -1)) {
120        c.zero = true;
121        c.bcd_digits = c.decimal_digits = c.negative = 0;
122        free(c.digits);
123        c.digits = NULL;
124    }
125    return c;
126}
127
128BCD_int BCD_from_ascii(const char *str, size_t digits, bool negative);
129inline BCD_int BCD_from_ascii(const char *str, size_t digits, bool negative) {
130    // packs an ASCII digit string into big-endian bytes, then runs through BCD_from_bytes()
131    size_t length = (digits + 1) / 2, i, j;
132    uint8_t *bytes = (uint8_t *) malloc(sizeof(uint8_t) * length);
133    j = i = digits % 2;
134    if (i)
135        bytes[0] = str[0] - '0';
136    for (; i < length; i++, j += 2)
137        bytes[i] = ((str[j] - '0') << 4) | ((str[j + 1] - '0') & 0xF);
138    BCD_int ret = BCD_from_bytes(bytes, length, negative, false);
139    free(bytes);
140    return ret;
141}
142
143BCD_int sub_bcd(BCD_int x, BCD_int y);
144
145BCD_int add_bcd(BCD_int x, BCD_int y) {
146    // performing this on two n-digit numbers will take O(n) time
147    if (unlikely(x.zero))
148        return copy_BCD_int(y);
149    if (unlikely(y.zero))
150        return copy_BCD_int(x);
151    if (x.negative != y.negative) {
152        // if signs don't match, absolute value would go down.
153        // that means we need to flip y's sign and move through sub_bcd()
154        y.negative = !y.negative;
155        return sub_bcd(x, y);
156    }
157    BCD_int z;
158    size_t i, min_digits = min(x.bcd_digits, y.bcd_digits), max_digits = max(x.bcd_digits, y.bcd_digits);
159    z.zero = false;  // result can't be zero because x and y are non-zero and share a sign
160    z.negative = x.negative;  // we know this is also y.negative
161    z.digits = (packed_BCD_pair *) malloc(sizeof(packed_BCD_pair) * (max_digits + 1));
162    packed_BCD_pair a, b, c;
163    bool overflow = false;
164    for (i = 0; i < min_digits; i++) {
165        a = x.digits[i];
166        b = y.digits[i];
167        if (!(overflow || a)) {
168            c = b;
169            overflow = false;
170        }
171        else if (!(overflow || b)) {
172            c = a;
173            overflow = false;
174        }
175        else    {
176            b += overflow;  // incorporate overflow from last pair
177            #if (!defined(NO_ASSEMBLY) && X86_COMPILER)
178                // if on these architectures, there's assembly tricks to adjust BCD in-silicon
179                #if CL_COMPILER
180                    // CL compiler has a different syntax for inline assembly, does a lot less lifting
181                    // note that the syntax here is: op [dest [, src]]
182                    // brackets indicate a C symbol is referenced rather than a register
183                    __asm   {
184                        mov al, [a];         // move C symbol a to register al
185                        add al, [b];         // add C symbol b to register al
186                        daa;                 // have the CPU make sure register al contains valid, packed BCD digits
187                        setc [overflow];     // set C symbol overflow to contain the carry bit, set by daa
188                        mov [c], al;         // move register al to C symbol c
189                    }
190                #else
191                    // this is the standard GCC/LLVM syntax for it
192                    // note that the syntax here is: op [[src, ] dest]
193                    // %\d indicates a C symbol is referenced, see the lookups at the end of code for which
194                    __asm__(
195                        "add %3, %%al;"    // add the register containing b to al
196                        "daa;"             // have the CPU make sure register al contains valid, packed BCD digits
197                        "setc %1;"         // set the register containing overflow to hold the carry bit, set by daa
198                                           // this next section tells the compiler what to do after execution
199                      : "=a" (c),          // store the contents of register al in symbol c
200                        "=rgm" (overflow)  // and a general register or memory location gets assigned to symbol overflow (referenced as %1)
201                                           // then below tells the compiler what our inputs are
202                      : "a" (a),           // symbol a should get dumped to register al
203                        "rgm" (b)          // and symbol b in a general register or memory location (referenced as %3)
204                    );
205                #endif
206            #else
207                // otherwise fall back to doing it in C
208                c = a + b;                             // set c to be the result of (a + b) % 0x100
209                if ((overflow = (a > 0x99 - b))) {  // if c would overflow the decimal range
210                    c += 0x60;                         // and add 0x60 to make a decimal digit
211                }
212                if (((a & 0xF) + (b & 0xF)) > 9) {  // if the lower nibble be bigger than 9
213                    c += 0x06;                         // add 6 to make a decimal digit
214                }
215            #endif
216            }
217        z.digits[i] = c;
218    }
219    if (x.bcd_digits < y.bcd_digits)
220        x = y;
221    for (; overflow && i < max_digits; i++) {  // while there's overflow and digits, continue adding
222        a = x.digits[i] + overflow;
223        if ((a & 0x0F) == 0x0A)  // since all that's left is overflow, we don't need to check ranges
224            a += 0x06;
225        if ((overflow = ((a & 0xF0) == 0xA0)))
226            a += 0x60;
227        z.digits[i] = a;
228    }
229    for (; i < max_digits; i++)  // if there's no more overflow, but still digits left, copy directly
230        z.digits[i] = x.digits[i];
231    z.digits[max_digits] = overflow;
232    z.bcd_digits = max_digits + overflow;
233    if (overflow)
234        z.decimal_digits = max_digits * 2 + 1;
235    else if (z.digits[max_digits - 1] & 0xF0)
236        z.decimal_digits = max_digits * 2;
237    else
238        z.decimal_digits = max_digits * 2 - 1;
239    return z;
240}
241
242BCD_int mul_bcd_pow_10(BCD_int x, uintmax_t tens) {
243    // this takes O(log_100(x)) time. Note that it's significantly faster if tens is even
244    // returns x * 10^tens
245    BCD_int ret;
246    if (unlikely(x.zero))
247        return new_BCD_int(0, false);
248    ret.zero = false;
249    ret.negative = x.negative;
250    ret.decimal_digits = x.decimal_digits + tens;
251    ret.bcd_digits = (ret.decimal_digits + 1) / 2;
252    ret.digits = (packed_BCD_pair *) calloc(ret.bcd_digits, sizeof(packed_BCD_pair));
253    if (tens % 2 == 0) {
254        // +--+--+    +--+--+--+
255        // |23|01| -> ...|23|01|
256        // +--+--+    +--+--+--+
257        const size_t digit_diff = ret.bcd_digits - x.bcd_digits;
258        memcpy(ret.digits + digit_diff, x.digits, x.bcd_digits);
259    }
260    else    {
261        // +--+--+    +--+--+--+
262        // |23|01| -> ...|30|12|
263        // +--+--+    +--+--+--+
264        // +--+--+    +--+--+--+--+
265        // |34|12| -> ...|40|23|01|
266        // +--+--+    +--+--+--+--+
267        const size_t digit_diff = ret.bcd_digits - x.bcd_digits - ret.decimal_digits % 2;
268        // note that digit_diff needs to be adjusted on this branch, so it can't be common
269        ret.digits[digit_diff] = x.digits[0] << 4;
270        for (size_t i = 1; i < x.bcd_digits; i++) {
271            ret.digits[i + digit_diff] = x.digits[i] << 4;
272            ret.digits[i + digit_diff] |= x.digits[i - 1] >> 4;
273        }
274        ret.digits[x.bcd_digits + digit_diff] |= x.digits[x.bcd_digits - 1] >> 4;
275    }
276    return ret;
277}
278
279BCD_int shift_bcd_left(BCD_int x, uintmax_t tens);
280inline BCD_int shift_bcd_left(BCD_int x, uintmax_t tens) {
281    return mul_bcd_pow_10(x, tens);
282}
283
284BCD_int mul_bcd_cuint(BCD_int x, uintmax_t y) {
285    // this takes roughly O(log(y) * log(x)) time, and is nearly linear if y is a multiple of 10
286    // it works by breaking the multiplication down into groups of addition
287    // full formula for performance is something like:
288    // y = 10^a * b
289    // f(i) = sum(n = 1 thru 19, (i % 10^(n + 1)) / 10^n) + i / 10^20
290    // bool(i) = 1 if i else 0
291    // time = O(bool(a) * log_100(x) + f(b) * log_100(xy))
292    if (unlikely(!y || x.zero))
293        return new_BCD_int(0, false);
294    uint8_t tens = 0;  // will up size when there's an 848-bit system
295    BCD_int ret, sum, mul_by_power_10;
296    // first remove factors of ten
297    while (y % 10 == 0) {
298        y /= 10;
299        ++tens;
300    }
301    if (tens)
302        ret = mul_bcd_pow_10(x, tens);
303    else
304        ret = new_BCD_int(0, x.negative);
305    // then for decreasing powers of ten, batch additions
306    uintmax_t p = (sizeof(uintmax_t) == 16) ? MAX_POW_10_128 : MAX_POW_10_64;
307    tens = (sizeof(uintmax_t) == 16) ? POW_OF_MAX_POW_10_128 : POW_OF_MAX_POW_10_64;
308    for (; p > 1; p /= 10, --tens) {
309        while (y >= p) {
310            mul_by_power_10 = mul_bcd_pow_10(x, tens);
311            sum = add_bcd(ret, mul_by_power_10);
312            free_BCD_int(mul_by_power_10);
313            free_BCD_int(ret);
314            ret = sum;
315            y -= p;
316        }
317    }
318    // then do simple addition
319    while (y--) {
320        sum = add_bcd(ret, x);
321        free_BCD_int(ret);
322        ret = sum;
323    }
324    return ret;
325}
326
327BCD_int pow_cuint_cuint(uintmax_t x, uintmax_t y);
328inline BCD_int pow_cuint_cuint(uintmax_t x, uintmax_t y) {
329    // this takes roughly O(xylog_100(xy)) time
330    BCD_int answer = new_BCD_int(1, false), tmp;
331    while (y--) {
332        tmp = mul_bcd_cuint(answer, x);
333        free_BCD_int(answer);
334        answer = tmp;
335    }
336    return answer;
337}
338
339uint16_t mul_dig_pair(packed_BCD_pair ab, packed_BCD_pair cd);
340inline uint16_t mul_dig_pair(packed_BCD_pair ab, packed_BCD_pair cd) {
341    // multiplies two digits pairs and returns an unsigned C short. valid range is 0 thru 9801
342    uint8_t a, b, c, d;
343    // first unpack the digits
344    a = ab >> 4;
345    b = ab & 0xF;
346    c = cd >> 4;
347    d = cd & 0xF;
348    // then solve by FOIL
349    return 100 * a * c + 10 * (a * d + b * c) + b * d;
350}
351
352BCD_int mul_bcd(BCD_int x, BCD_int y) {
353    // multiplies two BCD ints by breaking them down into their component bytes and adding the results
354    // this takes O(log_100(x) * log_100(y) * log_100(xy)) time
355    BCD_int answer = new_BCD_int(0, false), addend, tmp;
356    if (unlikely(x.zero || y.zero))
357        return answer;
358    size_t i, j;
359    uint16_t staging;
360    uintmax_t ipow_10 = 0, pow_10;
361    for (i = 0; i < x.bcd_digits; i++, ipow_10 += 2) {
362        for (j = 0, pow_10 = ipow_10; j < y.bcd_digits; j++, pow_10 += 2) {
363            staging = mul_dig_pair(x.digits[i], y.digits[j]);
364            if (staging == 0)
365                continue;
366            if (likely(pow_10)) {
367                tmp = new_BCD_int(staging, false);
368                addend = mul_bcd_pow_10(tmp, pow_10);  // this was not added to performance analysis
369                free_BCD_int(tmp);
370            }
371            else
372                addend = new_BCD_int(staging, false);
373            tmp = add_bcd(answer, addend);
374            free_BCD_int(addend);
375            free_BCD_int(answer);
376            answer = tmp;
377        }
378    }
379    answer.negative = !(x.negative == y.negative);
380    return answer;
381}
382
383BCD_int pow_bcd(BCD_int x, BCD_int y) {
384    // this takes O(y * 2log_100(x) * log_100(x)^2) time
385    BCD_int answer = new_BCD_int(1, false), tmp, one = new_BCD_int(1, false);
386    while (!y.zero) {
387        tmp = mul_bcd(answer, x);
388        free_BCD_int(answer);
389        answer = tmp;
390        tmp = sub_bcd(y, one);
391        free_BCD_int(y);
392        y = tmp;
393    }
394    free_BCD_int(one);
395    return answer;
396}
397
398signed char cmp_bcd(BCD_int x, BCD_int y) {
399    // returns:
400    // 1 if x > y
401    // -1 if y > x
402    // else 0
403    if (x.negative != y.negative)
404        return (x.negative) ? -1 : 1;
405    if (x.decimal_digits != y.decimal_digits) {
406        if (x.decimal_digits > y.decimal_digits)
407            return (x.negative) ? -1 : 1;
408        return (x.negative) ? 1 : -1;
409    }
410    for (size_t i = x.bcd_digits - 1; i != -1; i--) {
411        if (x.negative)
412            return (x.digits[i] > y.digits[i]) ? -1 : 1;
413        return (x.digits[i] > y.digits[i]) ? 1 : -1;
414    }
415    return 0;
416}
417
418BCD_int sub_bcd(BCD_int x, BCD_int y) {
419    if (unlikely(x.zero)) {
420        BCD_int ret = copy_BCD_int(y);
421        ret.negative = !ret.negative;
422        return ret;
423    }
424    if (unlikely(y.zero))
425        return copy_BCD_int(x);
426    if (x.negative != y.negative) {
427        // if signs don't match, absolute value would go up.
428        // that means we need to flip y's sign and move through add_bcd()
429        y.negative = !y.negative;
430        return add_bcd(x, y);
431    }
432    signed char cmp = cmp_bcd(x, y);
433    BCD_int z;
434    if ((z.zero = !cmp))
435        return new_BCD_int(0, false);
436    z.negative = (cmp == -1);
437    size_t i, min_digits = min(x.bcd_digits, y.bcd_digits), max_digits = max(x.bcd_digits, y.bcd_digits);
438    z.digits = (packed_BCD_pair *) malloc(sizeof(packed_BCD_pair) * max_digits);
439    packed_BCD_pair a, b, c;
440    bool carry = false;
441    for (i = 0; i < min_digits; i++) {
442        a = x.digits[i];
443        b = y.digits[i];
444        if (!(carry || a)) {
445            c = b;
446            carry = false;
447        }
448        else if (!(carry || b)) {
449            c = a;
450            carry = false;
451        }
452        else    {
453            b += carry;  // incorporate carry from last pair
454            #if (!defined(NO_ASSEMBLY) && X86_COMPILER)
455                // if on these architectures, there's assembly tricks to adjust BCD in-silicon
456                #if CL_COMPILER
457                    // CL compiler has a different syntax for inline assembly, does a lot less lifting
458                    // note that the syntax here is: op [dest [, src]]
459                    // brackets indicate a C symbol is referenced rather than a register
460                    __asm   {
461                        mov al, [a];         // move C symbol a to register al
462                        sub al, [b];         // add C symbol b to register al
463                        das;                 // have the CPU make sure register al contains valid, packed BCD digits
464                        setc [carry];        // set C symbol carry to contain the carry bit, set by daa
465                        mov [c], al;         // move register al to C symbol c
466                    }
467                #else
468                    // this is the standard GCC/LLVM syntax for it
469                    // note that the syntax here is: op [[src, ] dest]
470                    // %\d indicates a C symbol is referenced, see the lookups at the end of code for which
471                    __asm__(
472                        "sub %3, %%al;"    // add the register containing b to al
473                        "das;"             // have the CPU make sure register al contains valid, packed BCD digits
474                        "setc %1;"         // set the register containing carry to hold the carry bit, set by daa
475                                           // this next section tells the compiler what to do after execution
476                      : "=a" (c),          // store the contents of register al in symbol c
477                        "=rgm" (carry)     // and a general register or memory location gets assigned to symbol carry (referenced as %1)
478                                           // then below tells the compiler what our inputs are
479                      : "a" (a),           // symbol a should get dumped to register al
480                        "rgm" (b)          // and symbol b in a general register or memory location (referenced as %3)
481                    );
482                #endif
483            #else
484                // otherwise fall back to doing it in C
485                c = a - b;                             // set c to be the result of (a - b) % 0x100
486                if ((carry = (c & 0xF0) > 0x99)) {  // if c would overflow the decimal range
487                    c -= 0x60;                         // and subtract 0x60 to make a decimal digit
488                }
489                if ((c & 0x0F) > 9) {                 // if the lower nibble be bigger than 9
490                    c -= 0x06;                         // subtract 6 to make a decimal digit
491                }
492            #endif
493            }
494        z.digits[i] = c;
495    }
496    if (x.bcd_digits < y.bcd_digits)
497        x = y;
498    for (; carry && i < max_digits; i++) {  // while there's carry and digits, continue adding
499        a = x.digits[i] - carry;
500        if ((a & 0x0F) == 0x0F)  // since all that's left is carry, we don't need to check ranges
501            a -= 0x06;
502        if ((carry = ((a & 0xF0) == 0xF0)))
503            a -= 0x60;
504        z.digits[i] = a;
505    }
506    for (; i < max_digits; i++)  // if there's no more carry, but still digits left, copy directly
507        z.digits[i] = x.digits[i];
508    z.bcd_digits = i;
509    z.decimal_digits = z.bcd_digits / 2;
510    if (!(z.digits[i - 1] & 0xF0))
511        z.decimal_digits--;
512    return z;
513}
514
515BCD_int div_bcd_pow_10(BCD_int a, uintmax_t tens) {
516    if (unlikely(a.zero))
517        return copy_BCD_int(a);
518    BCD_int ret;
519    ret.negative = a.negative;
520    ret.zero = false;
521    ret.decimal_digits = a.decimal_digits - tens;
522    if (tens % 2 == 0) {
523        ret.bcd_digits = a.bcd_digits - tens / 2;
524        ret.digits = (packed_BCD_pair *) malloc(sizeof(packed_BCD_pair) * ret.bcd_digits);
525        memcpy(ret.digits, a.digits + tens / 2, ret.bcd_digits);
526    }
527    else {
528        // TODO
529    }
530    return ret;
531}
532
533BCD_int shift_bcd_right(BCD_int a, uintmax_t tens);
534inline BCD_int shift_bcd_right(BCD_int a, uintmax_t tens) {
535    return div_bcd_pow_10(a, tens);
536}
537
538void iadd_bcd(BCD_int *const x, const BCD_int y) {
539    BCD_int ret = add_bcd(*x, y);
540    free_BCD_int(*x);
541    *x = ret;
542}
543
544uintmax_t bcd_to_unsigned(BCD_int a) {
545    if (a.zero)
546        return 0;
547    uintmax_t answer = 0;
548    for (size_t i = 0; i < a.bcd_digits; ++i) {
549        size_t digit = a.bcd_digits - i - 1;
550        answer = answer * 100 + ((a.digits[digit] & 0xF0) >> 4) * 10 + (a.digits[digit] & 0xF);
551    }
552    return answer;
553}
554
555intmax_t bcd_to_signed(BCD_int a);
556inline intmax_t bcd_to_signed(BCD_int a) {
557    intmax_t answer = (intmax_t) bcd_to_unsigned(a);
558    if (a.negative)
559        return -answer;
560    return answer;
561}
562
563void print_bcd(BCD_int x) {
564    if (unlikely(x.zero)) {
565        printf("0");
566        return;
567    }
568    if (x.negative)
569        printf("-");
570    size_t i = x.bcd_digits - 1;
571    printf("%x", x.digits[i]);
572    if (i--)
573        for (; i != -1; i--)
574            printf("%02x", x.digits[i]);
575}
576
577void print_bcd_ln(BCD_int x);
578inline void print_bcd_ln(BCD_int x) {
579    print_bcd(x);
580    printf("\n");
581}

Tags: large-numbers