NSDecimalNumber rounding - ios

I would to round a decimal number like this :
4363,65 ----> 4364
I have tried this :
NSDecimalNumber *decimalNumber = [NSDecimalNumber decimalNumberWithString#"4363,65"];
NSDecimalNumberHandler *behav = [[NSDecimalNumberHandler alloc] initWithRoundingMode:NSRoundPlain scale:NSDecimalNoScale
raiseOnExactness:YES
raiseOnOverflow:YES
raiseOnUnderflow:YES
raiseOnDivideByZero:YES];
NSDecimalNumber *roundedDecimal = [decimalNumber decimalNumberByRoundingAccordingToBehavior:behav];
I don't have the expected result.How i can round it ?

I see 2 problems in your code. The first is the creation of the number. You are using , as a decimal separator. If your locale is not configured to use , for decimals, your number will be parsed as 4363. This is what probably happens.
The second problem is the value for the scale parameter. It takes the number of decimal digits but you are using a constant NSDecimalNoScale which is actually equal to SHRT_MAX. That's not what you want.
//make sure you use the correct format depending on your locale
NSDecimalNumber *decimalNumber = [NSDecimalNumber decimalNumberWithString:#"4363.65"];
NSDecimalNumberHandler *behav = [[NSDecimalNumberHandler alloc] initWithRoundingMode:NSRoundPlain
scale:0
raiseOnExactness:YES
raiseOnOverflow:YES
raiseOnUnderflow:YES
raiseOnDivideByZero:YES];

This is the easiest way:
float f = 4363,65;
int rounded = (f + 0.5);

Related

Format NSDecimalNumber to currency by country code without loss of precision

So right now I have the following code:
- (NSString*)convertToLocalCurrencyFormat:(NSDecimalNumber*)result {
NSNumberFormatter* formatter = [[NSNumberFormatter alloc] init];
formatter.numberStyle = NSNumberFormatterCurrencyStyle;
formatter.currencyCode = self.comparisonCurrency;
formatter.usesSignificantDigits = YES;
return [formatter stringFromNumber:result];
}
When I pass in an NSDecimalNumber* containing 678071967196719797153475347466.94627863, it gets formatted to ¥678,072,000,000,000,000,000,000,000,000 (with the currencyCode set to JPY). If I leave out the formatter.usesSignificantDigits = YES line, then it gets formatted to ¥678,071,967,196,719,797,153,475,347,467, closer, but still dropping the decimal and following values.
However, when I pass in 6780.0416000000012517376, it's formatted correctly to ¥6,780.04 with the significant digits line. It gets formatted to ¥6,780 without the significant digits line.
I know that NSNumberFormatter can take in any NSNumber as a parameter, but can only deal with values as precise as doubles, leaving NSDecimalNumber with no errors and incorrect results.
How can I format NSDecimalNumbers with currency codes without loss of precision?
Thanks
Try setting the minimum fraction digits instead:
formatter.minimumFractionDigits = 2;
HTH

Converting double to NSDecimalNumber while maintaining accuracy

I need to convert the results of calculations performed in a double, but I cannot use decimalNumberByMultiplyingBy or any other NSDecimalNumber function. I've tried to get an accurate result in the following ways:
double calc1 = 23.5 * 45.6 * 52.7; // <-- Correct answer is 56473.32
NSLog(#"calc1 = %.20f", calc1);
-> calc1 = 56473.32000000000698491931
NSDecimalNumber *calcDN = (NSDecimalNumber *)[NSDecimalNumber numberWithDouble:calc1];
NSLog(#"calcDN = %#", [calcDN stringValue]);
-> calcDN = 56473.32000000001024
NSDecimalNumber *testDN = [[[NSDecimalNumber decimalNumberWithString:#"23.5"] decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithString:#"45.6"]] decimalNumberByMultiplyingBy:[NSDecimalNumber decimalNumberWithString:#"52.7"]];
NSLog(#"testDN = %#", [testDN stringValue]);
-> testDN = 56473.32
I understand that this difference is related to the respective accuracies.
But here's my question: How can I round this number in the most accurate way possible regardless of what the initial value of double may be? And if a more accurate method exists to do the initial calculation, what is that method?
Well, you can either use double to represent the numbers and embrace inaccuracies or use some different number representation, such as NSDecimalNumber. It all depends on what are the expected values and business requirements concerning accuracy.
If it is really crucial not to use arithmetic methods provided by NSDecimalNumber, than the rounding behaviour is best controlled using NSDecimalNumberHandler, which is a concrete implementation of NSDecimalNumberBehaviors protocol. The actual rounding is performed using decimalNumberByRoundingAccordingToBehavior: method.
Here comes the snippet - it's in Swift, but it should be readable:
let behavior = NSDecimalNumberHandler(roundingMode: NSRoundingMode.RoundPlain,
scale: 2,
raiseOnExactness: false,
raiseOnOverflow: false,
raiseOnUnderflow: false,
raiseOnDivideByZero: false)
let calcDN : NSDecimalNumber = NSDecimalNumber(double: calc1)
.decimalNumberByRoundingAccordingToBehavior(behavior)
calcDN.stringValue // "56473.32"
I do not know of any method of improving the accuracy of the actual computations when using double representation.
I'd recommend rounding the number based on the number of digits in your double so that the NSDecimalNumber is truncated to only show the appropriate number of digits, thus eliminating the digits formed by potential error, ex:
// Get the number of decimal digits in the double
int digits = [self countDigits:calc1];
// Round based on the number of decimal digits in the double
NSDecimalNumberHandler *behavior = [NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundDown scale:digits raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO];
NSDecimalNumber *calcDN = (NSDecimalNumber *)[NSDecimalNumber numberWithDouble:calc1];
calcDN = [calcDN decimalNumberByRoundingAccordingToBehavior:behavior];
I've adapted the countDigits: method from this answer:
- (int)countDigits:(double)num {
int rv = 0;
const double insignificantDigit = 18; // <-- since you want 18 significant digits
double intpart, fracpart;
fracpart = modf(num, &intpart); // <-- Breaks num into an integral and a fractional part.
// While the fractional part is greater than 0.0000001f,
// multiply it by 10 and count each iteration
while ((fabs(fracpart) > 0.0000001f) && (rv < insignificantDigit)) {
num *= 10;
fracpart = modf(num, &intpart);
rv++;
}
return rv;
}

How to compare decimal values

I have a problem with comparison two decimal values.
I have a text field that contains number like 0.123456 and NSNumber that contains 0.000001.
Maximum fraction digits of both is 6. Minimum - 0
I've tried to do it like that:
NSNumberFormatter *decimalFormatter = [[NSNumberFormatter alloc] init];
[decimalFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
[decimalFormatter setMaximumFractionDigits:6];
double sum = [[decimalFormatter numberFromString:self.summTextField.text] doubleValue];
if (self.minSum != nil) {
if (sum < [self.minSum doubleValue]) {
return NO;
}
}
But i have a problem, that sometimes 0.123456 = 0,123455999... or 0,123456000000...01
For example #0.000001 doubleValue < #0.000001 doubleValue - TRUE.
How can I compare to NSNumber with a fractional part, to be sure that it will be correct?
Thanks in advance.
Create extension to decimal for rounding
extension Decimal {
func rounded(toDecimalPlace digit: Int = 2) -> Decimal {
var initialDecimal = self
var roundedDecimal = Decimal()
NSDecimalRound(&roundedDecimal, &initialDecimal, digit, .plain)
return roundedDecimal
}
}
let value1 = Decimal(2.34999999).rounded(toDecimalPlace: 4)
let value2 = Decimal(2.34999989).rounded(toDecimalPlace: 4)
print(value1.isEqual(to: value2))
this results in TRUE
You can round your value, if you worried about fractional part...
Something like this:
-(double)RoundNormal:(double) value :(int) digit
{
value = round(value * pow(10, digit));
return value / pow(10, digit);
}
And then compare it.
You can simply put the test otherwise if you do not want to bother much
if(abs(x-y) < 0.0001)
This should solve it:
NSNumberFormatter *decimalFormatter = [[NSNumberFormatter alloc] init];
[decimalFormatter setNumberStyle: NSNumberFormatterDecimalStyle];
[decimalFormatter setMaximumFractionDigits:6];
[decimalFormatter setMinimumFractionDigits:6];
[formatter setRoundingMode:NSNumberFormatterRoundHalfUp];
[formatter setRoundingIncrement:[NSNumber numberWithDouble:0.000001]]
Use the NSDecimalNumber class - see the guide Number and Values Programming Topics
This is how NSDecimal numbers are compared in iOS:
if ( [x compare:y] == NSOrderedSame ){
// If x is equal to y then your code here..
}
if([x compare:y] == NSOrderedDescending){
// If x is descendant to y then your code here..
}
if([x compare:y] == NSOrderedAscending){
// If x is ascendant to y then your code here..
}

iOS: calculating the remainder of a division for a long number

I have got an NSString * with for example the following numbers #"182316110006010135232100" and i need to do a calculation with this complete value. I have tried multiple types of number systems on iOS SDK for example Int, Float, etc. But because of the amount of bits it changes the number when i change the StringValue to for example an IntValue.
I need to do the following sum with this complete value: mod(digit, 97);
I have checked with for as far i know the longest type of number in Objective-C Long Long:
long long digit = [(NSString *)shouldBechecksum longLongValue];
And need to do the following calculation:
mod(digit, 97);
Now i get strange results because it does the sum with max version of the number. I need it to do this sum:
mod(182316110006010135232100, 97);
How can i do this calculation correctly?
Thanks!
You can use NSDecimalNumber class for precision up to 38 digits. To obtain the mod, just use this formula with the corresponding NSDecimalNumber methods you'll find explained in the documentation.
Mod = digit - int(digit/97)
This is because NSDecimalNumber can only do the basic operations, you have to obtain the mod as we did in school.
From Apple documentation:
NSDecimalNumber, an immutable subclass of NSNumber, provides an object-oriented wrapper for doing base-10 arithmetic. An instance can represent any number that can be expressed as mantissa x 10^exponent where mantissa is a decimal integer up to 38 digits long, and exponent is an integer from –128 through 127.
Fixed Thanks!
NSDecimalNumber *bigDecimal = [NSDecimalNumber decimalNumberWithString:shouldBechecksum];
NSDecimalNumber *divisor = [NSDecimalNumber decimalNumberWithDecimal:[[NSNumber numberWithDouble:97] decimalValue]];
NSDecimalNumber *quotient = [bigDecimal decimalNumberByDividingBy:divisor withBehavior:[NSDecimalNumberHandler decimalNumberHandlerWithRoundingMode:NSRoundDown scale:0 raiseOnExactness:NO raiseOnOverflow:NO raiseOnUnderflow:NO raiseOnDivideByZero:NO]];
NSDecimalNumber *subtractAmount = [quotient decimalNumberByMultiplyingBy:divisor];
NSDecimalNumber *remainder = [bigDecimal decimalNumberBySubtracting:subtractAmount];
int checkSum = 98 - [remainder intValue];
I have done a little test with the following code snippet:
NSString *digitStr = #"182316110006010135232100";
long long digit = [(NSString *)digitStr longLongValue];
short checksum = digit % 97;
NSLog(#"%#, %lli, %lli, %i", digitStr, LONG_LONG_MAX, digit, checksum);
The result was:
182316110006010135232100, 9223372036854775807, 9223372036854775807, 78
This means that your value passes the LONG_LONG_MAX value. So, your problem is not feasible this way.
Remark: apparently Objective C puts the value closest to your number in the variabel digit, being LONG_LONG_MAX.
I guess you will have to find some kind of solution for even longer numbers to do what you want to do. Maybe NSDecimalNumber.
Kind regards,
PF

iOS - %G float can only handle six numbers

Why does the %g format for strings only handle six numbers in a float and after that it turns into scientific notation? Is there any other way of displaying a float with something similar to the %g format but allows more than six numbers?
EDIT: I have figured out %g with precision i.e turning %g into %.Xg where x is the specified number of significant digits. But it doesnt help me in this situation:
-(IBAction)numberPressed:(id)sender {
if (decimalChecker == 1) {
currentDecimal = currentDecimal*10+ (float)[sender tag];
decimaledNumberString = [[NSString alloc] initWithFormat:#"%.17g.%.17g", currentNumber, currentDecimal];
calculatorScreen.text = decimaledNumberString;
currentDecimaledNumber = [decimaledNumberString floatValue];
NSLog(#"regular");
} else {
currentNumber = currentNumber*10+ (float)[sender tag];
calculatorScreen.text = [[NSString alloc] initWithFormat:#"%.17g", currentNumber];
NSLog(#"regular");
}
}
If I press "5" eight times instead of 55555555, I get 55551782 or something similar. How can I fix it to where I get the desired eight fives instead of the crazy number?
Insert a period and a numeral to specify the maximum number of significant digits you would like displayed, such as %.17g for 17 significant digits. As you discovered, the default is six.
According to http://developer.apple.com/library/ios/#documentation/cocoa/Conceptual/Strings/Articles/FormatStrings.html#//apple_ref/doc/uid/20000943, iOS string formatting uses the same placeholders as C's printf(), which specifies g/G as representing FP values with exponential notation for very large/small values while f only uses non-exponential representation.
http://en.wikipedia.org/wiki/Printf_format_string#Format_placeholders

Resources