![]() |
API 0.9.5
|
00001 /* 00002 * CPDecimal.j 00003 * Foundation 00004 * 00005 * Created by Stephen Paul Ierodiaconou 00006 * 00007 * This library is free software; you can redistribute it and/or 00008 * modify it under the terms of the GNU Lesser General Public 00009 * License as published by the Free Software Foundation; either 00010 * version 2.1 of the License, or (at your option) any later version. 00011 * 00012 * This library is distributed in the hope that it will be useful, 00013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00015 * Lesser General Public License for more details. 00016 * 00017 * You should have received a copy of the GNU Lesser General Public 00018 * License along with this library; if not, write to the Free Software 00019 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 00020 */ 00021 00022 /* 00023 Ported From GNUStep : 00024 00025 NSDecimal functions 00026 Copyright (C) 2000 Free Software Foundation, Inc. 00027 00028 Written by: Fred Kiefer <FredKiefer@gmx.de> 00029 Created: July 2000 00030 00031 This file is part of the GNUstep Base Library. 00032 00033 This library is free software; you can redistribute it and/or 00034 modify it under the terms of the GNU Lesser General Public 00035 License as published by the Free Software Foundation; either 00036 version 2 of the License, or (at your option) any later version. 00037 00038 This library is distributed in the hope that it will be useful, 00039 but WITHOUT ANY WARRANTY; without even the implied warranty of 00040 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00041 Library General Public License for more details. 00042 00043 You should have received a copy of the GNU Lesser General Public 00044 License along with this library; if not, write to the Free 00045 Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00046 Boston, MA 02111 USA. 00047 00048 <title>NSDecimal class reference</title> 00049 $Date: 2008-06-12 04:44:00 -0600 (Thu, 12 Jun 2008) $ $Revision: 26630 $ 00050 */ 00051 00052 00053 // Decimal size limits 00054 CPDecimalMaxDigits = 38; 00055 CPDecimalMaxExponent = 127; 00056 CPDecimalMinExponent = -128; 00057 00058 // Scale for no Rounding 00059 CPDecimalNoScale = 128 00060 00061 // CPCalculationError enum 00062 CPCalculationNoError = 0; 00063 CPCalculationLossOfPrecision = 1; 00064 CPCalculationOverflow = 2; 00065 CPCalculationUnderflow = 3; 00066 CPCalculationDivideByZero = 4; 00067 00068 //CPRoundingMode Enum 00069 CPRoundPlain = 1; 00070 CPRoundDown = 2; 00071 CPRoundUp = 3; 00072 CPRoundBankers = 4; 00073 _CPRoundHalfDown = 5; // Private API rounding mode used by CPNumberFormatter. 00074 00075 //Exceptions 00076 CPDecimalNumberOverflowException = @"CPDecimalNumberOverflowException"; 00077 CPDecimalNumberUnderflowException = @"CPDecimalNumberUnderflowException"; 00078 CPDecimalNumberExactnessException = @"CPDecimalNumberExactnessException"; 00079 CPDecimalNumberDivideByZeroException = @"CPDecimalNumberDivideByZeroException"; 00080 00081 /* 00082 Initialisers for NSDecimal do not exist so here I have created my own. 00083 The coefficient is called the 'mantissa' in this implementation as this is what Cocoa calls it. 00084 00085 CPDecimal format: 00086 ._mantissa : CPArray, containing each digit of the number as an unsigned integer. 00087 ._exponent : integer, the exponent of the number as an signed integer. 00088 ._isNegative : BOOL, sign of number 00089 ._isCompact : BOOL, has number been compacted. 00090 ._isNaN : BOOL, is NaN (Not a number) i.e. number is invalid. 00091 */ 00092 00100 // FIXME: locale support and Cocoaify, needs to accept .1 and leading 0s 00101 function CPDecimalMakeWithString(string, locale) 00102 { 00103 if (!string) 00104 return CPDecimalMakeNaN(); 00105 00106 // Regexp solution as found in JSON spec, with working regexp (I added groupings) 00107 // Test here: http://www.regexplanet.com/simple/index.html 00108 // Info from: http://stackoverflow.com/questions/638565/parsing-scientific-notation-sensibly 00109 // ([+\-]?)((?:0|[1-9]\d*)) - integer part, cant have leading zeros 00110 // (?:\.(\d*))? - optional decimal part plus number in group 00111 // (?:[eE]([+\-]?)(\d+))? - optional exponent part plus number in group 00112 // group 0: string, 1: sign, 2: integer, 3: decimal, 4: exponent sign, 5: exponent 00113 00114 // Note: this doesn't accept .01 for example, should it? 00115 // If yes simply add '?' after integer part group, i.e. ([+\-]?)((?:0|[1-9]\d*)?) 00116 var matches = string.match(/^([+\-]?)((?:0|[1-9]\d*))(?:\.(\d*))?(?:[eE]([+\-]?)(\d+))?$/); 00117 if (!matches) 00118 return CPDecimalMakeNaN(); 00119 00120 var ds = matches[1], 00121 intpart = matches[2], 00122 decpart = matches[3], 00123 es = matches[4], 00124 exp = matches[5]; 00125 00126 var isNegative = NO; 00127 if (ds && ds === "-") 00128 isNegative = YES; 00129 00130 var exponent = 0; 00131 if (exp) 00132 exponent = parseInt(exp) * ((es && es === "-")?-1:1); 00133 00134 if (decpart) // push decimal point to last digit, then let compact handle the zeros 00135 exponent -= decpart.length; 00136 00137 var inputlength = (intpart?intpart.length:0) + (decpart?decpart.length:0); 00138 if (inputlength > CPDecimalMaxDigits) 00139 { 00140 // input is too long, increase exponent and truncate 00141 exponent += inputlength - CPDecimalMaxDigits; 00142 } 00143 00144 if (exponent > CPDecimalMaxExponent || exponent < CPDecimalMinExponent) 00145 return CPDecimalMakeNaN(); 00146 00147 // Representation internally starts at most significant digit 00148 var m = [], 00149 i = 0; 00150 for (; i < (intpart?intpart.length:0); i++) 00151 { 00152 if (i >= CPDecimalMaxDigits) 00153 break; // truncate 00154 Array.prototype.push.call(m, parseInt(intpart.charAt(i))); 00155 } 00156 var j = 0; 00157 for (; j < (decpart?decpart.length:0); j++) 00158 { 00159 if ((i + j) >= CPDecimalMaxDigits) 00160 break; // truncate 00161 Array.prototype.push.call(m, parseInt(decpart.charAt(j))); 00162 } 00163 00164 var dcm = {_exponent:exponent, _isNegative:isNegative, _isCompact:NO, _isNaN:NO, _mantissa:m}; 00165 CPDecimalCompact(dcm); 00166 00167 return dcm; 00168 } 00169 00177 function CPDecimalMakeWithParts(mantissa, exponent) 00178 { 00179 var m = [], 00180 isNegative = NO; 00181 00182 if (mantissa < 0 ) 00183 { 00184 isNegative = YES; 00185 mantissa = ABS(mantissa); 00186 } 00187 00188 if (mantissa == 0) 00189 Array.prototype.push.call(m, 0); 00190 00191 if (exponent > CPDecimalMaxExponent || exponent < CPDecimalMinExponent) 00192 return CPDecimalMakeNaN(); 00193 00194 // remaining digits are disposed of via truncation 00195 while ((mantissa > 0) && (m.length < CPDecimalMaxDigits)) // count selector here could be optimised away 00196 { 00197 Array.prototype.unshift.call(m, parseInt(mantissa % 10)); 00198 mantissa = FLOOR(mantissa / 10); 00199 } 00200 00201 var dcm = {_exponent:exponent, _isNegative:isNegative, _isCompact:YES, _isNaN:NO, _mantissa:m}; 00202 00203 CPDecimalCompact(dcm); 00204 00205 return dcm; 00206 } 00207 00213 function CPDecimalMakeZero() 00214 { 00215 return CPDecimalMakeWithParts(0, 0); 00216 } 00217 00223 function CPDecimalMakeOne() 00224 { 00225 return CPDecimalMakeWithParts(1, 0); 00226 } 00227 00228 00234 function CPDecimalMakeNaN() 00235 { 00236 var d = CPDecimalMakeWithParts(0, 0); 00237 d._isNaN = YES; 00238 return d; 00239 } 00240 00241 // private methods 00242 function _CPDecimalMakeMaximum() 00243 { 00244 var s = @"", 00245 i = 0; 00246 for (; i < CPDecimalMaxDigits; i++) 00247 s += "9"; 00248 s += "e" + CPDecimalMaxExponent; 00249 return CPDecimalMakeWithString(s); 00250 } 00251 00252 function _CPDecimalMakeMinimum() 00253 { 00254 var s = @"-", 00255 i = 0; 00256 for (; i < CPDecimalMaxDigits; i++) 00257 s += "9"; 00258 s += "e" + CPDecimalMaxExponent; 00259 return CPDecimalMakeWithString(s); 00260 } 00261 00267 function CPDecimalIsZero(dcm) 00268 { 00269 // exponent doesn't matter as long as mantissa = 0 00270 if (!dcm._isNaN) 00271 { 00272 for (var i = 0; i < dcm._mantissa.length; i++) 00273 if (dcm._mantissa[i] !== 0) 00274 return NO; 00275 00276 return YES; 00277 } 00278 00279 return NO; 00280 } 00281 00287 function CPDecimalIsOne(dcm) 00288 { 00289 CPDecimalCompact(dcm); 00290 // exponent doesn't matter as long as mantissa = 0 00291 if (!dcm._isNaN) 00292 { 00293 if (dcm._mantissa && (dcm._mantissa.length == 1) && (dcm._mantissa[0] == 1)) 00294 return YES; 00295 } 00296 return NO; 00297 } 00298 00299 //private method to copy attribute values 00300 function _CPDecimalSet(t,s) 00301 { 00302 // should all be [x copy] ? 00303 t._exponent = s._exponent; 00304 t._isNegative = s._isNegative; 00305 t._isCompact = s._isCompact; 00306 t._isNaN = s._isNaN; 00307 t._mantissa = Array.prototype.slice.call(s._mantissa, 0); 00308 } 00309 00310 function _CPDecimalSetZero(result) 00311 { 00312 result._mantissa = [0]; 00313 result._exponent = 0; 00314 result._isNegative = NO; 00315 result._isCompact = YES; 00316 result._isNaN = NO; 00317 } 00318 00319 function _CPDecimalSetOne(result) 00320 { 00321 result._mantissa = [1]; 00322 result._exponent = 0; 00323 result._isNegative = NO; 00324 result._isCompact = YES; 00325 result._isNaN = NO; 00326 } 00327 00333 function CPDecimalIsNotANumber(dcm) 00334 { 00335 return (dcm._isNaN)?YES:NO; 00336 } 00337 00344 function CPDecimalCopy(dcm) 00345 { 00346 return {_exponent:dcm._exponent, 00347 _isNegative:dcm._isNegative, 00348 _isCompact:dcm._isCompact, 00349 _isNaN:dcm._isNaN, 00350 _mantissa:Array.prototype.slice.call(dcm._mantissa, 0) 00351 }; 00352 } 00353 00362 function CPDecimalCompare(leftOperand, rightOperand) 00363 { 00364 if (leftOperand._isNaN && rightOperand._isNaN) 00365 return CPOrderedSame; 00366 00367 if (leftOperand._isNegative != rightOperand._isNegative) 00368 { 00369 if (rightOperand._isNegative) 00370 return CPOrderedDescending; 00371 else 00372 return CPOrderedAscending; 00373 } 00374 00375 // Before comparing number size check if zero (dont use CPDecimalIsZero as it is more computationally intensive) 00376 var leftIsZero = (leftOperand._mantissa.length == 1 && leftOperand._mantissa[0] == 0), 00377 rightIsZero = (rightOperand._mantissa.length == 1 && rightOperand._mantissa[0] == 0), 00378 // Sign is the same, quick check size (length + exp) 00379 s1 = leftOperand._exponent + leftOperand._mantissa.length, 00380 s2 = rightOperand._exponent + rightOperand._mantissa.length; 00381 00382 if (leftIsZero || s1 < s2) 00383 { 00384 if (rightOperand._isNegative) 00385 return CPOrderedDescending; 00386 else 00387 return CPOrderedAscending; 00388 } 00389 if (rightIsZero || s1 > s2) 00390 { 00391 if (rightOperand._isNegative) 00392 return CPOrderedAscending; 00393 else 00394 return CPOrderedDescending; 00395 } 00396 00397 // Same size, so check mantissa 00398 var l = MIN(leftOperand._mantissa.length, rightOperand._mantissa.length), 00399 i = 0; 00400 00401 for (; i < l; i++) 00402 { 00403 var d = rightOperand._mantissa[i] - leftOperand._mantissa[i]; 00404 00405 if (d > 0) 00406 { 00407 if (rightOperand._isNegative) 00408 return CPOrderedDescending; 00409 else 00410 return CPOrderedAscending; 00411 } 00412 if (d < 0) 00413 { 00414 if (rightOperand._isNegative) 00415 return CPOrderedAscending; 00416 else 00417 return CPOrderedDescending; 00418 } 00419 } 00420 00421 // Same digits, check length 00422 if (leftOperand._mantissa.length > rightOperand._mantissa.length) 00423 { 00424 if (rightOperand._isNegative) 00425 return CPOrderedAscending; 00426 else 00427 return CPOrderedDescending; 00428 } 00429 if (leftOperand._mantissa.length < rightOperand._mantissa.length) 00430 { 00431 if (rightOperand._isNegative) 00432 return CPOrderedDescending; 00433 else 00434 return CPOrderedAscending; 00435 } 00436 00437 return CPOrderedSame; 00438 } 00439 00440 // GNUSteps addition. This is standard O(n) complexity. 00441 // longMode makes the addition not round for up to double max digits, this is 00442 // to preserve precision during multiplication 00443 function _SimpleAdd(result, leftOperand, rightOperand, roundingMode, longMode) 00444 { 00445 var factor = (longMode)?2:1; 00446 00447 _CPDecimalSet(result, leftOperand); 00448 00449 var j = leftOperand._mantissa.length - rightOperand._mantissa.length, 00450 l = rightOperand._mantissa.length, 00451 i = l - 1, 00452 carry = 0, 00453 error = CPCalculationNoError; 00454 00455 // Add all the digits 00456 for (; i >= 0; i--) 00457 { 00458 var d = rightOperand._mantissa[i] + result._mantissa[i + j] + carry; 00459 if (d >= 10) 00460 { 00461 d = d % 10; // a division. subtraction and conditions faster? 00462 carry = 1; 00463 } 00464 else 00465 carry = 0; 00466 00467 result._mantissa[i + j] = d; 00468 } 00469 00470 if (carry) 00471 { 00472 for (i = j - 1; i >= 0; i--) 00473 { 00474 if (result._mantissa[i] != 9) 00475 { 00476 result._mantissa[i]++; 00477 carry = 0; 00478 break; 00479 } 00480 result._mantissa[i] = 0; 00481 } 00482 00483 if (carry) 00484 { 00485 Array.prototype.splice.call(result._mantissa, 0, 0, 1); 00486 00487 // The number must be shifted to the right 00488 if ((CPDecimalMaxDigits * factor) == leftOperand._mantissa.length) 00489 { 00490 var scale = - result._exponent - 1; 00491 CPDecimalRound(result, result, scale, roundingMode); 00492 } 00493 00494 if (CPDecimalMaxExponent < result._exponent) 00495 { 00496 result._isNaN = YES; 00497 error = CPCalculationOverflow; 00498 result._exponent = CPDecimalMaxExponent; 00499 } 00500 } 00501 } 00502 return error; 00503 } 00504 00514 function CPDecimalAdd(result, leftOperand, rightOperand, roundingMode, longMode) 00515 { 00516 if (leftOperand._isNaN || rightOperand._isNaN) 00517 { 00518 result._isNaN = YES; 00519 return CPCalculationNoError; 00520 } 00521 // check for zero 00522 if (CPDecimalIsZero(leftOperand)) 00523 { 00524 _CPDecimalSet(result, rightOperand); 00525 return CPCalculationNoError; 00526 } 00527 if (CPDecimalIsZero(rightOperand)) 00528 { 00529 _CPDecimalSet(result, leftOperand); 00530 return CPCalculationNoError; 00531 } 00532 00533 var n1 = CPDecimalCopy(leftOperand), 00534 n2 = CPDecimalCopy(rightOperand); 00535 00536 // For different signs use subtraction 00537 if (leftOperand._isNegative != rightOperand._isNegative) 00538 { 00539 if (leftOperand._isNegative) 00540 { 00541 n1._isNegative = NO; 00542 return CPDecimalSubtract(result, rightOperand, n1, roundingMode); 00543 } 00544 else 00545 { 00546 n2._isNegative = NO; 00547 return CPDecimalSubtract(result, leftOperand, n2, roundingMode); 00548 } 00549 } 00550 00551 var normerror = CPDecimalNormalize(n1, n2, roundingMode, longMode); 00552 00553 // below is equiv. of simple compare 00554 var comp = 0, 00555 ll = n1._mantissa.length, 00556 lr = n2._mantissa.length; 00557 if (ll == lr) 00558 comp = CPOrderedSame; 00559 else if (ll > lr) 00560 comp = CPOrderedDescending; 00561 else 00562 comp = CPOrderedAscending; 00563 00564 // both negative, make positive 00565 if (leftOperand._isNegative) 00566 { 00567 n1._isNegative = NO; 00568 n2._isNegative = NO; 00569 // SimpleCompare does not look at sign 00570 if (comp == CPOrderedDescending) 00571 { 00572 adderror = _SimpleAdd(result, n1, n2, roundingMode, longMode); 00573 } 00574 else 00575 { 00576 adderror = _SimpleAdd(result, n2, n1, roundingMode, longMode); 00577 } 00578 result._isNegative = YES; 00579 // swap sign over over/underflow exception 00580 if (CPCalculationUnderflow == adderror) 00581 adderror = CPCalculationOverflow; 00582 else if (CPCalculationUnderflow == adderror) 00583 adderror = CPCalculationUnderflow; 00584 } 00585 else 00586 { 00587 if (comp == CPOrderedAscending) 00588 { 00589 adderror = _SimpleAdd(result, n2, n1, roundingMode, longMode); 00590 } 00591 else 00592 { 00593 adderror = _SimpleAdd(result, n1, n2, roundingMode, longMode); 00594 } 00595 } 00596 00597 CPDecimalCompact(result); 00598 00599 if (adderror == CPCalculationNoError) 00600 return normerror; 00601 else 00602 return adderror; 00603 } 00604 00605 // GNUStep port internal subtract 00606 function _SimpleSubtract(result, leftOperand, rightOperand, roundingMode) 00607 { 00608 var error = CPCalculationNoError, 00609 borrow = 0, 00610 l = rightOperand._mantissa.length, 00611 j = leftOperand._mantissa.length - l, 00612 i = l - 1; 00613 00614 _CPDecimalSet(result, leftOperand); 00615 00616 // Now subtract all digits 00617 for (; i >= 0; i--) 00618 { 00619 var d = result._mantissa[i + j] - rightOperand._mantissa[i] - borrow; 00620 if (d < 0) 00621 { 00622 d = d + 10; 00623 borrow = 1; 00624 } 00625 else 00626 borrow = 0; 00627 00628 result._mantissa[i + j] = d; 00629 } 00630 00631 if (borrow) 00632 { 00633 for (i = j - 1; i >= 0; i--) 00634 { 00635 if (result._mantissa[i] != 0) 00636 { 00637 result._mantissa[i]--; 00638 break; 00639 } 00640 result._mantissa[i] = 9; 00641 } 00642 00643 if (-1 == i) 00644 { 00645 error = nil; 00646 } 00647 } 00648 00649 return error; 00650 } 00651 00661 function CPDecimalSubtract(result, leftOperand, rightOperand, roundingMode) 00662 { 00663 if (leftOperand._isNaN || rightOperand._isNaN) 00664 { 00665 result._isNaN = YES; 00666 return CPCalculationNoError; 00667 } 00668 00669 // check for zero 00670 if (CPDecimalIsZero(leftOperand)) 00671 { 00672 _CPDecimalSet(result, rightOperand); 00673 result._isNegative = !result._isNegative; 00674 return CPCalculationNoError; 00675 } 00676 if (CPDecimalIsZero(rightOperand)) 00677 { 00678 _CPDecimalSet(result, leftOperand); 00679 return CPCalculationNoError; 00680 } 00681 00682 var n1 = CPDecimalCopy(leftOperand), 00683 n2 = CPDecimalCopy(rightOperand), 00684 error1 = CPCalculationNoError; 00685 00686 // For different signs use addition 00687 if (leftOperand._isNegative != rightOperand._isNegative) 00688 { 00689 if (leftOperand._isNegative) 00690 { 00691 n1._isNegative = NO; 00692 error1 = CPDecimalAdd(result, n1, rightOperand, roundingMode); 00693 result._isNegative = YES; 00694 if (error1 == CPCalculationUnderflow) 00695 error1 = CPCalculationOverflow; 00696 else if (error1 == CPCalculationOverflow) // gnustep has bug here 00697 error1 = CPCalculationUnderflow; 00698 return error1; 00699 } 00700 else 00701 { 00702 n2._isNegative = NO; 00703 return CPDecimalAdd(result, leftOperand, n2, roundingMode); 00704 } 00705 } 00706 00707 var error = CPDecimalNormalize(n1, n2, roundingMode), 00708 comp = CPDecimalCompare(leftOperand, rightOperand); 00709 00710 if (comp == CPOrderedSame) 00711 { 00712 _CPDecimalSetZero(result); 00713 return CPCalculationNoError; 00714 } 00715 00716 // both negative, make positive and change order 00717 if (leftOperand._isNegative) 00718 { 00719 n1._isNegative = NO; 00720 n2._isNegative = NO; 00721 if (comp == CPOrderedAscending) 00722 { 00723 error1 = _SimpleSubtract(result, n1, n2, roundingMode); 00724 result._isNegative = YES; 00725 } 00726 else 00727 { 00728 error1 = _SimpleSubtract(result, n2, n1, roundingMode); 00729 } 00730 } 00731 else 00732 { 00733 if (comp == CPOrderedAscending) 00734 { 00735 error1 = _SimpleSubtract(result, n2, n1, roundingMode); 00736 result._isNegative = YES; 00737 } 00738 else 00739 { 00740 error1 = _SimpleSubtract(result, n1, n2, roundingMode); 00741 } 00742 } 00743 00744 CPDecimalCompact(result); 00745 00746 if (error1 == CPCalculationNoError) 00747 return error; 00748 else 00749 return error1; 00750 } 00751 00752 // this is a very simple O(n^2) implementation that uses subtract. Are there faster divides? 00753 function _SimpleDivide(result, leftOperand, rightOperand, roundingMode) 00754 { 00755 var error = CPCalculationNoError, 00756 n1 = CPDecimalMakeZero(), 00757 k = 0, 00758 firsttime = YES, 00759 stopk = CPDecimalMaxDigits + 1, 00760 used = 0; // How many digits of l have been used? 00761 00762 _CPDecimalSetZero(result); 00763 00764 n1._mantissa = []; 00765 00766 while ((k < leftOperand._mantissa.length) || (n1._mantissa.length 00767 && !((n1._mantissa.length == 1) && (n1._mantissa[0] == 0)))) 00768 { 00769 while (CPOrderedAscending == CPDecimalCompare(n1, rightOperand)) 00770 { 00771 if (stopk == k) 00772 break; 00773 if (n1._exponent) 00774 { 00775 // Put back zeros removed by compacting 00776 Array.prototype.push.call(n1._mantissa, 0); 00777 n1._exponent--; 00778 n1._isCompact = NO; 00779 } 00780 else 00781 { 00782 if (used < leftOperand._mantissa.length) 00783 { 00784 // Fill up with own digits 00785 if (n1._mantissa.length || leftOperand._mantissa[used]) 00786 { 00787 // only add 0 if there is already something 00788 Array.prototype.push.call(n1._mantissa, (leftOperand._mantissa[used])); 00789 n1._isCompact = NO; 00790 } 00791 used++; 00792 } 00793 else 00794 { 00795 if (result._exponent == CPDecimalMinExponent) 00796 { 00797 // use this as an end flag 00798 k = stopk; 00799 break; 00800 } 00801 // Borrow one digit 00802 Array.prototype.push.call(n1._mantissa, 0); 00803 result._exponent--; 00804 } 00805 00806 // Zeros must be added while enough digits are fetched to do the 00807 // subtraction, but first time round this just add zeros at the 00808 // start of the number , increases k, and hence reduces 00809 // the available precision. To solve this only inc k/add zeros if 00810 // this isn't first time round. 00811 if (!firsttime) 00812 { 00813 k++; 00814 result._mantissa[k - 1] = 0; 00815 } 00816 } 00817 } 00818 00819 // At this point digit in result we are working on is (k-1) so when 00820 // k == (CPDecimalMaxDigits+1) then we should stop i.e. last subtract 00821 // was last valid one. 00822 if (stopk == k) 00823 { 00824 error = CPCalculationLossOfPrecision; 00825 break; 00826 } 00827 00828 if (firsttime) 00829 { 00830 firsttime = NO; 00831 k++; 00832 } 00833 00834 error1 = CPDecimalSubtract(n1, n1, rightOperand, roundingMode); 00835 if (error1 != CPCalculationNoError) 00836 error = error1; 00837 00838 result._mantissa[k - 1]++; 00839 } 00840 00841 return error; 00842 } 00843 00853 function CPDecimalDivide(result, leftOperand, rightOperand, roundingMode) 00854 { 00855 var error = CPCalculationNoError, 00856 exp = leftOperand._exponent - rightOperand._exponent, 00857 neg = (leftOperand._isNegative != rightOperand._isNegative); 00858 00859 if (leftOperand._isNaN || rightOperand._isNaN) 00860 { 00861 result._isNaN = YES; 00862 return CPCalculationNoError; 00863 } 00864 00865 // check for zero 00866 if (CPDecimalIsZero(rightOperand)) 00867 { 00868 result._isNaN = YES; 00869 return CPCalculationDivideByZero; 00870 } 00871 if (CPDecimalIsZero(leftOperand)) 00872 { 00873 _CPDecimalSetZero(result); 00874 return CPCalculationNoError; 00875 } 00876 00877 //FIXME: Should also check for one 00878 00879 var n1 = CPDecimalCopy(leftOperand), 00880 n2 = CPDecimalCopy(rightOperand); 00881 00882 n1._exponent = 0; 00883 n1._isNegative = NO; 00884 n2._exponent = 0; 00885 n2._isNegative = NO; 00886 00887 error = _SimpleDivide(result, n1, n2, roundingMode); 00888 CPDecimalCompact(result); 00889 00890 if (result._exponent + exp > CPDecimalMaxExponent) 00891 { 00892 result._isNaN = YES; 00893 if (neg) 00894 return CPCalculationUnderflow; 00895 else 00896 return CPCalculationOverflow; 00897 } 00898 else if (result._exponent + exp < CPDecimalMinExponent) 00899 { 00900 // We must cut off some digits 00901 CPDecimalRound(result, result, exp + CPDecimalMaxExponent + 1, roundingMode); 00902 error = CPCalculationLossOfPrecision; 00903 00904 if (result._exponent + exp < CPDecimalMinExponent) 00905 { 00906 CPDecimalSetZero(result); 00907 return error; 00908 } 00909 } 00910 result._exponent += exp; 00911 result._isNegative = neg; 00912 return error; 00913 } 00914 00915 // Simple multiply O(n^2) , replace with something faster, like divide-n-conquer algo? 00916 function _SimpleMultiply(result, leftOperand, rightOperand, roundingMode, powerMode) 00917 { 00918 var error = CPCalculationNoError, 00919 carry = 0, 00920 exp = 0, 00921 n = CPDecimalMakeZero(); 00922 00923 _CPDecimalSetZero(result); 00924 00925 // Do every digit of the second number 00926 var i = 0; 00927 for (; i < rightOperand._mantissa.length; i++) 00928 { 00929 _CPDecimalSetZero(n); 00930 00931 n._exponent = rightOperand._mantissa.length - i - 1; 00932 carry = 0; 00933 d = rightOperand._mantissa[i]; 00934 00935 if (d == 0) 00936 continue; 00937 00938 var j = 0; 00939 for (j = leftOperand._mantissa.length - 1; j >= 0; j--) 00940 { 00941 e = leftOperand._mantissa[j] * d + carry; 00942 if (e >= 10) 00943 { 00944 carry = FLOOR(e / 10); 00945 e = e % 10; 00946 } 00947 else 00948 carry = 0; 00949 00950 // This is one off to allow final carry 00951 n._mantissa[j + 1] = e; 00952 } 00953 n._mantissa[0] = carry; 00954 00955 CPDecimalCompact(n); 00956 00957 error1 = CPDecimalAdd(result, result, n, roundingMode, YES); 00958 00959 if (error1 != CPCalculationNoError) 00960 error = error1; 00961 } 00962 00963 if (result._exponent + exp > CPDecimalMaxExponent) 00964 { 00965 // This should almost never happen 00966 result._isNaN = YES; 00967 return CPCalculationOverflow; 00968 } 00969 result._exponent += exp; 00970 00971 // perform round to CPDecimalMaxDigits 00972 if (result._mantissa.length > CPDecimalMaxDigits && !powerMode) 00973 { 00974 result._isCompact = NO; 00975 var scale = CPDecimalMaxDigits - (result._mantissa.length + result._exponent); 00976 CPDecimalRound(result, result, scale, roundingMode); // calls compact 00977 00978 error = CPCalculationLossOfPrecision; 00979 } 00980 return error; 00981 } 00982 00992 function CPDecimalMultiply(result, leftOperand, rightOperand, roundingMode, powerMode) 00993 { 00994 var error = CPCalculationNoError, 00995 exp = leftOperand._exponent + rightOperand._exponent, 00996 neg = (leftOperand._isNegative != rightOperand._isNegative); 00997 00998 if (leftOperand._isNaN || rightOperand._isNaN) 00999 { 01000 result._isNaN = YES; 01001 return CPCalculationNoError; 01002 } 01003 01004 // check for zero 01005 if (CPDecimalIsZero(rightOperand) || CPDecimalIsZero(leftOperand)) 01006 { 01007 _CPDecimalSetZero(result); 01008 return CPCalculationNoError; 01009 } 01010 01011 //FIXME: Should also check for one 01012 01013 if (exp > CPDecimalMaxExponent) 01014 { 01015 result._isNaN = YES; 01016 if (neg) 01017 return CPCalculationUnderflow; 01018 else 01019 return CPCalculationOverflow; 01020 } 01021 01022 var n1 = CPDecimalCopy(leftOperand), 01023 n2 = CPDecimalCopy(rightOperand); 01024 01025 n1._exponent = 0; 01026 n2._exponent = 0; 01027 n1._isNegative = NO; 01028 n2._isNegative = NO; 01029 01030 // below is equiv. of simple compare 01031 var comp = 0, 01032 ll = n1._mantissa.length, 01033 lr = n2._mantissa.length; 01034 if (ll == lr) 01035 comp = CPOrderedSame; 01036 else if (ll > lr) 01037 comp = CPOrderedDescending; 01038 else 01039 comp = CPOrderedAscending; 01040 01041 01042 if (comp == CPOrderedDescending) 01043 { 01044 error = _SimpleMultiply(result, n1, n2, roundingMode, powerMode); 01045 } 01046 else 01047 { 01048 error = _SimpleMultiply(result, n2, n1, roundingMode, powerMode); 01049 } 01050 01051 CPDecimalCompact(result); 01052 01053 if (result._exponent + exp > CPDecimalMaxExponent) 01054 { 01055 result._isNaN = YES; 01056 if (neg) 01057 return CPCalculationUnderflow; 01058 else 01059 return CPCalculationOverflow; 01060 } 01061 else if (result._exponent + exp < CPDecimalMinExponent) 01062 { 01063 // We must cut off some digits 01064 CPDecimalRound(result, result, exp + CPDecimalMaxExponent + 1, roundingMode); 01065 error = CPCalculationLossOfPrecision; 01066 01067 if (result._exponent + exp < CPDecimalMinExponent) 01068 { 01069 _CPDecimalSetZero(result); 01070 return error; 01071 } 01072 } 01073 01074 result._exponent += exp; 01075 result._isNegative = neg; 01076 01077 return error; 01078 } 01079 01089 function CPDecimalMultiplyByPowerOf10(result, dcm, power, roundingMode) 01090 { 01091 _CPDecimalSet(result, dcm); 01092 var p = result._exponent + power; 01093 if (p > CPDecimalMaxExponent) 01094 { 01095 result._isNaN = YES; 01096 return CPCalculationOverflow; 01097 } 01098 if (p < CPDecimalMinExponent) 01099 { 01100 result._isNaN = YES; 01101 return CPCalculationUnderflow; 01102 } 01103 result._exponent += power; 01104 return CPCalculationNoError; 01105 } 01106 01116 function CPDecimalPower(result, dcm, power, roundingMode) 01117 { 01118 var error = CPCalculationNoError, 01119 neg = (dcm._isNegative && (power % 2)), 01120 n1 = CPDecimalCopy(dcm); 01121 01122 n1._isNegative = NO; 01123 01124 _CPDecimalSetOne(result); 01125 01126 var e = power; 01127 while (e) 01128 { 01129 if (e & 1) 01130 { 01131 error = CPDecimalMultiply(result, result, n1, roundingMode); //, YES); // enable for high precision powers 01132 } 01133 01134 error = CPDecimalMultiply(n1, n1, n1, roundingMode); //, YES); // enable for high precision powers 01135 01136 e >>= 1; 01137 01138 if (error > CPCalculationLossOfPrecision) 01139 break; 01140 } 01141 01142 result._isNegative = neg; 01143 01144 /* // enable is powerMode to do finally rounding to Max Digits. 01145 if ([result._mantissa count] > CPDecimalMaxDigits) 01146 { 01147 result._isCompact = NO; 01148 var scale = CPDecimalMaxDigits - ([result._mantissa count] + result._exponent); 01149 CPDecimalRound(result, result, scale ,roundingMode); // calls compact 01150 error = CPCalculationLossOfPrecision; 01151 } 01152 */ 01153 01154 CPDecimalCompact(result); 01155 01156 return error; 01157 } 01158 01168 function CPDecimalNormalize(dcm1, dcm2, roundingMode, longMode) 01169 { 01170 var factor = (longMode)?2:1; 01171 01172 if (dcm1._isNaN || dcm2._isNaN) 01173 return CPCalculationNoError; // FIXME: correct behavior? 01174 01175 // ensure compact 01176 if (!dcm1._isCompact) 01177 CPDecimalCompact(dcm1); 01178 if (!dcm2._isCompact) 01179 CPDecimalCompact(dcm2); 01180 01181 if (dcm1._exponent == dcm2._exponent) 01182 return CPCalculationNoError; 01183 01184 var e1 = dcm1._exponent, 01185 e2 = dcm2._exponent; 01186 01187 // Add zeros 01188 var l2 = dcm2._mantissa.length, 01189 l1 = dcm1._mantissa.length, 01190 l = 0; 01191 01192 var e = 0; 01193 if (e2 > e1 && e1 >= 0 && e2 >= 0) 01194 e = e2 - e1; 01195 else if (e2 > e1 && e1 < 0 && e2 >= 0) 01196 e = e2 - e1; 01197 else if (e2 > e1 && e1 < 0 && e2 < 0) 01198 e = e2 - e1; 01199 else if (e2 < e1 && e1 >= 0 && e2 >= 0) 01200 e = e1 - e2; 01201 else if (e2 < e1 && e1 >= 0 && e2 < 0) 01202 e = e1 - e2; 01203 else if (e2 < e1 && e1 < 0 && e2 < 0) 01204 e = e1 - e2; 01205 01206 if (e2 > e1) 01207 l = MIN((CPDecimalMaxDigits * factor) - l2, e); //(e2 - e1)); 01208 else 01209 l = MIN((CPDecimalMaxDigits * factor) - l1, e); //(e1 - e2)); 01210 01211 for (var i = 0; i < l; i++) 01212 { 01213 if (e2 > e1) 01214 Array.prototype.push.call(dcm2._mantissa, 0); //dcm2._mantissa[i + l2] = 0; 01215 else 01216 Array.prototype.push.call(dcm1._mantissa, 0); 01217 } 01218 if (e2 > e1) 01219 { 01220 dcm2._exponent -= l; 01221 dcm2._isCompact = NO; 01222 } 01223 else 01224 { 01225 dcm1._exponent -= l; 01226 dcm1._isCompact = NO; 01227 } 01228 01229 // has been normalised? 01230 if (l != ABS(e2 - e1))//e2 - e1) 01231 { 01232 // no.. 01233 // Round of some digits to increase exponent - will compact too 01234 // One number may become zero after this 01235 if (e2 > e1) 01236 { 01237 CPDecimalRound(dcm1, dcm1, -dcm2._exponent, roundingMode); 01238 l1 = CPDecimalIsZero(dcm1); 01239 } 01240 else 01241 { 01242 CPDecimalRound(dcm2, dcm2, -dcm1._exponent, roundingMode); 01243 l2 = CPDecimalIsZero(dcm2); 01244 } 01245 01246 if ((dcm1._exponent != dcm2._exponent) && ((!l1) || (!l2))) 01247 { 01248 // Some zeros where cut of again by compacting 01249 if (e2 > e1) 01250 { 01251 l1 = dcm1._mantissa.length; 01252 l = MIN((CPDecimalMaxDigits * factor) - l1, ABS(dcm1._exponent - dcm2._exponent)); 01253 for (var i = 0; i < l; i++) 01254 { 01255 dcm1._mantissa[i + l1] = 0; // or addObject: ? one faster than other? 01256 } 01257 dcm1._isCompact = NO; 01258 dcm1._exponent = dcm2._exponent; 01259 } 01260 else 01261 { 01262 l2 = dcm2._mantissa.length; 01263 l = MIN((CPDecimalMaxDigits * factor) - l2, ABS(dcm2._exponent - dcm1._exponent)); 01264 for (var i = 0; i < l; i++) 01265 { 01266 dcm2._mantissa[i + l2] = 0; // or addObject: ? one faster than other? 01267 } 01268 dcm2._exponent = dcm1._exponent; 01269 dcm2._isCompact = NO; 01270 } 01271 } 01272 return CPCalculationLossOfPrecision; 01273 } 01274 01275 return CPCalculationNoError; 01276 } 01277 01290 function CPDecimalRound(result, dcm, scale ,roundingMode) 01291 { 01292 if (dcm._isNaN) 01293 return; 01294 01295 if (!dcm._isCompact) 01296 CPDecimalCompact(dcm); 01297 01298 // FIXME: check for valid inputs (eg scale etc) 01299 01300 // FIXME: if in longMode should this double? 01301 if (scale == CPDecimalNoScale) 01302 return; 01303 01304 _CPDecimalSet(result,dcm); 01305 01306 var mc = result._mantissa.length, 01307 l = mc + scale + result._exponent; 01308 01309 if (mc <= l) 01310 return; 01311 else if (l <= 0) 01312 { 01313 _CPDecimalSetZero(result); 01314 return; 01315 } else { 01316 var c = 0, 01317 n = 0, 01318 up = 0; 01319 01320 // Adjust length and exponent 01321 result._exponent += mc - l; 01322 01323 switch (roundingMode) 01324 { 01325 case CPRoundDown: 01326 up = result._isNegative; 01327 break; 01328 case CPRoundUp: 01329 up = !result._isNegative; 01330 break; 01331 case CPRoundPlain: 01332 n = result._mantissa[l]; 01333 up = (n >= 5); 01334 break; 01335 case _CPRoundHalfDown: 01336 n = result._mantissa[l]; 01337 up = (n > 5); 01338 break; 01339 case CPRoundBankers: 01340 n = result._mantissa[l]; 01341 if (n > 5) 01342 up = YES; 01343 else if (n < 5) 01344 up = NO; 01345 else 01346 { 01347 if (l == 0) 01348 c = 0; 01349 else 01350 c = result._mantissa[l - 1]; 01351 up = ((c % 2) != 0); 01352 } 01353 break; 01354 default: 01355 up = NO; 01356 break; 01357 } 01358 // cut mantissa 01359 result._mantissa = Array.prototype.slice.call(result._mantissa, 0, l); 01360 01361 if (up) 01362 { 01363 for (var i = l-1; i >= 0; i--) 01364 { 01365 if (result._mantissa[i] != 9) 01366 { 01367 result._mantissa[i]++; 01368 break; 01369 } 01370 result._mantissa[i] = 0; 01371 } 01372 // Final overflow? 01373 if (i == -1) 01374 { 01375 // As all digits are zeros, just change the first 01376 result._mantissa[0] = 1; 01377 if (result._exponent >= CPDecimalMaxExponent) 01378 { 01379 // Overflow in rounding. 01380 // Add one zero add the end. There must be space as 01381 // we just cut off some digits. 01382 Array.prototype.push.call(result._mantissa, 0); 01383 } 01384 else 01385 result._exponent++; 01386 } 01387 } 01388 } 01389 01390 CPDecimalCompact(result); 01391 } 01392 01398 function CPDecimalCompact(dcm) 01399 { 01400 // if positive or zero exp leading zeros simply delete, trailing ones u need to increment exponent 01401 if (!dcm || dcm._mantissa.length == 0 || CPDecimalIsNotANumber(dcm) ) 01402 return; 01403 01404 01405 if (CPDecimalIsZero(dcm)) 01406 { 01407 // handle zero number compacting 01408 _CPDecimalSetZero(dcm); 01409 return; 01410 } 01411 // leading zeros, when exponent is zero these mean we need to move our decimal point to compact 01412 // if exp is zero does it make sense to have them? don't think so so delete them 01413 while (dcm._mantissa[0] === 0) 01414 { 01415 Array.prototype.shift.call(dcm._mantissa); 01416 } 01417 // trailing zeros, strip them 01418 while (dcm._mantissa[dcm._mantissa.length - 1] === 0) 01419 { 01420 Array.prototype.pop.call(dcm._mantissa); 01421 dcm._exponent++; 01422 if (dcm._exponent + 1 > CPDecimalMaxExponent) 01423 { 01424 // TODO: test case for this 01425 // overflow if we compact anymore, so don't 01426 break; 01427 } 01428 } 01429 01430 dcm._isCompact = YES; 01431 } 01432 01440 function CPDecimalString(dcm, locale) 01441 { 01442 // Cocoa seems to just add all the zeros... this maybe controlled by locale, 01443 // will check. 01444 if (dcm._isNaN) 01445 return @"NaN"; 01446 01447 var string = @"", 01448 i = 0; 01449 01450 if (dcm._isNegative) 01451 string += "-"; 01452 01453 var k = dcm._mantissa.length, 01454 l = ((dcm._exponent < 0) ? dcm._exponent : 0) + k; 01455 01456 if (l < 0) 01457 { 01458 // add leading zeros 01459 string += "0."; 01460 for (i = 0; i < ABS(l); i++) 01461 { 01462 string += "0"; 01463 } 01464 l = k; 01465 } 01466 else if (l == 0) 01467 { 01468 string += "0"; 01469 } 01470 01471 for (i = 0; i < l; i++) 01472 { 01473 string += dcm._mantissa[i]; 01474 } 01475 if (l < k) 01476 { 01477 string += "."; 01478 for (i = l; i < k; i++) 01479 { 01480 string += dcm._mantissa[i]; 01481 } 01482 } 01483 for (i = 0; i < dcm._exponent; i++) 01484 { 01485 string += "0"; 01486 } 01487 01488 return string; 01489 /* 01490 // GNUStep 01491 if (dcm._isNaN) 01492 return @"NaN"; 01493 01494 var sep = 0; 01495 if ((locale == nil) || (sep = [locale objectForKey: CPDecimalSeparator]) == nil) 01496 sep = @"."; 01497 01498 if (CPDecimalIsZero(dcm)) 01499 return @"0" + sep + "0"; 01500 01501 var string = @""; 01502 01503 if (dcm._isNegative) 01504 string += "-"; 01505 01506 var len = [dcm._mantissa count], 01507 size = len + dcm._exponent; 01508 01509 if ((len <= 6) && (0 < size) && (size < 7)) 01510 { 01511 // For small numbers use the normal format 01512 var i = 0 01513 for (; i < len; i++) 01514 { 01515 if (size == i) 01516 string += sep; 01517 d = dcm._mantissa[i]; 01518 string += d.toString(); 01519 } 01520 for (i = 0; i < dcm._exponent; i++) 01521 { 01522 string += "0"; 01523 } 01524 } 01525 else if ((len <= 6) && (0 >= size) && (size > -3)) 01526 { 01527 // For small numbers use the normal format 01528 string += "0"; 01529 string += sep; 01530 01531 var i = 0; 01532 for (; i > size; i--) 01533 { 01534 string += "0"; 01535 } 01536 for (i = 0; i < len; i++) 01537 { 01538 d = dcm._mantissa[i]; 01539 string += d.toString(); 01540 } 01541 } 01542 else 01543 { 01544 // Scientific format 01545 var i = 0; 01546 for (; i < len; i++) 01547 { 01548 if (1 == i) 01549 string += sep; 01550 d = dcm._mantissa[i]; 01551 string += d.toString(); 01552 } 01553 if (size != 1) 01554 { 01555 //s = [NSString stringWithFormat: @"E%d", size-1]; 01556 //[string appendString: s]; 01557 string += "E" + (size - 1).toString(); 01558 } 01559 } 01560 01561 return string; 01562 */ 01563 }