fp_t fp_mul(fp_t a, fp_t b) { int i, j, p; fp_t out; uint8_t prod[FP_DIGITS * 2] = {0}; /* compute intermediary product */ for(i = FP_DIGITS - 1; i >= 0; --i) { for(j = FP_DIGITS - 1; j >= 0; --j) { /* compute digit product */ p = fp_getdigit(&a, i) * fp_getdigit(&b, j); /* skip if the product is zero */ if(!p) continue; /* add it to partial product sum */ prod[i + j + 1] += p % 10; prod[i + j] += p / 10; } } /* fix carry */ for(i = sizeof prod - 1; i > 0; --i) { while(prod[i] > 9) { prod[i] -= 10; ++prod[i - 1]; } } /* compute sign */ out.sgn = a.sgn ^ b.sgn; /* compute exponent */ i = prod[0] == 0; out.expt = a.expt + b.expt - 0x80 + !i; /* copy shifted fractional part */ for(j = 0; j < FP_DIGITS; ++j) fp_setdigit(&out, j, prod[i++]); return out; }
/* * This function takes two normalized floating-point values and adds them * together to produce a normalized result and store it in the parameter c. * If this "addition" is actually a subtraction, it internally calls fp_sub. * * NOTE: Eventually this function will be extended to return a result * indicating the success of this function to include overflow * notification, etc. */ void fp_add(fp_t *a, fp_t *b, fp_t *c) { int i; uint8_t tmp, carry; uint8_t offset[2]; /* Use the larger of the two exponents and compute the distance between * it and a->expt, b->expt. These offsets are used to simulate the adjustment * of a and b to have the same exponent without performing the actual * operation. This means that a and b don't have to be clobbered, duplicated * on the stack, etc. and generally saves having to iterate through the * fractional component of their data. */ c->expt = a->expt > b->expt ? a->expt : b->expt; offset[0] = c->expt - a->expt; offset[1] = c->expt - b->expt; /* Determine sign of result. */ switch((a->sgn << 1) | b->sgn) { /* c <- a + b = a + b */ case 0: c->sgn = 0; break; /* c <- a + (-b) = a - b */ case 1: fp_sub(a, b, c, offset); return; /* c <- (-a) + b = b - a */ case 2: tmp = offset[0]; offset[0] = offset[1]; offset[1] = tmp; fp_sub(b, a, c, offset); return; /* c <- (-a) + (-b) = -(a + b) */ case 3: c->sgn = 1; break; } carry = 0; for(i = (sizeof a->data * 2) - 1; i >= 0; --i) { /* Compute digit sum using the simulated shifting. */ tmp = fp_getdigit(a, i - offset[0]) + fp_getdigit(b, i - offset[1]) + carry; /* Compute new carry and optionally adjust the sum digit. */ carry = tmp > 9; if(carry) tmp -= 10; /* Update c with result. */ fp_setdigit(c, i, tmp); } /* If we still have carry, then this means that we are done performing * normalization after done shifting once. Then set the topmost digit to * 1, the carry value. */ if(carry) { fp_rshift(c, 1); fp_setdigit(c, 0, 1); } /* Shift to the left until we have a leading nonzero digit. The value * will then be normalized. Make sure shifting does not continue on * forever due to a sum of zero. */ else { for(i = 0; i < (int)(sizeof a->data * 2); ++i) { if(a->data[0] >> 4) break; fp_lshift(c, 1); } } }