I am confused by NSDecimalNumber and its "behaviors". I have an NSDecimalNumber that represents a dollar value, say $37.50. I'd like to find out how many times say 5.0 goes into that number and then know what's left over. I can get the straight division and get 7.50 but I want 7 mod 2.50. I could convert to an integer but need to save the "cents" so wondering if there's some tricks in the framework?
Using Peter Hoseys example, but with iOS code:
NSDecimalNumber *dividend = [NSDecimalNumber decimalNumberWithDecimal:[[NSNumber numberWithDouble:37.5] decimalValue]];
NSDecimalNumber *divisor = [NSDecimalNumber decimalNumberWithDecimal:[[NSNumber numberWithDouble:5.0] decimalValue]];
NSDecimalNumber *quotient = [dividend decimalNumberByDividingBy:divisor withBehavior:[NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundDown scale:0 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO]];
NSDecimalNumber *subtractAmount = [quotient decimalNumberByMultiplyingBy:divisor];
NSDecimalNumber *remainder = [dividend decimalNumberBySubtracting:subtractAmount];
- Divide the dividend by the divisor, rounding down. This gets you the quotient.
- Multiply the divisor by the quotient.
- Subtract that from the dividend. This gets you the remainder.
(37.50 // 5) == 7; 7 * 5 == 35; 37.50 - 35 = 2.50.
(Note: //
is Python's operator for integral division. I've borrowed it here. Obviously, you should not actually attempt to use //
for division in Objective-C code.)
decimalNumberByDividingBy:withBehavior:
to do the division. Then you don't need to round separately. –
Roz NSDecimalNumber
category below that takes care of those cases. –
Frulla Here's a NSDecimalNumber
category which also works with negative numbers and negative divisors:
- (BOOL)isNegative
{
return (NSOrderedDescending == [[NSDecimalNumber zero] compare:self]);
}
- (NSDecimalNumber *)invertedNumber
{
NSDecimalNumber *negOne = [NSDecimalNumber decimalNumberWithMantissa:1 exponent:0 isNegative:YES];
return [self decimalNumberByMultiplyingBy:negOne];
}
- (NSDecimalNumber *)moduloFor:(NSDecimalNumber *)divisor
{
NSRoundingMode roundingMode = ([self isNegative] ^ [divisor isNegative]) ? NSRoundUp : NSRoundDown;
NSDecimalNumberHandler *rounding = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:roundingMode
scale:0
raiseOnExactness:NO
raiseOnOverflow:NO
raiseOnUnderflow:NO
raiseOnDivideByZero:NO];
// divide and get the remainder
NSDecimalNumber *quotient = [self decimalNumberByDividingBy:divisor withBehavior:rounding];
NSDecimalNumber *subtract = [quotient decimalNumberByMultiplyingBy:divisor];
NSDecimalNumber *modulo = [self decimalNumberBySubtracting:subtract];
if ([divisor isNegative]) {
return [modulo invertedNumber];
}
return modulo;
}
modulo
at the end is unnecessary. modulo
already carries the correct sign from the quotient. After removing the inversion and just returning modulo
the results match Java's BigDecimal.remainder
operator for all four combinations of dividend/divisor positive/negative. –
Relegate Same solution in Swift 4:
let dividend = NSDecimalNumber(string: Price)
let divisor = NSDecimalNumber(decimal: 0.05)
let quotient = dividend.dividing(by: divisor, withBehavior: NSDecimalNumberHandler(roundingMode: .down, scale: 0, raiseOnExactness: false, raiseOnOverflow: false, raiseOnUnderflow: false, raiseOnDivideByZero: false))
let subtractAmount = quotient?.multiplying(by: divisor)
let remainder = dividend.subtracting(anAmount)
if remainder != 0
{ ...
You could theoretically do some tricks with the NSDecimalNumber methods decimalNumberByDividingBy:
, decimalNumberByMultiplyingBy:
, and decimalValue
. Divide the numbers, grab the decimal number structure, figure out the remainder from the structure, then create a new decimal number with that remainder and multiply that remainder by the original number.
The easier way to do it, however, would probably be to figure out the maximum precision you want (call it N places after the decimal point), multiply the number by 10eN, then just grab the integer value and do your division and mod on that. You run the risk of losing data, especially with very large numbers, however, so check the biggest number you want and figure out what data type - if any - will work for you. NSNumber does support unsignedLongLongValue
.
© 2022 - 2024 — McMap. All rights reserved.
NSRoundingMode roundingMode = ((dividend.floatValue < 0) ^ (divisor.floatValue < 0)) ? NSRoundUp : NSRoundDown;
– Purposeful