On Fri, 31 Aug 2018 17:14:53 +0300, Anton Shepelev
<anton.txt@g{oogle}
mail.com> wrote:
>fir:
>
>>small library for basic bignum (N*long) arithmetic
>
>Am I wrong in thinking that a trivial implementation is
>possible that stores numbers as byte arrays and treats them
>numbers in base 256? Futhermore, one may encode rational
>numbers losslessly storing numerator and denominator
>separately as such bignums.
Sure, but for a trivial implementation, it's just as easy to pick a
limb* that's half the width of the longest type that's convenient to
use. So if you have unsigned long longs that are 64 bits, and
unsigned longs that are 32, using 32 bit limbs is just as easy any a
smaller value, and yet leaves you with the ability to easily catch
carries, leaves room for carry propagation, and allow you to use the
widest multiplier (since the result will then fit in the 64 bit type).
Admittedly picking the types for the limb and double-limb sized
elements is not easy to do optimally if you can rely only on standard
C. You *could* just do all the intermediate calculations in unsigned
long longs, and store limbs in unsigned longs, while assuming that you
have a 64-bit intermediate format and a 32 bit storage format. While
that will work, it may waste considerable storage if that assumption
about sizes is not true (most LP64 platforms, for example), and does
not take advantage of larger sizes (for example, many GCC platforms
have a 128-bit type, which may be useful for the intermediate
operations).
The resulting algorithms for addition, subtraction and multiplication
are trivial (heck, I've posted them a few times over the years). Half
a dozen lines for addition, a dozen for multiplication (for unsigned
numbers, and punting memory management to elsewhere).
For larger numbers there are more complex techniques that can run
faster then the O(n**2) schoolbook multiplication algorithm (Karatsuba
- ~O(n**1.6), FFTs - O(n*log n*log(log n)) ), but at the expense of
complexity. Karatsuba is not that complex, and can be worthwhile for
numbers of 4-8 limbs or larger. FFTs are rather complex, and are not
really a net gain until you get to (at least) hundreds of limbs.
Division is something of a PITA no matter what, though. It's not
hard, per-se, but the only (near) trivial algorithm is really slow,
and the best depend on a mast implementation of multiplication.
You can use larger limbs (relative to the maximum "convenient" size),
but it adds a non-trivial amount of complexity. OTOH, sticking with
the half-maximum-width limbs is pretty much free, and will usually
perform much better than using bytes.
*limb = the effective bignum digit/base you're working in.