diff --git a/Source/NSDecimal.m b/Source/NSDecimal.m new file mode 100644 index 000000000..b6984ba2c --- /dev/null +++ b/Source/NSDecimal.m @@ -0,0 +1,1232 @@ +/* + NSDecimal functions + Copyright (C) 2000 Free Software Foundation, Inc. + + Written by: Fred Kiefer + Created: July 2000 + + This file is part of the GNUstep Base Library. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA. + */ + +#include +#include +#include +#include +#include +#include + +/* + This file provides two implementations of the NSDecimal functions. + One is based on pure simple decimal mathematics, as we all learned it + in school. This version is rather slow and may be inexact in the extrem + cases. + THIS IS TESTED AND WORKING. + + The second implemenation requieres the GMP library, the GNU math package, + to do the hard work. This is very fast and accurate. But as GMP is not + available on all computers this has to be switched on at compile time. + THIS IS STILL NOT IMPLEMENTED. + */ + +#ifdef HAVE_GMP + +// Define GSDecimal as using a character vector +typedef struct { + char exponent; /* Signed exponent - -128 to 127 */ + BOOL isNegative; /* Is this negative? */ + BOOL validNumber; /* Is this a valid number? */ + char length; /* digits in mantissa. */ + char cMantissa[NSDecimalMaxDigit]; +} GSDecimal; + +#else +// Make GSDecimal a synonym of NSDecimal +typedef NSDecimal GSDecimal; + +#endif + + +void +NSDecimalCopy(NSDecimal *destination, const NSDecimal *source) +{ + memcpy(destination, source, sizeof(NSDecimal)); +} + +static void +GSDecimalCompact(GSDecimal *number) +{ + int i, j; + + if (!number->validNumber) + return; + + // Cut off trailing 0's + for (i = number->length-1; i >= 0; i--) + { + if (number->cMantissa[i] == 0) + { + if (number->exponent == 127) + { + // Overflow in compacting!! + // Leave the remaining 0s there. + break; + } + number->length--; + number->exponent++; + } + else + break; + } + + // Cut off leading 0's + for (i = 0; i < number->length; i++) + { + if (number->cMantissa[i] != 0) + break; + } + if (i > 0) + { + for (j = 0; j < number->length-i; j++) + { + number->cMantissa[j] = number->cMantissa[j+i]; + } + number->length -= i; + } + + if (!number->length) + { + number->exponent = 0; + number->isNegative = NO; + } +} + +static NSComparisonResult +GSDecimalCompare(const GSDecimal *leftOperand, const GSDecimal *rightOperand) +{ + int i, l; + int s1 = leftOperand->exponent + leftOperand->length; + int s2 = rightOperand->exponent + rightOperand->length; + + if (leftOperand->isNegative != rightOperand->isNegative) + { + if (rightOperand->isNegative) + return NSOrderedDescending; + else + return NSOrderedAscending; + } + + // Same sign, check size + if (s1 < s2) + { + if (rightOperand->isNegative) + return NSOrderedDescending; + else + return NSOrderedAscending; + } + if (s1 > s2) + { + if (rightOperand->isNegative) + return NSOrderedAscending; + else + return NSOrderedDescending; + } + + // Same size, check digits + l = MIN(leftOperand->length, rightOperand->length); + for (i = 0; i < l; i++) + { + int d = rightOperand->cMantissa[i] - leftOperand->cMantissa[i]; + + if (d > 0) + { + if (rightOperand->isNegative) + return NSOrderedDescending; + else + return NSOrderedAscending; + } + if (d < 0) + { + if (rightOperand->isNegative) + return NSOrderedAscending; + else + return NSOrderedDescending; + } + } + + // Same digits, check length + if (leftOperand->length > rightOperand->length) + { + if (rightOperand->isNegative) + return NSOrderedDescending; + else + return NSOrderedAscending; + } + + if (leftOperand->length < rightOperand->length) + { + if (rightOperand->isNegative) + return NSOrderedAscending; + else + return NSOrderedDescending; + } + + return NSOrderedSame; +} + +void +GSDecimalRound(GSDecimal *result, int scale, NSRoundingMode mode) +{ + int i; + // last valid digit in number + int l = scale + result->exponent + result->length; + + if (scale == NSDecimalNoScale) + return; + + if (!result->validNumber) + return; + + if (result->length <= l) + return; + else if (l <= 0) + { + result->length = 0; + result->exponent = 0; + result->isNegative = NO; + return; + } + else + { + int c, n; + BOOL up; + + // Adjust length and exponent + result->exponent += result->length - l; + result->length = l; + + switch (mode) + { + case NSRoundDown: + up = result->isNegative; + break; + case NSRoundUp: + up = !result->isNegative; + break; + case NSRoundPlain: + n = result->cMantissa[l]; + up = (n >= 5); + break; + case NSRoundBankers: + n = result->cMantissa[l]; + if (n > 5) + up = YES; + else if (n < 5) + up = NO; + else + { + if (l == 0) + c = 0; + else + c = result->cMantissa[l-1]; + up = ((c % 2) != 0); + } + break; + default: // No way to get here + up = NO; + break; + } + + if (up) + { + for (i = l-1; i >= 0; i--) + { + if (result->cMantissa[i] != 9) + { + result->cMantissa[i]++; + break; + } + result->cMantissa[i] = 0; + } + // Final overflow? + if (i == -1) + { + // As all digits are zeros, just change the first + result->cMantissa[0] = 1; + if (result->exponent == 127) + { + // Overflow in rounding!! + // Add one zero add the end. There must be space as + // we just cut off some digits. + result->cMantissa[l] = 0; + result->length++; + } + else + result->exponent++;; + } + } + } + + GSDecimalCompact(result); +} + +NSCalculationError +GSDecimalNormalize(GSDecimal *n1, GSDecimal *n2, NSRoundingMode mode) +{ + int e1 = n1->exponent; + int e2 = n2->exponent; + int i, l; + + if (!n1->validNumber || !n2->validNumber) + return NSCalculationNoError; + + // Do they have the same exponent already? + if (e1 == e2) + return NSCalculationNoError; + + // make sure n2 has the bigger exponent + if (e1 > e2) + { + GSDecimal *t; + + t = n1; + n1 = n2; + n2 = t; + i = e2; + e2 = e1; + e1 = i; + } + + // Add zeros to n2, as far as possible + l = MIN(NSDecimalMaxDigit - n2->length, e2 - e1); + for (i = 0; i < l; i++) + { + n2->cMantissa[i + n2->length] = 0; + } + n2->length += l; + n2->exponent -= l; + + if (l != e2 - e1) + { + // Round of some digit from n1 to increase exponent + GSDecimalRound(n1, -n2->exponent, mode); + if (n1->exponent != n2->exponent) + { + // Some zeros where cut of again by compacting + l = MIN(NSDecimalMaxDigit - n1->length, n1->exponent - n2->exponent); + for (i = 0; i < l; i++) + { + n1->cMantissa[(int)n1->length] = 0; + n1->length++; + } + n1->exponent = n2->exponent; + } + return NSCalculationLossOfPrecision; + } + + return NSCalculationNoError; +} + +#ifdef HAVE_GMP + +static void CharvecToDecimal(GSDecimal *m, NSDecimal *n) +{ + // Convert form a GSDecimal to a NSDecimal + n->exponent = m->exponent; + n->isNegative = m->isNegative; + n->validNumber = m->validNumber; + + n->size = mpn_set_str(n->mantissa, m->cMantissa, m->length, 10); +} + +static void DecimalToCharvec(NSDecimal *n, GSDecimal *m) +{ + // Convert form a NSDecimal to a GSDecimal + m->exponent = n->exponent; + m->isNegative = n->isNegative; + m->validNumber = n->validNumber; + + m->lenght = mpn_get_str(m->cMantissa, 10, n->mantissa, n->size); +} + +void +NSDecimalCompact(NSDecimal *number) +{ + GSDecimal m; + + DecimalToCharvec(number, &m); + GSDecimalCompact(&m); + CharvecToDecimal(&m, number); +} + +NSComparisonResult +NSDecimalCompare(const NSDecimal *leftOperand, const NSDecimal *rightOperand) +{ + GSDecimal m1; + GSDecimal m2; + + DecimalToCharvec(leftOperand, &m1); + DecimalToCharvec(rightOperand, &m2); + return GSDecimalCompare(&m1, &m2); +} + +void +NSDecimalRound(NSDecimal *result, const NSDecimal *number, int scale, + NSRoundingMode mode) +{ + GSDecimal m; + + DecimalToCharvec(number, &m); + GSDecimalRound(&m, scale, mode); + CharvecToDecimal(&m, result); +} + +NSCalculationError +NSDecimalNormalize(NSDecimal *n1, NSDecimal *n2, NSRoundingMode mode) +{ + NSCalculationError error; + GSDecimal m1; + GSDecimal m2; + + DecimalToCharvec(n1, &m1); + DecimalToCharvec(n2, &m2); + error = GSDecimalNormalize(&m1, &m2, mode); + CharvecToDecimal(&m1, n1); + CharvecToDecimal(&m2, n2); +} + +NSCalculationError +NSDecimalAdd(NSDecimal *result, const NSDecimal *left, const NSDecimal *right, + NSRoundingMode mode) +{ + NSCalculationError error = NSCalculationNoError; + NSDecimal n1; + NSDecimal n2; + NSDecimal n3; + mp_limb_t carry; + + NSDecimalCopy(&n1, left); + NSDecimalCopy(&n2, right); + error = NSDecimalNormalize(&n1, &n2, mode); + + if (n1.size >= n2.size) + { + carry = mpn_add(n3.lMantissa, n1.lMantissa, n1.size, n2.lMantissa, n2.size); + n3.size = n1.size; + } + else + { + carry = mpn_add(n3.lMantissa, n2.lMantissa, n2.size, n1.lMantissa, n1.size); + n3.size = n2.size; + } + //FIXME: check carry + + return error; +} + +#else + +// First implementations of the functions defined in NSDecimal.h +static NSDecimal zero = {0, NO, YES, 0, {0}}; + +void +NSDecimalCompact(NSDecimal *number) +{ + GSDecimalCompact(number); +} + +NSComparisonResult +NSDecimalCompare(const NSDecimal *leftOperand, const NSDecimal *rightOperand) +{ + return GSDecimalCompare(leftOperand, rightOperand); +} + +void +NSDecimalRound(NSDecimal *result, const NSDecimal *number, int scale, + NSRoundingMode mode) +{ + NSDecimalCopy(result, number); + + GSDecimalRound(result, scale, mode); +} + +NSCalculationError +NSDecimalNormalize(NSDecimal *n1, NSDecimal *n2, NSRoundingMode mode) +{ + return GSDecimalNormalize(n1, n2, mode); +} + +NSCalculationError +NSDecimalAdd(NSDecimal *result, const NSDecimal *left, const NSDecimal *right, + NSRoundingMode mode) +{ + NSCalculationError error = NSCalculationNoError; + int i, j, l, d; + int carry = 0; + NSDecimal n1; + NSDecimal n2; + NSDecimal *n; + BOOL neg; + + if (!left->validNumber || !right->validNumber) + { + result->validNumber = NO; + return NSCalculationNoError; + } + + // For different signs use subtraction + if (left->isNegative != right->isNegative) + { + if (left->isNegative) + { + NSDecimalCopy(&n1, left); + n1.isNegative = NO; + return NSDecimalSubtract(result, right, &n1, mode); + } + else + { + NSDecimalCopy(&n1, right); + n1.isNegative = NO; + return NSDecimalSubtract(result, left, &n1, mode); + } + } + + if (!left->length) + { + NSDecimalCopy(result, right); + return error; + } + if (!right->length) + { + NSDecimalCopy(result, left); + return error; + } + NSDecimalCopy(&n1, left); + NSDecimalCopy(&n2, right); + error = NSDecimalNormalize(&n1, &n2, mode); + + if (!n1.length) + { + NSDecimalCopy(result, right); + return error; + } + if (!n2.length) + { + NSDecimalCopy(result, left); + return error; + } + + j = n1.length - n2.length; + if (j >= 0) + { + // Use sign from input as n1 might be zero + neg = left->isNegative; + NSDecimalCopy(result, &n1); + result->isNegative = neg; + n = &n2; + l = n2.length; + } + else + { + // Use sign from input as n2 might be zero + neg = left->isNegative; + NSDecimalCopy(result, &n2); + result->isNegative = neg; + n = &n1; + l = n1.length; + j = -j; + } + + // Add all the digits + for (i = l-1; i >= 0; i--) + { + d = n->cMantissa[i] + result->cMantissa[i + j] + carry; + if (d >= 10) + { + d = d % 10; + carry = 1; + } + else + carry = 0; + + result->cMantissa[i + j] = d; + } + + if (carry) + { + for (i = j-1; i >= 0; i--) + { + if (result->cMantissa[i] != 9) + { + result->cMantissa[i]++; + carry = 0; + break; + } + result->cMantissa[i] = 0; + } + + if (carry) + { + // The number must be shifted to the right + if (result->length == NSDecimalMaxDigit) + { + NSDecimalRound(result, result, + NSDecimalMaxDigit - 1 - result->exponent, + mode); + } + + if (result->exponent == 127) + { + result->validNumber = NO; + if (result->isNegative) + return NSCalculationUnderflow; + else + return NSCalculationOverflow; + } + + for (i = result->length-1; i >= 0; i--) + { + result->cMantissa[i+1] = result->cMantissa[i]; + } + result->cMantissa[0] = 1; + result->length++; + } + } + + NSDecimalCompact(result); + + return error; +} + +NSCalculationError +NSDecimalSubtract(NSDecimal *result, const NSDecimal *left, + const NSDecimal *right, NSRoundingMode mode) +{ + NSCalculationError error = NSCalculationNoError; + int i, j, l, d; + int carry = 0; + NSDecimal n1; + NSDecimal n2; + NSDecimal *n; + NSComparisonResult comp; + + if (!left->validNumber || !right->validNumber) + { + result->validNumber = NO; + return NSCalculationNoError; + } + + // For different signs use addition + if (left->isNegative != right->isNegative) + { + if (left->isNegative) + { + NSDecimalCopy(&n1, left); + n1.isNegative = NO; + error = NSDecimalAdd(result, &n1, right, mode); + result->isNegative = YES; + return error; + } + else + { + NSDecimalCopy(&n1, right); + n1.isNegative = NO; + return NSDecimalAdd(result, left, &n1, mode); + } + } + + // both negative, make positive and change order + if (left->isNegative) + { + NSDecimalCopy(&n1, left); + n1.isNegative = NO; + NSDecimalCopy(&n2, right); + n2.isNegative = NO; + return NSDecimalSubtract(result, &n2, &n1, mode); + } + + comp = NSDecimalCompare(left, right); + if (comp == NSOrderedSame) + { + NSDecimalCopy(result, &zero); + return NSCalculationNoError; + } + + if (comp == NSOrderedAscending) + { + error = NSDecimalSubtract(result, right, left, mode); + result->isNegative = YES; + return error; + } + + if (!right->length) + { + NSDecimalCopy(result, left); + return error; + } + + // Now left is the bigger number + NSDecimalCopy(&n1, left); + NSDecimalCopy(&n2, right); + error = NSDecimalNormalize(&n1, &n2, mode); + + if (!n2.length) + { + NSDecimalCopy(result, left); + return error; + } + + j = n1.length - n2.length; + if (j >= 0) + { + NSDecimalCopy(result, &n1); + n = &n2; + l = n2.length; + } + else + { + NSLog(@"Wrong order in subtract"); + NSLog(@"The left is %@, %@", NSDecimalString(left, nil), NSDecimalString(&n1, nil)); + NSLog(@"The right is %@, %@", NSDecimalString(right, nil), NSDecimalString(&n2, nil)); + NSDecimalCopy(result, &n2); + n = &n1; + l = n1.length; + j = -j; + } + + // Now subtract all digits + for (i = l-1; i >= 0; i--) + { + d = result->cMantissa[i + j] - n->cMantissa[i] - carry; + if (d < 0) + { + d = d + 10; + carry = 1; + } + else + carry = 0; + + result->cMantissa[i + j] = d; + } + + if (carry) + { + for (i = j-1; i >= 0; i--) + { + if (result->cMantissa[i] != 0) + { + result->cMantissa[i]--; + carry = 0; + break; + } + result->cMantissa[i] = 9; + } + + if (carry) + { + NSLog(@"Impossible error in substraction"); + } + } + + NSDecimalCompact(result); + + return error; +} + +NSCalculationError +NSDecimalMultiply(NSDecimal *result, const NSDecimal *l, const NSDecimal *r, NSRoundingMode mode) +{ + NSCalculationError error = NSCalculationNoError; + int i, j, d, e; + int carry = 0; + NSDecimal n1; + NSDecimal n2; + NSDecimal n; + int exp; + BOOL neg = l->isNegative != r->isNegative; + + if (!l->validNumber || !r->validNumber) + { + result->validNumber = NO; + return error; + } + + exp = l->exponent + r->exponent; + if (exp > 127) + { + result->validNumber = NO; + if (neg) + return NSCalculationUnderflow; + else + return NSCalculationOverflow; + } + else + { + NSDecimalCopy(&n1, l); + NSDecimalCopy(&n2, r); + } + + NSDecimalCopy(result, &zero); + n.validNumber = YES; + n.isNegative = NO; + + // if l->length = 38 round one off + if (n1.length == NSDecimalMaxDigit) + { + NSDecimalRound(&n1, &n1, -1-n1.exponent, mode); + // This might changed more than one + exp = n1.exponent + n2.exponent; + } + + // Do every digit of the second number + for (i = 0; i < n2.length; i++) + { + n.length = n1.length+1; + n.exponent = n2.length - i - 1; + carry = 0; + d = n2.cMantissa[i]; + + for (j = n1.length-1; j >= 0; j--) + { + e = n1.cMantissa[j] * d + carry; + + if (e >= 10) + { + carry = e / 10; + e = e % 10; + } + else + carry = 0; + // This is one off to allow final carry + n.cMantissa[j+1] = e; + } + n.cMantissa[0] = carry; + NSDecimalCompact(&n); + error = NSDecimalAdd(result, result, &n, mode); + } + + if (result->exponent + exp > 127) + { + result->validNumber = NO; + if (neg) + return NSCalculationUnderflow; + else + return NSCalculationOverflow; + } + else if (result->exponent + exp < -127) + { + // We must cut off some digits + NSDecimalRound(result, result, exp+127, mode); + error = NSCalculationLossOfPrecision; + + if (result->exponent + exp < -127) + { + NSDecimalCopy(result, &zero); + return error; + } + } + + result->exponent += exp; + result->isNegative = neg; + NSDecimalCompact(result); + + return error; +} + +NSCalculationError +NSDecimalDivide(NSDecimal *result, const NSDecimal *l, const NSDecimal *r, NSRoundingMode mode) +{ + NSCalculationError error = NSCalculationNoError; + int k, m; + int used; // How many digits of l have been used? + NSDecimal n1; + NSDecimal n2; + NSDecimal n3; + int exp; + BOOL neg = l->isNegative != r->isNegative; + + if (!l->validNumber || !r->validNumber) + { + result->validNumber = NO; + return NSCalculationNoError; + } + + // Check for zero + if (!r->length) + { + result->validNumber = NO; + return NSCalculationDivideByZero; + } + + // Should also check for one + + exp = l->exponent - r->exponent; + NSDecimalCopy(&n1, &zero); + NSDecimalCopy(&n2, r); + n2.exponent = 0; + n2.isNegative = NO; + NSDecimalCopy(&n3, l); + NSDecimalCopy(result, &zero); + m = n2.length; + k = 0; + used = 0; + + while ((k < n3.length ) || (n1.length)) + { + while (NSDecimalCompare(&n1, &n2) == NSOrderedAscending) + { + if (k == NSDecimalMaxDigit-1) + break; + if (n1.exponent) + { + // Put back removed zeros + n1.cMantissa[(int)n1.length] = 0; + n1.length++; + n1.exponent--; + } + else + { + if (used < n3.length) + { + // Fill up with own digits + n1.cMantissa[(int)n1.length] = n3.cMantissa[used]; + used++; + } + else + { + if (exp == -127) + { + // use this as a end flag + k = NSDecimalMaxDigit-1; + break; + } + // Borrow one digit + n1.cMantissa[(int)n1.length] = 0; + exp--; + } + n1.length++; + k++; + result->cMantissa[k-1] = 0; + result->length++; + } + } + + if (k == NSDecimalMaxDigit-1) + { + error = NSCalculationLossOfPrecision; + break; + } + + error = NSDecimalSubtract(&n1, &n1, &n2, mode); + result->cMantissa[k-1]++; + NSDecimalCompact(&n1); + } + + if (result->exponent + exp > 127) + { + result->validNumber = NO; + if (neg) + return NSCalculationUnderflow; + else + return NSCalculationOverflow; + } + else if (result->exponent + exp < -127) + { + NSDecimalCopy(result, &zero); + return NSCalculationLossOfPrecision; + } + + result->exponent += exp; + result->isNegative = neg; + NSDecimalCompact(result); + + return error; +} + +NSCalculationError +NSDecimalPower(NSDecimal *result, const NSDecimal *l, unsigned power, NSRoundingMode mode) +{ + NSCalculationError error = NSCalculationNoError; + unsigned int e = power; + NSDecimal n1; + BOOL neg = (l->isNegative && (power % 2)); + + NSDecimalCopy(&n1, l); + n1.isNegative = NO; + NSDecimalCopy(result, &zero); + result->length = 1; + result->cMantissa[0] = 1; + + while (e) + { + if (e & 1) + { + error = NSDecimalMultiply(result, result, &n1, mode); + } + // keep on squaring the number + error = NSDecimalMultiply(&n1, &n1, &n1, mode); + e >>= 1; + } + + result->isNegative = neg; + NSDecimalCompact(result); + + return error; +} + +NSCalculationError +NSDecimalMultiplyByPowerOf10(NSDecimal *result, const NSDecimal *n, short power, NSRoundingMode mode) +{ + int p = result->exponent + power; + + NSDecimalCopy(result, n); + if (p > 127) + { + result->validNumber = NO; + return NSCalculationOverflow; + } + if (p < -127) + { + result->validNumber = NO; + return NSCalculationUnderflow; + } + result->exponent += power; + return NSCalculationNoError; +} + +NSString* +NSDecimalString(const NSDecimal *number, NSDictionary *locale) +{ + int i; + int d; + NSString *s; + NSMutableString *string; + NSString *sep; + int size; + + if (!number->validNumber) + return @"NaN"; + + if ((locale == nil) || + (sep = [locale objectForKey: NSDecimalSeparator]) == nil) + sep = @"."; + + string = [NSMutableString stringWithCapacity: 45]; + + if (!number->length) + { + [string appendString: @"0"]; + [string appendString: sep]; + [string appendString: @"0"]; + return string; + } + + if (number->isNegative) + [string appendString: @"-"]; + + size = number->length + number->exponent; + if ((number->length <= 6) && (0 < size) && (size < 7)) + { + // For small numbers use the normal format + for (i = 0; i < number->length; i++) + { + if (size == i) + [string appendString: sep]; + d = number->cMantissa[i]; + s = [NSString stringWithFormat: @"%d", d]; + [string appendString: s]; + } + for (i = 0; i < number->exponent; i++) + { + [string appendString: @"0"]; + } + } + else if ((number->length <= 6) && (0 >= size) && (size > -3)) + { + // For small numbers use the normal format + [string appendString: @"0"]; + [string appendString: sep]; + + for (i = 0; i > size; i--) + { + [string appendString: @"0"]; + } + for (i = 0; i < number->length; i++) + { + d = number->cMantissa[i]; + s = [NSString stringWithFormat: @"%d", d]; + [string appendString: s]; + } + } + else + { + // Scientific format + for (i = 0; i < number->length; i++) + { + if (i == 1) + [string appendString: sep]; + d = number->cMantissa[i]; + s = [NSString stringWithFormat: @"%d", d]; + [string appendString: s]; + } + if (size != 1) + { + s = [NSString stringWithFormat: @"E%d", size-1]; + [string appendString: s]; + } + } + + return string; +} + +// GNUstep extensions to make the implementation of NSDecimalNumber totaly +// independent for NSDecimals internal representation + +// Give back the biggest NSDecimal +GS_EXPORT void +NSDecimalMax(NSDecimal *result) +{ + NSDecimalFromComponents(result, 9, 127, NO); +} + +// Give back the smallest NSDecimal +GS_EXPORT void +NSDecimalMin(NSDecimal *result) +{ + // FIXME: Should this be the smallest possible or the smallest positive number + NSDecimalFromComponents(result, 9, 127, YES); +} + +// Give back the value of a NSDecimal as a double +GS_EXPORT double +NSDecimalDouble(NSDecimal *number) +{ + double d = 0.0; + int i; + + if (!number->validNumber) + return d; + + // Sum up the digits + for (i = 0; i < number->length; i++) + { + d *= 10; + d += number->cMantissa[i]; + } + + // multiply with the exponent + // There is also a GNU extension pow10!! + d *= pow(10, number->exponent); + + if (number->isNegative) + d = -d; + + return d; +} + +// Create a NSDecimal with a cMantissa, exponent and a negative flag +GS_EXPORT void +NSDecimalFromComponents(NSDecimal *result, unsigned long long mantissa, + short exponent, BOOL negative) +{ + char digit; + int i, j; + result->isNegative = negative; + result->exponent = exponent; + result->validNumber = YES; + + i = 0; + while (mantissa) + { + digit = mantissa % 10; + // Store the digit starting from the end of the array + result->cMantissa[NSDecimalMaxDigit-i-1] = digit; + mantissa = mantissa / 10; + i++; + } + + for (j = 0; j < i; j++) + { + // Move the digits to the beginning + result->cMantissa[j] = result->cMantissa[j + NSDecimalMaxDigit-i]; + } + + result->length = i; + + NSDecimalCompact(result); +} + +// Create a NSDecimal from a string using the local +GS_EXPORT void +NSDecimalFromString(NSDecimal *result, NSString *numberValue, + NSDictionary *locale) +{ + NSRange found; + NSString *sep = [locale objectForKey: NSDecimalSeparator]; + const char *s; + int i; + + if (sep == nil) + sep = @"."; + + NSDecimalCopy(result, &zero); + found = [numberValue rangeOfString: sep]; + if (found.length) + { + s = [[numberValue substringToIndex: found.location] cString]; + while ((*s) && (!isdigit(*s))) s++; + i = 0; + while ((*s) && (isdigit(*s))) + { + result->cMantissa[i++] = *s - '0'; + result->length++; + s++; + } + s = [[numberValue substringFromIndex: NSMaxRange(found)] cString]; + while ((*s) && (isdigit(*s))) + { + result->cMantissa[i++] = *s - '0'; + result->length++; + result->exponent--; + s++; + } + } + else + { + s = [numberValue cString]; + while ((*s) && (!isdigit(*s))) s++; + i = 0; + while ((*s) && (isdigit(*s))) + { + result->cMantissa[i++] = *s - '0'; + result->length++; + s++; + } + } + + if ((*s == 'e') || (*s == 'E')) + { + s++; + result->exponent += atoi(s); + } + + if (!result->length) + result->validNumber = NO; + + NSDecimalCompact(result); +} + +#endif