bcd.h
View source code here on GitHub!
Includes
"math.h" (if compiled on PCC)
<stdlib.h>
(if not compiled on PCC)<math.h>
(if not compiled on PCC)
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.
-
packed_BCD_pair *digits
-
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 aBCD_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 mul_bcd_pow_10(BCD_int x, uintmax_t tens)
-
BCD_int shift_bcd_left(BCD_int x, uintmax_t 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.
-
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}