/*******************************************************************1** m a t h 6 4 . c2** Forth Inspired Command Language - 64 bit math support routines3** Author: John Sadler ([email protected])4** Created: 25 January 19985** Rev 2.03: Support for 128 bit DP math. This file really ouught to6** be renamed!7** $Id: math64.c,v 1.9 2001/12/05 07:21:34 jsadler Exp $8*******************************************************************/9/*10** Copyright (c) 1997-2001 John Sadler ([email protected])11** All rights reserved.12**13** Get the latest Ficl release at http://ficl.sourceforge.net14**15** I am interested in hearing from anyone who uses ficl. If you have16** a problem, a success story, a defect, an enhancement request, or17** if you would like to contribute to the ficl release, please18** contact me by email at the address above.19**20** L I C E N S E and D I S C L A I M E R21**22** Redistribution and use in source and binary forms, with or without23** modification, are permitted provided that the following conditions24** are met:25** 1. Redistributions of source code must retain the above copyright26** notice, this list of conditions and the following disclaimer.27** 2. Redistributions in binary form must reproduce the above copyright28** notice, this list of conditions and the following disclaimer in the29** documentation and/or other materials provided with the distribution.30**31** THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND32** ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE33** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE34** ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE35** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL36** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS37** OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)38** HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT39** LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY40** OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF41** SUCH DAMAGE.42*/434445#include "ficl.h"46#include "math64.h"474849/**************************************************************************50m 6 4 A b s51** Returns the absolute value of an DPINT52**************************************************************************/53DPINT m64Abs(DPINT x)54{55if (m64IsNegative(x))56x = m64Negate(x);5758return x;59}606162/**************************************************************************63m 6 4 F l o o r e d D i v I64**65** FROM THE FORTH ANS...66** Floored division is integer division in which the remainder carries67** the sign of the divisor or is zero, and the quotient is rounded to68** its arithmetic floor. Symmetric division is integer division in which69** the remainder carries the sign of the dividend or is zero and the70** quotient is the mathematical quotient rounded towards zero or71** truncated. Examples of each are shown in tables 3.3 and 3.4.72**73** Table 3.3 - Floored Division Example74** Dividend Divisor Remainder Quotient75** -------- ------- --------- --------76** 10 7 3 177** -10 7 4 -278** 10 -7 -4 -279** -10 -7 -3 180**81**82** Table 3.4 - Symmetric Division Example83** Dividend Divisor Remainder Quotient84** -------- ------- --------- --------85** 10 7 3 186** -10 7 -3 -187** 10 -7 3 -188** -10 -7 -3 189**************************************************************************/90INTQR m64FlooredDivI(DPINT num, FICL_INT den)91{92INTQR qr;93UNSQR uqr;94int signRem = 1;95int signQuot = 1;9697if (m64IsNegative(num))98{99num = m64Negate(num);100signQuot = -signQuot;101}102103if (den < 0)104{105den = -den;106signRem = -signRem;107signQuot = -signQuot;108}109110uqr = ficlLongDiv(m64CastIU(num), (FICL_UNS)den);111qr = m64CastQRUI(uqr);112if (signQuot < 0)113{114qr.quot = -qr.quot;115if (qr.rem != 0)116{117qr.quot--;118qr.rem = den - qr.rem;119}120}121122if (signRem < 0)123qr.rem = -qr.rem;124125return qr;126}127128129/**************************************************************************130m 6 4 I s N e g a t i v e131** Returns TRUE if the specified DPINT has its sign bit set.132**************************************************************************/133int m64IsNegative(DPINT x)134{135return (x.hi < 0);136}137138139/**************************************************************************140m 6 4 M a c141** Mixed precision multiply and accumulate primitive for number building.142** Multiplies DPUNS u by FICL_UNS mul and adds FICL_UNS add. Mul is typically143** the numeric base, and add represents a digit to be appended to the144** growing number.145** Returns the result of the operation146**************************************************************************/147DPUNS m64Mac(DPUNS u, FICL_UNS mul, FICL_UNS add)148{149DPUNS resultLo = ficlLongMul(u.lo, mul);150DPUNS resultHi = ficlLongMul(u.hi, mul);151resultLo.hi += resultHi.lo;152resultHi.lo = resultLo.lo + add;153154if (resultHi.lo < resultLo.lo)155resultLo.hi++;156157resultLo.lo = resultHi.lo;158159return resultLo;160}161162163/**************************************************************************164m 6 4 M u l I165** Multiplies a pair of FICL_INTs and returns an DPINT result.166**************************************************************************/167DPINT m64MulI(FICL_INT x, FICL_INT y)168{169DPUNS prod;170int sign = 1;171172if (x < 0)173{174sign = -sign;175x = -x;176}177178if (y < 0)179{180sign = -sign;181y = -y;182}183184prod = ficlLongMul(x, y);185if (sign > 0)186return m64CastUI(prod);187else188return m64Negate(m64CastUI(prod));189}190191192/**************************************************************************193m 6 4 N e g a t e194** Negates an DPINT by complementing and incrementing.195**************************************************************************/196DPINT m64Negate(DPINT x)197{198x.hi = ~x.hi;199x.lo = ~x.lo;200x.lo ++;201if (x.lo == 0)202x.hi++;203204return x;205}206207208/**************************************************************************209m 6 4 P u s h210** Push an DPINT onto the specified stack in the order required211** by ANS Forth (most significant cell on top)212** These should probably be macros...213**************************************************************************/214void i64Push(FICL_STACK *pStack, DPINT i64)215{216stackPushINT(pStack, i64.lo);217stackPushINT(pStack, i64.hi);218return;219}220221void u64Push(FICL_STACK *pStack, DPUNS u64)222{223stackPushINT(pStack, u64.lo);224stackPushINT(pStack, u64.hi);225return;226}227228229/**************************************************************************230m 6 4 P o p231** Pops an DPINT off the stack in the order required by ANS Forth232** (most significant cell on top)233** These should probably be macros...234**************************************************************************/235DPINT i64Pop(FICL_STACK *pStack)236{237DPINT ret;238ret.hi = stackPopINT(pStack);239ret.lo = stackPopINT(pStack);240return ret;241}242243DPUNS u64Pop(FICL_STACK *pStack)244{245DPUNS ret;246ret.hi = stackPopINT(pStack);247ret.lo = stackPopINT(pStack);248return ret;249}250251252/**************************************************************************253m 6 4 S y m m e t r i c D i v254** Divide an DPINT by a FICL_INT and return a FICL_INT quotient and a255** FICL_INT remainder. The absolute values of quotient and remainder are not256** affected by the signs of the numerator and denominator (the operation257** is symmetric on the number line)258**************************************************************************/259INTQR m64SymmetricDivI(DPINT num, FICL_INT den)260{261INTQR qr;262UNSQR uqr;263int signRem = 1;264int signQuot = 1;265266if (m64IsNegative(num))267{268num = m64Negate(num);269signRem = -signRem;270signQuot = -signQuot;271}272273if (den < 0)274{275den = -den;276signQuot = -signQuot;277}278279uqr = ficlLongDiv(m64CastIU(num), (FICL_UNS)den);280qr = m64CastQRUI(uqr);281if (signRem < 0)282qr.rem = -qr.rem;283284if (signQuot < 0)285qr.quot = -qr.quot;286287return qr;288}289290291/**************************************************************************292m 6 4 U M o d293** Divides a DPUNS by base (an UNS16) and returns an UNS16 remainder.294** Writes the quotient back to the original DPUNS as a side effect.295** This operation is typically used to convert an DPUNS to a text string296** in any base. See words.c:numberSignS, for example.297** Mechanics: performs 4 ficlLongDivs, each of which produces 16 bits298** of the quotient. C does not provide a way to divide an FICL_UNS by an299** UNS16 and get an FICL_UNS quotient (ldiv is closest, but it's signed,300** unfortunately), so I've used ficlLongDiv.301**************************************************************************/302#if (BITS_PER_CELL == 32)303304#define UMOD_SHIFT 16305#define UMOD_MASK 0x0000ffff306307#elif (BITS_PER_CELL == 64)308309#define UMOD_SHIFT 32310#define UMOD_MASK 0x00000000ffffffff311312#endif313314UNS16 m64UMod(DPUNS *pUD, UNS16 base)315{316DPUNS ud;317UNSQR qr;318DPUNS result;319320result.hi = result.lo = 0;321322ud.hi = 0;323ud.lo = pUD->hi >> UMOD_SHIFT;324qr = ficlLongDiv(ud, (FICL_UNS)base);325result.hi = qr.quot << UMOD_SHIFT;326327ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->hi & UMOD_MASK);328qr = ficlLongDiv(ud, (FICL_UNS)base);329result.hi |= qr.quot & UMOD_MASK;330331ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->lo >> UMOD_SHIFT);332qr = ficlLongDiv(ud, (FICL_UNS)base);333result.lo = qr.quot << UMOD_SHIFT;334335ud.lo = (qr.rem << UMOD_SHIFT) | (pUD->lo & UMOD_MASK);336qr = ficlLongDiv(ud, (FICL_UNS)base);337result.lo |= qr.quot & UMOD_MASK;338339*pUD = result;340341return (UNS16)(qr.rem);342}343344345/**************************************************************************346** Contributed by347** Michael A. Gauland [email protected]348**************************************************************************/349#if PORTABLE_LONGMULDIV != 0350/**************************************************************************351m 6 4 A d d352**353**************************************************************************/354DPUNS m64Add(DPUNS x, DPUNS y)355{356DPUNS result;357int carry;358359result.hi = x.hi + y.hi;360result.lo = x.lo + y.lo;361362363carry = ((x.lo | y.lo) & CELL_HI_BIT) && !(result.lo & CELL_HI_BIT);364carry |= ((x.lo & y.lo) & CELL_HI_BIT);365366if (carry)367{368result.hi++;369}370371return result;372}373374375/**************************************************************************376m 6 4 S u b377**378**************************************************************************/379DPUNS m64Sub(DPUNS x, DPUNS y)380{381DPUNS result;382383result.hi = x.hi - y.hi;384result.lo = x.lo - y.lo;385386if (x.lo < y.lo)387{388result.hi--;389}390391return result;392}393394395/**************************************************************************396m 6 4 A S L397** 64 bit left shift398**************************************************************************/399DPUNS m64ASL( DPUNS x )400{401DPUNS result;402403result.hi = x.hi << 1;404if (x.lo & CELL_HI_BIT)405{406result.hi++;407}408409result.lo = x.lo << 1;410411return result;412}413414415/**************************************************************************416m 6 4 A S R417** 64 bit right shift (unsigned - no sign extend)418**************************************************************************/419DPUNS m64ASR( DPUNS x )420{421DPUNS result;422423result.lo = x.lo >> 1;424if (x.hi & 1)425{426result.lo |= CELL_HI_BIT;427}428429result.hi = x.hi >> 1;430return result;431}432433434/**************************************************************************435m 6 4 O r436** 64 bit bitwise OR437**************************************************************************/438DPUNS m64Or( DPUNS x, DPUNS y )439{440DPUNS result;441442result.hi = x.hi | y.hi;443result.lo = x.lo | y.lo;444445return result;446}447448449/**************************************************************************450m 6 4 C o m p a r e451** Return -1 if x < y; 0 if x==y, and 1 if x > y.452**************************************************************************/453int m64Compare(DPUNS x, DPUNS y)454{455int result;456457if (x.hi > y.hi)458{459result = +1;460}461else if (x.hi < y.hi)462{463result = -1;464}465else466{467/* High parts are equal */468if (x.lo > y.lo)469{470result = +1;471}472else if (x.lo < y.lo)473{474result = -1;475}476else477{478result = 0;479}480}481482return result;483}484485486/**************************************************************************487f i c l L o n g M u l488** Portable versions of ficlLongMul and ficlLongDiv in C489** Contributed by:490** Michael A. Gauland [email protected]491**************************************************************************/492DPUNS ficlLongMul(FICL_UNS x, FICL_UNS y)493{494DPUNS result = { 0, 0 };495DPUNS addend;496497addend.lo = y;498addend.hi = 0; /* No sign extension--arguments are unsigned */499500while (x != 0)501{502if ( x & 1)503{504result = m64Add(result, addend);505}506x >>= 1;507addend = m64ASL(addend);508}509return result;510}511512513/**************************************************************************514f i c l L o n g D i v515** Portable versions of ficlLongMul and ficlLongDiv in C516** Contributed by:517** Michael A. Gauland [email protected]518**************************************************************************/519UNSQR ficlLongDiv(DPUNS q, FICL_UNS y)520{521UNSQR result;522DPUNS quotient;523DPUNS subtrahend;524DPUNS mask;525526quotient.lo = 0;527quotient.hi = 0;528529subtrahend.lo = y;530subtrahend.hi = 0;531532mask.lo = 1;533mask.hi = 0;534535while ((m64Compare(subtrahend, q) < 0) &&536(subtrahend.hi & CELL_HI_BIT) == 0)537{538mask = m64ASL(mask);539subtrahend = m64ASL(subtrahend);540}541542while (mask.lo != 0 || mask.hi != 0)543{544if (m64Compare(subtrahend, q) <= 0)545{546q = m64Sub( q, subtrahend);547quotient = m64Or(quotient, mask);548}549mask = m64ASR(mask);550subtrahend = m64ASR(subtrahend);551}552553result.quot = quotient.lo;554result.rem = q.lo;555return result;556}557558#endif559560561562