/*************************************************************************** * __________ __ ___. * Open \______ \ ____ ____ | | _\_ |__ _______ ___ * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ * \/ \/ \/ \/ \/ * $Id$ * * Copyright (C) 2018 by Michael A. Sevakis * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY * KIND, either express or implied. * ****************************************************************************/ #include "ap_int.h" #include "fixedpoint.h" /* Miscellaneous large-sized integer functions */ /* round string, base 10 */ bool round_number_string10(char *p_rdig, long len) { /* * * p should point to the digit that determines if rounding should occur * * buffer is updated in reverse * * an additional '1' may be added to the beginning: eg. 9.9 => 10.0 */ #if 1 /* nearest */ bool round = p_rdig[0] >= '5'; #else /* even */ bool round = p_rdig[0] >= '5' && (p_rdig[-1] & 1); #endif while (round && len-- > 0) { int d = *--p_rdig; round = ++d > '9'; *p_rdig = round ? '0' : d; } if (round) { /* carry to the next place */ *--p_rdig = '1'; } return round; } /* format arbitrary-precision base 10 integer */ char * format_ap_int10(struct ap_int *a, char *p_end) { /* * * chunks are in least-to-most-significant order * * chunk array is used for intermediate division results * * digit string buffer is written high-to-low address order */ long numchunks = a->numchunks; char *p = p_end; if (numchunks == 0) { /* fast formatting */ uint64_t val = a->val; do { *--p = val % 10 + '0'; val /= 10; } while (val); a->len = p_end - p; return p; } uint32_t *chunks = a->chunks; long topchunk = numchunks - 1; /* if top chunk(s) are zero, ignore */ while (topchunk >= 0 && chunks[topchunk] == 0) { topchunk--; } /* optimized to divide number by the biggest 10^x a uint32_t can hold so that r_part holds the remainder (x % 1000000000) at the end of the division */ do { uint64_t r_part = 0; for (long i = topchunk; i >= 0; i--) { /* * Testing showed 29 bits as a sweet spot: * * Is a 32-bit constant (good for 32-bit hardware) * * No more normalization is required than with 30 and 31 * (32 bits requires the least but also a large constant) * * Doesn't need to be reduced before hand by subtracting the * divisor in order to keep it 32-bits which obviates the need * to correct with another term of the remainder after * multiplying * * 2305843009 = floor(ldexp(1, 29) / 1000000000.0 * ldexp(1, 32)) */ static const unsigned long c = 2305843009; /* .213693952 */ uint64_t q_part = r_part*c >> 29; r_part = (r_part << 32) | chunks[i]; r_part -= q_part*1000000000; /* if remainder is still out of modular range, normalize it and carry over into quotient */ while (r_part >= 1000000000) { r_part -= 1000000000; q_part++; } chunks[i] = q_part; } /* if top chunk(s) became zero, ignore from now on */ while (topchunk >= 0 && chunks[topchunk] == 0) { topchunk--; } /* format each digit chunk, padded to width 9 if not the leading one */ uint32_t val = r_part; int len = 8*(topchunk >= 0); while (len-- >= 0 || val) { *--p = (val % 10) + '0'; val /= 10; } } while (topchunk >= 0); a->len = p_end - p; return p; } /* format arbitrary-precision base 10 fraction */ char * format_ap_frac10(struct ap_int *a, char *p_start, long precision) { /* * * chunks are in least-to-most-significant order * * chunk array is used for intermediate multiplication results * * digit string buffer is written low-to-high address order * * high bit of fraction must be left-justified to a chunk * boundary */ long numchunks = a->numchunks; bool trimlz = precision < 0; char *p = p_start; if (trimlz) { /* trim leading zeros and provide digits; a->len will end up greater than the specified precision unless the value is zero */ precision = -precision; } a->len = precision; if (numchunks == 0) { /* fast formatting; shift must be <= 60 as top four bits are used for digit carryout */ if (trimlz && !a->val) { /* value is zero */ trimlz = false; } uint64_t val = a->val << (60 - a->shift); while (precision > 0) { val *= 10; uint32_t c_part = val >> 60; if (trimlz) { if (!c_part) { a->len++; continue; } trimlz = false; } *p++ = c_part + '0'; val ^= (uint64_t)c_part << 60; precision--; } return p; } uint32_t *chunks = a->chunks; long bottomchunk = 0, topchunk = numchunks; while (topchunk > 0 && chunks[topchunk - 1] == 0) { topchunk--; } /* optimized to multiply number by the biggest 10^x a uint32_t can hold so that c_part holds the carryover into the integer part at the end of the multiplication */ while (precision > 0) { /* if bottom chunk(s) are or became zero, skip them */ while (bottomchunk < numchunks && chunks[bottomchunk] == 0) { bottomchunk++; } uint32_t c_part = 0; for (long i = bottomchunk; i < topchunk; i++) { uint64_t p_part = chunks[i]; p_part = p_part * 1000000000 + c_part; c_part = p_part >> 32; chunks[i] = p_part; } if (topchunk < numchunks && c_part) { chunks[topchunk++] = c_part; c_part = 0; } int len = 9; if (trimlz && bottomchunk < numchunks) { if (!c_part) { a->len += 9; continue; } /* first non-zero chunk has leading zeros? */ for (uint32_t val = c_part; val < 100000000; val *= 10) { len--; } a->len += 9 - len; trimlz = false; } /* format each digit chunk, padded to width 9 if not exceeding precision */ precision -= len; if (precision < 0) { /* remove extra digits */ c_part /= ipow(10, -precision); len += precision; } p += len; char *p2 = p; while (len-- > 0) { *--p2 = (c_part % 10) + '0'; c_part /= 10; } } return p; }