math64.c revision 40843
1/******************************************************************* 2** m a t h 6 4 . c 3** Forth Inspired Command Language - 64 bit math support routines 4** Author: John Sadler (john_sadler@alum.mit.edu) 5** Created: 25 January 1998 6** 7*******************************************************************/ 8 9#include "ficl.h" 10#include "math64.h" 11 12 13/************************************************************************** 14 m 6 4 A b s 15** Returns the absolute value of an INT64 16**************************************************************************/ 17INT64 m64Abs(INT64 x) 18{ 19 if (m64IsNegative(x)) 20 x = m64Negate(x); 21 22 return x; 23} 24 25 26/************************************************************************** 27 m 6 4 F l o o r e d D i v I 28** 29** FROM THE FORTH ANS... 30** Floored division is integer division in which the remainder carries 31** the sign of the divisor or is zero, and the quotient is rounded to 32** its arithmetic floor. Symmetric division is integer division in which 33** the remainder carries the sign of the dividend or is zero and the 34** quotient is the mathematical quotient rounded towards zero or 35** truncated. Examples of each are shown in tables 3.3 and 3.4. 36** 37** Table 3.3 - Floored Division Example 38** Dividend Divisor Remainder Quotient 39** -------- ------- --------- -------- 40** 10 7 3 1 41** -10 7 4 -2 42** 10 -7 -4 -2 43** -10 -7 -3 1 44** 45** 46** Table 3.4 - Symmetric Division Example 47** Dividend Divisor Remainder Quotient 48** -------- ------- --------- -------- 49** 10 7 3 1 50** -10 7 -3 -1 51** 10 -7 3 -1 52** -10 -7 -3 1 53**************************************************************************/ 54INTQR m64FlooredDivI(INT64 num, INT32 den) 55{ 56 INTQR qr; 57 UNSQR uqr; 58 int signRem = 1; 59 int signQuot = 1; 60 61 if (m64IsNegative(num)) 62 { 63 num = m64Negate(num); 64 signQuot = -signQuot; 65 } 66 67 if (den < 0) 68 { 69 den = -den; 70 signRem = -signRem; 71 signQuot = -signQuot; 72 } 73 74 uqr = ficlLongDiv(m64CastIU(num), (UNS32)den); 75 qr = m64CastQRUI(uqr); 76 if (signQuot < 0) 77 { 78 qr.quot = -qr.quot; 79 if (qr.rem != 0) 80 { 81 qr.quot--; 82 qr.rem = den - qr.rem; 83 } 84 } 85 86 if (signRem < 0) 87 qr.rem = -qr.rem; 88 89 return qr; 90} 91 92 93/************************************************************************** 94 m 6 4 I s N e g a t i v e 95** Returns TRUE if the specified INT64 has its sign bit set. 96**************************************************************************/ 97int m64IsNegative(INT64 x) 98{ 99 return (x.hi < 0); 100} 101 102 103/************************************************************************** 104 m 6 4 M a c 105** Mixed precision multiply and accumulate primitive for number building. 106** Multiplies UNS64 u by UNS32 mul and adds UNS32 add. Mul is typically 107** the numeric base, and add represents a digit to be appended to the 108** growing number. 109** Returns the result of the operation 110**************************************************************************/ 111UNS64 m64Mac(UNS64 u, UNS32 mul, UNS32 add) 112{ 113 UNS64 resultLo = ficlLongMul(u.lo, mul); 114 UNS64 resultHi = ficlLongMul(u.hi, mul); 115 resultLo.hi += resultHi.lo; 116 resultHi.lo = resultLo.lo + add; 117 118 if (resultHi.lo < resultLo.lo) 119 resultLo.hi++; 120 121 resultLo.lo = resultHi.lo; 122 123 return resultLo; 124} 125 126 127/************************************************************************** 128 m 6 4 M u l I 129** Multiplies a pair of INT32s and returns an INT64 result. 130**************************************************************************/ 131INT64 m64MulI(INT32 x, INT32 y) 132{ 133 UNS64 prod; 134 int sign = 1; 135 136 if (x < 0) 137 { 138 sign = -sign; 139 x = -x; 140 } 141 142 if (y < 0) 143 { 144 sign = -sign; 145 y = -y; 146 } 147 148 prod = ficlLongMul(x, y); 149 if (sign > 0) 150 return m64CastUI(prod); 151 else 152 return m64Negate(m64CastUI(prod)); 153} 154 155 156/************************************************************************** 157 m 6 4 N e g a t e 158** Negates an INT64 by complementing and incrementing. 159**************************************************************************/ 160INT64 m64Negate(INT64 x) 161{ 162 x.hi = ~x.hi; 163 x.lo = ~x.lo; 164 x.lo ++; 165 if (x.lo == 0) 166 x.hi++; 167 168 return x; 169} 170 171 172/************************************************************************** 173 m 6 4 P u s h 174** Push an INT64 onto the specified stack in the order required 175** by ANS Forth (most significant cell on top) 176** These should probably be macros... 177**************************************************************************/ 178void i64Push(FICL_STACK *pStack, INT64 i64) 179{ 180 stackPushINT32(pStack, i64.lo); 181 stackPushINT32(pStack, i64.hi); 182 return; 183} 184 185void u64Push(FICL_STACK *pStack, UNS64 u64) 186{ 187 stackPushINT32(pStack, u64.lo); 188 stackPushINT32(pStack, u64.hi); 189 return; 190} 191 192 193/************************************************************************** 194 m 6 4 P o p 195** Pops an INT64 off the stack in the order required by ANS Forth 196** (most significant cell on top) 197** These should probably be macros... 198**************************************************************************/ 199INT64 i64Pop(FICL_STACK *pStack) 200{ 201 INT64 ret; 202 ret.hi = stackPopINT32(pStack); 203 ret.lo = stackPopINT32(pStack); 204 return ret; 205} 206 207UNS64 u64Pop(FICL_STACK *pStack) 208{ 209 UNS64 ret; 210 ret.hi = stackPopINT32(pStack); 211 ret.lo = stackPopINT32(pStack); 212 return ret; 213} 214 215 216/************************************************************************** 217 m 6 4 S y m m e t r i c D i v 218** Divide an INT64 by an INT32 and return an INT32 quotient and an INT32 219** remainder. The absolute values of quotient and remainder are not 220** affected by the signs of the numerator and denominator (the operation 221** is symmetric on the number line) 222**************************************************************************/ 223INTQR m64SymmetricDivI(INT64 num, INT32 den) 224{ 225 INTQR qr; 226 UNSQR uqr; 227 int signRem = 1; 228 int signQuot = 1; 229 230 if (m64IsNegative(num)) 231 { 232 num = m64Negate(num); 233 signRem = -signRem; 234 signQuot = -signQuot; 235 } 236 237 if (den < 0) 238 { 239 den = -den; 240 signQuot = -signQuot; 241 } 242 243 uqr = ficlLongDiv(m64CastIU(num), (UNS32)den); 244 qr = m64CastQRUI(uqr); 245 if (signRem < 0) 246 qr.rem = -qr.rem; 247 248 if (signQuot < 0) 249 qr.quot = -qr.quot; 250 251 return qr; 252} 253 254 255/************************************************************************** 256 m 6 4 U M o d 257** Divides an UNS64 by base (an UNS16) and returns an UNS16 remainder. 258** Writes the quotient back to the original UNS64 as a side effect. 259** This operation is typically used to convert an UNS64 to a text string 260** in any base. See words.c:numberSignS, for example. 261** Mechanics: performs 4 ficlLongDivs, each of which produces 16 bits 262** of the quotient. C does not provide a way to divide an UNS32 by an 263** UNS16 and get an UNS32 quotient (ldiv is closest, but it's signed, 264** unfortunately), so I've used ficlLongDiv. 265**************************************************************************/ 266UNS16 m64UMod(UNS64 *pUD, UNS16 base) 267{ 268 UNS64 ud; 269 UNSQR qr; 270 UNS64 result; 271 272 result.hi = result.lo = 0; 273 274 ud.hi = 0; 275 ud.lo = pUD->hi >> 16; 276 qr = ficlLongDiv(ud, (UNS32)base); 277 result.hi = qr.quot << 16; 278 279 ud.lo = (qr.rem << 16) | (pUD->hi & 0x0000ffff); 280 qr = ficlLongDiv(ud, (UNS32)base); 281 result.hi |= qr.quot & 0x0000ffff; 282 283 ud.lo = (qr.rem << 16) | (pUD->lo >> 16); 284 qr = ficlLongDiv(ud, (UNS32)base); 285 result.lo = qr.quot << 16; 286 287 ud.lo = (qr.rem << 16) | (pUD->lo & 0x0000ffff); 288 qr = ficlLongDiv(ud, (UNS32)base); 289 result.lo |= qr.quot & 0x0000ffff; 290 291 *pUD = result; 292 293 return (UNS16)(qr.rem); 294} 295 296 297