📄 qmath.c
字号:
/* * qmath - extended precision rational arithmetic primitive routines * * Copyright (C) 1999-2004 David I. Bell and Ernest Bowen * * Primary author: David I. Bell * * Calc is open software; you can redistribute it and/or modify it under * the terms of the version 2.1 of the GNU Lesser General Public License * as published by the Free Software Foundation. * * Calc is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General * Public License for more details. * * A copy of version 2.1 of the GNU Lesser General Public License is * distributed with calc under the filename COPYING-LGPL. You should have * received a copy with calc; if not, write to Free Software Foundation, Inc. * 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA. * * @(#) $Revision: 29.7 $ * @(#) $Id: qmath.c,v 29.7 2006/12/15 16:18:10 chongo Exp $ * @(#) $Source: /usr/local/src/cmd/calc/RCS/qmath.c,v $ * * Under source code control: 1990/02/15 01:48:21 * File existed as early as: before 1990 * * Share and enjoy! :-) http://www.isthe.com/chongo/tech/comp/calc/ */#include <stdio.h>#include "qmath.h"#include "config.h"NUMBER _qzero_ = { { _zeroval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL };NUMBER _qone_ = { { _oneval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL };NUMBER _qtwo_ = { { _twoval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL };NUMBER _qthree_ = { { _threeval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL };NUMBER _qfour_ = { { _fourval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL };NUMBER _qten_ = { { _tenval_, 1, 0 }, { _oneval_, 1, 0 }, 1, NULL };NUMBER _qnegone_ = { { _oneval_, 1, 1 }, { _oneval_, 1, 0 }, 1, NULL };NUMBER _qonehalf_ = { { _oneval_, 1, 0 }, { _twoval_, 1, 0 }, 1, NULL };NUMBER _qneghalf_ = { { _oneval_, 1, 1 }, { _twoval_, 1, 0 }, 1, NULL };NUMBER _qonesqbase_ = { { _oneval_, 1, 0 }, { _sqbaseval_, 2, 0 }, 1, NULL };NUMBER * initnumbs[INITCONSTCOUNT] = {&_qzero_, &_qone_, &_qtwo_, &_qthree_, &_qfour_, &_qten_, &_qnegone_, &_qonehalf_, &_qneghalf_};/* * Create another copy of a number. * q2 = qcopy(q1); */NUMBER *qcopy(NUMBER *q){ register NUMBER *r; r = qalloc(); r->num.sign = q->num.sign; if (!zisunit(q->num)) { r->num.len = q->num.len; r->num.v = alloc(r->num.len); zcopyval(q->num, r->num); } if (!zisunit(q->den)) { r->den.len = q->den.len; r->den.v = alloc(r->den.len); zcopyval(q->den, r->den); } return r;}/* * Convert a number to a normal integer. * i = qtoi(q); */longqtoi(NUMBER *q){ long i; ZVALUE res; if (qisint(q)) return ztoi(q->num); zquo(q->num, q->den, &res, 0); i = ztoi(res); zfree(res); return i;}/* * Convert a normal integer into a number. * q = itoq(i); */NUMBER *itoq(long i){ register NUMBER *q; if ((i >= -1) && (i <= 10)) { switch ((int) i) { case 0: q = &_qzero_; break; case 1: q = &_qone_; break; case 2: q = &_qtwo_; break; case 10: q = &_qten_; break; case -1: q = &_qnegone_; break; default: q = NULL; } if (q) return qlink(q); } q = qalloc(); itoz(i, &q->num); return q;}/* * Convert a number to a normal unsigned integer. * u = qtou(q); */FULLqtou(NUMBER *q){ FULL i; ZVALUE res; if (qisint(q)) return ztou(q->num); zquo(q->num, q->den, &res, 0); i = ztou(res); zfree(res); return i;}/* * Convert a number to a normal signed integer. * s = qtos(q); */SFULLqtos(NUMBER *q){ SFULL i; ZVALUE res; if (qisint(q)) return ztos(q->num); zquo(q->num, q->den, &res, 0); i = ztos(res); zfree(res); return i;}/* * Convert a normal unsigned integer into a number. * q = utoq(i); */NUMBER *utoq(FULL i){ register NUMBER *q; if (i <= 10) { switch ((int) i) { case 0: q = &_qzero_; break; case 1: q = &_qone_; break; case 2: q = &_qtwo_; break; case 10: q = &_qten_; break; default: q = NULL; } if (q) return qlink(q); } q = qalloc(); utoz(i, &q->num); return q;}/* * Convert a normal signed integer into a number. * q = stoq(s); */NUMBER *stoq(SFULL i){ register NUMBER *q; if (i <= 10) { switch ((int) i) { case 0: q = &_qzero_; break; case 1: q = &_qone_; break; case 2: q = &_qtwo_; break; case 10: q = &_qten_; break; default: q = NULL; } if (q) return qlink(q); } q = qalloc(); stoz(i, &q->num); return q;}/* * Create a number from the given FULL numerator and denominator. * q = uutoq(inum, iden); */NUMBER *uutoq(FULL inum, FULL iden){ register NUMBER *q; FULL d; BOOL sign; if (iden == 0) { math_error("Division by zero"); /*NOTREACHED*/ } if (inum == 0) return qlink(&_qzero_); sign = 0; d = uugcd(inum, iden); inum /= d; iden /= d; if (iden == 1) return utoq(inum); q = qalloc(); if (inum != 1) utoz(inum, &q->num); utoz(iden, &q->den); q->num.sign = sign; return q;}/* * Create a number from the given integral numerator and denominator. * q = iitoq(inum, iden); */NUMBER *iitoq(long inum, long iden){ register NUMBER *q; long d; BOOL sign; if (iden == 0) { math_error("Division by zero"); /*NOTREACHED*/ } if (inum == 0) return qlink(&_qzero_); sign = 0; if (inum < 0) { sign = 1; inum = -inum; } if (iden < 0) { sign = 1 - sign; iden = -iden; } d = iigcd(inum, iden); inum /= d; iden /= d; if (iden == 1) return itoq(sign ? -inum : inum); q = qalloc(); if (inum != 1) itoz(inum, &q->num); itoz(iden, &q->den); q->num.sign = sign; return q;}/* * Add two numbers to each other. * q3 = qqadd(q1, q2); */NUMBER *qqadd(NUMBER *q1, NUMBER *q2){ NUMBER *r; ZVALUE t1, t2, temp, d1, d2, vpd1, upd1; if (qiszero(q1)) return qlink(q2); if (qiszero(q2)) return qlink(q1); r = qalloc(); /* * If either number is an integer, then the result is easy. */ if (qisint(q1) && qisint(q2)) { zadd(q1->num, q2->num, &r->num); return r; } if (qisint(q2)) { zmul(q1->den, q2->num, &temp); zadd(q1->num, temp, &r->num); zfree(temp); zcopy(q1->den, &r->den); return r; } if (qisint(q1)) { zmul(q2->den, q1->num, &temp); zadd(q2->num, temp, &r->num); zfree(temp); zcopy(q2->den, &r->den); return r; } /* * Both arguments are true fractions, so we need more work. * If the denominators are relatively prime, then the answer is the * straightforward cross product result with no need for reduction. */ zgcd(q1->den, q2->den, &d1); if (zisunit(d1)) { zfree(d1); zmul(q1->num, q2->den, &t1); zmul(q1->den, q2->num, &t2); zadd(t1, t2, &r->num); zfree(t1); zfree(t2); zmul(q1->den, q2->den, &r->den); return r; } /* * The calculation is now more complicated. * See Knuth Vol 2 for details. */ zquo(q2->den, d1, &vpd1, 0); zquo(q1->den, d1, &upd1, 0); zmul(q1->num, vpd1, &t1); zmul(q2->num, upd1, &t2); zadd(t1, t2, &temp); zfree(t1); zfree(t2); zfree(vpd1); zgcd(temp, d1, &d2); zfree(d1); if (zisunit(d2)) { zfree(d2); r->num = temp; zmul(upd1, q2->den, &r->den); zfree(upd1); return r; } zquo(temp, d2, &r->num, 0); zfree(temp); zquo(q2->den, d2, &temp, 0); zfree(d2); zmul(temp, upd1, &r->den); zfree(temp); zfree(upd1); return r;}/* * Subtract one number from another. * q3 = qsub(q1, q2); */NUMBER *qsub(NUMBER *q1, NUMBER *q2){ NUMBER *r; if (q1 == q2) return qlink(&_qzero_); if (qiszero(q2)) return qlink(q1); if (qisint(q1) && qisint(q2)) { r = qalloc(); zsub(q1->num, q2->num, &r->num); return r; } q2 = qneg(q2); if (qiszero(q1)) return q2; r = qqadd(q1, q2); qfree(q2); return r;}/* * Increment a number by one. */NUMBER *qinc(NUMBER *q){ NUMBER *r; r = qalloc(); if (qisint(q)) { zadd(q->num, _one_, &r->num); return r; } zadd(q->num, q->den, &r->num); zcopy(q->den, &r->den); return r;}/* * Decrement a number by one. */NUMBER *qdec(NUMBER *q){ NUMBER *r; r = qalloc(); if (qisint(q)) { zsub(q->num, _one_, &r->num); return r; } zsub(q->num, q->den, &r->num); zcopy(q->den, &r->den); return r;}/* * Add a normal small integer value to an arbitrary number. */NUMBER *qaddi(NUMBER *q1, long n){ NUMBER addnum; /* temporary number */ HALF addval[2]; /* value of small number */ BOOL neg; /* TRUE if number is neg */#if LONG_BITS > BASEB FULL nf;#endif if (n == 0) return qlink(q1); if (n == 1) return qinc(q1); if (n == -1) return qdec(q1); if (qiszero(q1)) return itoq(n); addnum.num.sign = 0; addnum.num.v = addval; addnum.den = _one_; neg = (n < 0); if (neg) n = -n; addval[0] = (HALF) n;#if LONG_BITS > BASEB nf = (((FULL) n) >> BASEB); if (nf) { addval[1] = (HALF) nf; addnum.num.len = 2; }#else addnum.num.len = 1;#endif if (neg) return qsub(q1, &addnum); else return qqadd(q1, &addnum);}/* * Multiply two numbers. * q3 = qmul(q1, q2); */NUMBER *qmul(NUMBER *q1, NUMBER *q2){ NUMBER *r; /* returned value */ ZVALUE n1, n2, d1, d2; /* numerators and denominators */ ZVALUE tmp; if (qiszero(q1) || qiszero(q2)) return qlink(&_qzero_); if (qisone(q1)) return qlink(q2); if (qisone(q2)) return qlink(q1); if (qisint(q1) && qisint(q2)) { /* easy results if integers */ r = qalloc(); zmul(q1->num, q2->num, &r->num); return r; } n1 = q1->num; n2 = q2->num; d1 = q1->den; d2 = q2->den; if (ziszero(d1) || ziszero(d2)) { math_error("Division by zero"); /*NOTREACHED*/ } if (ziszero(n1) || ziszero(n2)) return qlink(&_qzero_); if (!zisunit(n1) && !zisunit(d2)) { /* possibly reduce */ zgcd(n1, d2, &tmp); if (!zisunit(tmp)) { zequo(q1->num, tmp, &n1); zequo(q2->den, tmp, &d2); } zfree(tmp); } if (!zisunit(n2) && !zisunit(d1)) { /* again possibly reduce */ zgcd(n2, d1, &tmp); if (!zisunit(tmp)) { zequo(q2->num, tmp, &n2); zequo(q1->den, tmp, &d1); } zfree(tmp); } r = qalloc(); zmul(n1, n2, &r->num); zmul(d1, d2, &r->den); if (q1->num.v != n1.v) zfree(n1); if (q1->den.v != d1.v) zfree(d1); if (q2->num.v != n2.v) zfree(n2); if (q2->den.v != d2.v) zfree(d2); return r;}/* * Multiply a number by a small integer. * q2 = qmuli(q1, n); */NUMBER *qmuli(NUMBER *q, long n){ NUMBER *r; long d; /* gcd of multiplier and denominator */ int sign; if ((n == 0) || qiszero(q)) return qlink(&_qzero_); if (n == 1) return qlink(q); r = qalloc(); if (qisint(q)) { zmuli(q->num, n, &r->num); return r; } sign = 1; if (n < 0) { n = -n; sign = -1; } d = zmodi(q->den, n); d = iigcd(d, n); zmuli(q->num, (n * sign) / d, &r->num); (void) zdivi(q->den, d, &r->den); return r;}/* * Divide two numbers (as fractions). * q3 = qqdiv(q1, q2); */NUMBER *qqdiv(NUMBER *q1, NUMBER *q2){ NUMBER temp; if (qiszero(q2)) { math_error("Division by zero"); /*NOTREACHED*/ } if ((q1 == q2) || !qcmp(q1, q2)) return qlink(&_qone_); if (qisone(q1)) return qinv(q2); temp.num = q2->den; temp.den = q2->num; temp.num.sign = temp.den.sign; temp.den.sign = 0; temp.links = 1; return qmul(q1, &temp);}/* * Divide a number by a small integer. * q2 = qdivi(q1, n); */NUMBER *qdivi(NUMBER *q, long n){ NUMBER *r; long d; /* gcd of divisor and numerator */ int sign; if (n == 0) { math_error("Division by zero"); /*NOTREACHED*/ } if ((n == 1) || qiszero(q)) return qlink(q); sign = 1; if (n < 0) { n = -n; sign = -1; } r = qalloc(); d = zmodi(q->num, n); d = iigcd(d, n); (void) zdivi(q->num, d * sign, &r->num); zmuli(q->den, n / d, &r->den); return r;}/* * Return the integer quotient of a pair of numbers * If q1/q2 is an integer qquo(q1, q2) returns this integer * If q2 is zero, zero is returned * In other cases whether rounding is down, up, towards zero, etc. * is determined by rnd. */NUMBER *qquo(NUMBER *q1, NUMBER *q2, long rnd){ ZVALUE tmp, tmp1, tmp2; NUMBER *q; if (qiszero(q1) || qiszero(q2)) return qlink(&_qzero_); if (qisint(q1) && qisint(q2)) { zquo(q1->num, q2->num, &tmp, rnd); } else { zmul(q1->num, q2->den, &tmp1); zmul(q2->num, q1->den, &tmp2); zquo(tmp1, tmp2, &tmp, rnd); zfree(tmp1); zfree(tmp2); } if (ziszero(tmp)) { zfree(tmp); return qlink(&_qzero_); } q = qalloc(); q->num = tmp; return q;}/* * Return the absolute value of a number. * q2 = qqabs(q1); */NUMBER *qqabs(NUMBER *q){ register NUMBER *r; if (q->num.sign == 0) return qlink(q); r = qalloc(); if (!zisunit(q->num)) zcopy(q->num, &r->num); if (!zisunit(q->den)) zcopy(q->den, &r->den); r->num.sign = 0; return r;}/* * Negate a number. * q2 = qneg(q1); */NUMBER *qneg(NUMBER *q){ register NUMBER *r; if (qiszero(q)) return qlink(&_qzero_); r = qalloc(); if (!zisunit(q->num)) zcopy(q->num, &r->num); if (!zisunit(q->den)) zcopy(q->den, &r->den); r->num.sign = !q->num.sign; return r;}/* * Return the sign of a number (-1, 0 or 1) */NUMBER *qsign(NUMBER *q){ if (qiszero(q)) return qlink(&_qzero_); if (qisneg(q)) return qlink(&_qnegone_); return qlink(&_qone_);}/* * Invert a number. * q2 = qinv(q1); */NUMBER *qinv(NUMBER *q){ register NUMBER *r; if (qisunit(q)) { r = (qisneg(q) ? &_qnegone_ : &_qone_); return qlink(r); } if (qiszero(q)) { math_error("Division by zero"); /*NOTREACHED*/ } r = qalloc(); if (!zisunit(q->num)) zcopy(q->num, &r->den); if (!zisunit(q->den)) zcopy(q->den, &r->num); r->num.sign = q->num.sign; r->den.sign = 0; return r;}/* * Return just the numerator of a number. * q2 = qnum(q1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -