#include #include "fixedpoint96.h" void initFixedPt(int64 i, uint32 f, fixedPt* result) { result->i = i; result->f = f; } // logical shift static void shiftRightSigned(int64 input, uint shiftAmount, int64* result) { if (input < 0) { *result = ~(~input >> shiftAmount); } else { *result = input >> shiftAmount; } } // split 64 bit unsigned into two 32 bit unsigned parts // both parts live in the lowest 32 bits of the uint64 static void splitUnsigned(uint64 input, uint64* upper, uint64* lower) { *upper = input >> 32; *lower = input - (*upper << 32); } // split 64 bit signed into two 32 bit parts // both parts live in the lowest 32 bits of the 64 bit int // upper keeps the sign // lower is unsigned static void splitSigned(int64 input, int64* upper, uint64* lower) { shiftRightSigned(input, 32, upper); *lower = (uint64)(input - (*upper << 32)); } static void addFractional(uint32 a, uint32 b, uint32* result, uint32* carry) { uint64 a64 = (uint64)a; uint64 b64 = (uint64)b; uint64 carry64, result64; splitUnsigned(a64 + b64, &carry64, &result64); // carry is 0 or 1 *carry = (uint32)carry64; *result = (uint32)result64; } // returns 1 if sum overflows static int addSignedIntegers(int64 a, int64 b, int64* result) { if (b >= 0 && a > LLONG_MAX - b) { return 1; } else if (b < 0 && a < LLONG_MIN - b) { return 1; } *result = a + b; return 0; } int roundFixedPt(fixedPt* fp, int64* result) { return addSignedIntegers(fp->i, (int64)(fp->f >= 0x80000000), result); } // returns 0 if successful, 1 otherwise int add(fixedPt* a, fixedPt* b, fixedPt* result) { uint32 carry; addFractional(a->f, b->f, &result->f, &carry); // detect overflow while trying each combination: 3 terms, 2 operations int64 val; // (a + carry) + b if (addSignedIntegers(a->i, (int64)carry, &val) == 0) { if (addSignedIntegers(val, b->i, &result->i) == 0) { return 0; } } // a + (carry + b) if (addSignedIntegers((int64)carry, b->i, &val) == 0) { if (addSignedIntegers(a->i, val, &result->i) == 0) { return 0; } } // (a + b) + carry if (addSignedIntegers(a->i, b->i, &val) == 0) { if (addSignedIntegers(val, (int64)carry, &result->i) == 0) { return 0; } } // unavoidable overflow return 1; } // always successful // subtract borrow from a's next MSB [integer] part static void subtractFractional(uint32 a, uint32 b, uint32* result, int64* borrow) { *result = a - b; *borrow = (a < b) ? 1 : 0; } // returns 1 if subtraction goes out of range static int subtractSignedIntegers(int64 a, int64 b, int64* result) { if (b < 0 && a > LLONG_MAX + b) { return 1; } else if (b >= 0 && a < LLONG_MIN + b) { return 1; } *result = a - b; return 0; } // returns 1 if result would go out of range int subtract(fixedPt* a, fixedPt* b, fixedPt* result) { int64 borrow; subtractFractional(a->f, b->f, &result->f, &borrow); // detect overflow while trying each combination: 3 terms, 2 operations int64 val; // (a - borrow) - b if (subtractSignedIntegers(a->i, borrow, &val) == 0) { if (subtractSignedIntegers(val, b->i, &result->i) == 0) { return 0; } } // a - (borrow + b) if (addSignedIntegers(borrow, b->i, &val) == 0) { if (subtractSignedIntegers(a->i, val, &result->i) == 0) { return 0; } } // (a - b) - borrow if (subtractSignedIntegers(a->i, b->i, &val) == 0) { if (subtractSignedIntegers(val, borrow, &result->i) == 0) { return 0; } } // unavoidable overflow return 1; } // returns 1 if integer part overflows // fractional part is truncated int multiply(fixedPt* a, fixedPt* b, fixedPt* result) { // split everything into 32 bit values, stored in 64 bit types // that way, multiplying 2 32 bit values will fit in 64 bits // also, uint64 to int64 casts will be safe uint64 af, bf, rf; af = (uint64)a->f; bf = (uint64)b->f; uint64 ai0, bi0, ri0; int64 ai1, bi1, ri1; splitSigned(a->i, &ai1, &ai0); splitSigned(b->i, &bi1, &bi0); // actual values: // a = (2^32)*ai1 + ai0 + (2^-32)*af // b = (2^32)*bi1 + bi0 + (2^-32)*bf // // r = a*b = // A (2^64) * ai1*bi1 // B + (2^32) * (ai1*bi0 + ai0*bi1) // C + (ai0*bi0 + ai1*bf + af*bi1) // D + (2^-32) * (ai0*bf + af*bi0) // E + (2^-64) * af*bf // // // (MSB) (LSB) // -----fixedPt----- // a*b= _____|_____|_ri1_|_ri0_|_rf__|_____ // -----A----- // -----B----- // -----C----- // -----D----- // -----E----- // perform sum from LSB to MSB // - store 32 bit result // - carry overflow to next, more significant 32 bit chunk // naming // highA : 32 MSB of A stored in 32 LSB of highA // lowA : 32 LSB of A stored in 32 LSB of lowA // (2^-64) * E uint64 E = af * bf; uint64 highE = E >> 32; // omit lowE (truncated) // D = (2^-32) * (D1 + D2) uint64 highD1, lowD1, highD2, lowD2; splitUnsigned(ai0 * bf, &highD1, &lowD1); splitUnsigned(af * bi0, &highD2, &lowD2); // highD -> result LSB integer part // lowD -> result fractional part uint64 highD, uCarryD; splitUnsigned(lowD1 + lowD2 + highE, &uCarryD, &rf); splitUnsigned(highD1 + highD2 + uCarryD, &uCarryD, &highD); // C = C1 + C2 + C3 uint64 highC1; int64 highC2, highC3; uint64 lowC1, lowC2, lowC3; // C1 is unsigned (uint32 * uint32 only fits in uint64) // uint32 * int32 fits in int64 splitUnsigned(ai0 * bi0, &highC1, &lowC1); splitSigned(ai1 * (int64)bf, &highC2, &lowC2); splitSigned((int64)af * bi1, &highC3, &lowC3); // highC -> MSB integer part // lowC -> LSB integer part int64 sCarryC; uint64 highC; splitSigned((int64)lowC1 + (int64)lowC2 + (int64)lowC3 + (int64)highD, &sCarryC, &ri0); splitSigned((int64)highC1 + highC2 + highC3 + (int64)uCarryD + sCarryC, &sCarryC, &highC); // B = (2^32) * (B1 + B2) int64 highB1, highB2; uint64 lowB1, lowB2; splitSigned(ai1 * (int64)bi0, &highB1, &lowB1); splitSigned((int64)ai0 * bi1, &highB2, &lowB2); // lowB -> MSB integer part // highB -> more significant 32 bits than we can hold in fixedPt uint64 ri1Unsigned; int64 sCarryB; splitSigned((int64)lowB1 + (int64)lowB2 + (int64)highC, &sCarryB, &ri1Unsigned); int64 highB = highB1 + highB2 + sCarryC + sCarryB; // MSB of overall product keeps sign int64 A = ai1 * bi1 + highB; // check ri1Unsigned with A's sign is in range of int64 ri1Unsigned <<= 32; uint64 leftmostBitSetVal = (uint64)1 << 63; if (A == -1) { // result < 0 if (ri1Unsigned <= LLONG_MAX) { return 1; } // cast ri1Unsigned safely (set MSB to 1) ri1 = (int64)(ri1Unsigned - leftmostBitSetVal) - leftmostBitSetVal; } else if (A == 0){ // result >= 0 if (ri1Unsigned > LLONG_MAX) { return 1; } ri1 = (int64)ri1Unsigned; } else { return 1; } result->f = (uint32)rf; result->i = ri1 + ri0; return 0; }