int fs_decimal_multiply(const fs_decimal *a, const fs_decimal *b, fs_decimal *r) { fs_decimal sum = zero_val; if (a->flags & FS_D_OVERFLOW || b->flags & FS_D_OVERFLOW) { fs_decimal_init(r); r->flags = FS_D_OVERFLOW; return 1; } for (int i=0; i < FS_D_DIGITS; i++) { if (b->digit[FS_D_DIGITS-i-FS_D_OVER_DIGITS]) { fs_decimal tmp = zero_val; mul_internal(a, b->digit[FS_D_DIGITS-i-FS_D_OVER_DIGITS], i, &tmp); if (tmp.digit[0] || tmp.digit[1]) { sum.flags |= FS_D_OVERFLOW; } fs_decimal_add(&sum, &tmp, &sum); } } sum.flags |= (a->flags & FS_D_NEGATIVE) ^ (b->flags & FS_D_NEGATIVE); *r = sum; return 0; }
int fs_decimal_divide(const fs_decimal *n, const fs_decimal *d, fs_decimal *q) { fs_decimal norm; int shift = 0; /* catch divide by zero error */ if (fs_decimal_is_zero(d)) { return 1; } /* use Newton-Raphson series approximation to calculate 1/d */ fs_decimal_normalise(d, &norm, &shift); fs_decimal x; if (norm.digit[FS_D_OVER_DIGITS + FS_D_INT_DIGITS] >= 5) { /* for 0.5 < norm < 1.0 we can use x = 2.914 - 2d as starting pt */ fs_decimal twod; fs_decimal_multiply(&d2_val, &norm, &twod); fs_decimal_subtract(&d2_914_val, &twod, &x); } else { /* otherwise, don't know where to start, use 1.0 */ x = d1_val; } fs_decimal last = zero_val; /* if it hasn't converged after 30 iterations it usually doesn't */ for (int i=0; i<30; i++) { #if 0 printf("step %2d = ", i); fs_decimal_print(&x, stdout); printf("\n"); #endif /* calculate x = x(2-dx) */ fs_decimal dx, tmp; fs_decimal_multiply(&norm, &x, &dx); fs_decimal_subtract(&d2_val, &dx, &tmp); fs_decimal_multiply(&tmp, &x, &x); if (fs_decimal_equal(&x, &last)) break; last = x; } /* round up to nearest representable number */ fs_decimal_add(&x, &unit_val, &x); #if 0 printf("step N = "); fs_decimal_print(&x, stdout); printf("\n"); #endif /* shift the aproximate reciprocal back to correct power */ decimal_shift(&x, &x, shift); /* q = n * 1/d */ fs_decimal_multiply(n, &x, q); q->flags ^= (d->flags & FS_D_NEGATIVE); return 0; }
int fs_decimal_subtract(const fs_decimal *a, const fs_decimal *b, fs_decimal *r) { fs_decimal intl; fs_decimal_negate(b, &intl); return fs_decimal_add(a, &intl, r); }
fs_value fn_numeric_add(fs_query *q, fs_value a, fs_value b) { #if 0 fs_value_print(a); printf(" + "); fs_value_print(b); printf("\n"); #endif a = fs_value_promote(q, a, b); b = fs_value_promote(q, b, a); #if 0 fs_value_print(a); printf(" P+ "); fs_value_print(b); printf("\n"); #endif if (a.attr == b.attr && a.attr != FS_RID_NULL && a.attr != fs_c.empty) { fs_value v = fs_value_blank(); v.attr = a.attr; if (a.attr == fs_c.xsd_double || a.attr == fs_c.xsd_float) { v.fp = a.fp + b.fp; v.valid = fs_valid_bit(FS_V_FP); return v; } else if (a.attr == fs_c.xsd_decimal) { fs_decimal_add(&a.de, &b.de, &v.de); v.valid = fs_valid_bit(FS_V_DE); return v; } else if (a.attr == fs_c.xsd_integer) { v.in = a.in + b.in; v.valid = fs_valid_bit(FS_V_IN); return v; } } return fs_value_error(FS_ERROR_INVALID_TYPE, "non-numeric arguments to fn:add"); }