How to compare decimal values - ios

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..
}

Related

How to truncate fraction using NSNumberFormatter

How can I format decimal values with next rules
0.12345 => 0.12
10.12345 => 10.12
100.12345 => 100.1
1000.12345 => 1,000
Please, notice that idea is to take the same space.
C style printf allows to do this using something 4.2%f plus somehow thousands separator but I don't get how to reflect it in Cocoa.
UPDATE
If you didn't notice example says that 0.12345 transformed to 0.12 not to 0.1234.
Since NSNumberFormatter ignores maximumFractionDigits when significant mode enabled I created custom class
class TRNumberFormatter: NSNumberFormatter {
override func stringFromNumber(number: NSNumber) -> String? {
let digitsCount = "\(number.integerValue)".characters.count
let value = self.Tround(number.doubleValue, precision: min(self.maximumIntegerDigits - digitsCount, self.maximumFractionDigits))
return super.stringFromNumber(value)
}
func Tround(value: Double, precision: Int) -> Double {
let divisor = pow(10.0, Double(precision))
return round(value * divisor) / divisor
}
}
To use it just
var numberFormatter: NSNumberFormatter = TRNumberFormatter()
numberFormatter.usesGroupingSeparator = true
numberFormatter.maximumIntegerDigits = 4
numberFormatter.minimumIntegerDigits = 1
numberFormatter.maximumFractionDigits = 2
Results
print(numberFormatter.stringFromNumber(0.12345)) // Optional("0.12")
print(numberFormatter.stringFromNumber(10.12345)) // Optional("10.12")
print(numberFormatter.stringFromNumber(100.12345)) // Optional("100.1")
print(numberFormatter.stringFromNumber(1000.12345)) // Optional("1,000")
this comes close:
#import <Foundation/Foundation.h>
int main(int argc, char *argv[]) {
#autoreleasepool {
NSNumberFormatter *nf = [[NSNumberFormatter alloc] init];
nf.usesSignificantDigits = YES;
nf.maximumSignificantDigits = 4;
nf.usesGroupingSeparator = YES;
NSLog(#"%#", [nf stringFromNumber:#(10.23455)]);
}
}
BUT it disregards the maximum of 2 fraction digits the OP wants and setting maximumFractionDigits=2 doesn't work
seems to me we can't emulate this 4.2%f exactly... without subclassing the whole thing

Converting very large NSDecimal to string eg. 400,000,000,000 -> 400 T and so forth

I am making a game that requires me to use very large numbers. I believe I am able to store very large numbers with NSDecimal. However, when displaying the numbers to users I would like to be able to convert the large number to a succinct string that uses characters to signify the value eg. 100,000 -> 100k 1,000,000 -> 1.00M 4,200,000,000 -> 4.20B and so forth going up to extremely large numbers. Is there any built in method for doing so or would I have to use a bunch of
NSDecimalCompare statements to determine the size of the number and convert?
I am hoping to use objective c for the application.
I know that I can use NSString *string = NSDecimalString(&NSDecimal, _usLocale); to convert to a string could I then do some type of comparison on this string to get the result I'm looking for?
Use this method to convert your number into a smaller format just as you need:
-(NSString*) suffixNumber:(NSNumber*)number
{
if (!number)
return #"";
long long num = [number longLongValue];
int s = ( (num < 0) ? -1 : (num > 0) ? 1 : 0 );
NSString* sign = (s == -1 ? #"-" : #"" );
num = llabs(num);
if (num < 1000)
return [NSString stringWithFormat:#"%#%lld",sign,num];
int exp = (int) (log(num) / 3.f); //log(1000));
NSArray* units = #[#"K",#"M",#"G",#"T",#"P",#"E"];
return [NSString stringWithFormat:#"%#%.1f%#",sign, (num / pow(1000, exp)), [units objectAtIndex:(exp-1)]];
}
Some sample examples:
NSLog(#"%#",[self suffixNumber:#99999]); // 100.0K
NSLog(#"%#",[self suffixNumber:#5109999]); // 5.1M
Source
Solved my issue: Can only be used if you know that your NSDecimal that you are trying to format will only be a whole number without decimals so make sure you round when doing any math on the NSDecimals.
-(NSString *)returnFormattedString:(NSDecimal)nsDecimalToFormat{
NSMutableArray *formatArray = [NSMutableArray arrayWithObjects:#"%.2f",#"%.1f",#"%.0f",nil];
NSMutableArray *suffixes = [NSMutableArray arrayWithObjects:#"k",#"M",#"B",#"T",#"Qa",#"Qi",#"Sx",#"Sp",#"Oc",#"No",#"De",#"Ud",#"Dud",#"Tde",#"Qde",#"Qid",#"Sxd",#"Spd",#"Ocd",#"Nvd",#"Vi",#"Uvi",#"Dvi",#"Tvi", nil];
int dick = [suffixes count];
NSLog(#"count %i",dick);
NSString *string = NSDecimalString(&nsDecimalToFormat, _usLocale);
NSString *formatedString;
NSUInteger characterCount = [string length];
if (characterCount > 3) {
NSString *trimmedString=[string substringToIndex:3];
float a;
a = 100.00/(pow(10, (characterCount - 4)%3));
int remainder = (characterCount-4)%3;
int suffixIndex = (characterCount + 3 - 1)/3 - 2;
NSLog(#"%i",suffixIndex);
if(suffixIndex < [suffixes count]){
NSString *formatSpecifier = [formatArray[remainder] stringByAppendingString:suffixes[suffixIndex]];
formatedString= [NSString stringWithFormat:formatSpecifier, [trimmedString floatValue] / a];
}
else {
formatedString = #"too Big";
}
}
else{
formatedString = string;
}
return formatedString;
}

NSDecimalNumber rounding

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);

Using NSNumberFormatter to generate number with trailing zeros after decimal

In Swift, how would you create an NSNumberFormatter that would preserve trailing zeros after a decimal (12.000) while also generating a number appropriate for the current locale?
My current code and example output:
let formatter = NSNumberFormatter()
formatter.numberStyle = .DecimalStyle
formatter.locale = NSLocale.currentLocale()
formatter.maximumFractionDigits = 10
var doubleNumString = "12.0"
println(formatter.numberFromString(doubleNumString)) //in English it prints 12, want it to print 12.0
var doubleNumString = "12.000"
println(formatter.numberFromString(doubleNumString)) //prints 12, want it to print 12.000
var doubleNumString = "12.125"
println(formatter.numberFromString(doubleNumString)) //prints 12.125 as expected
var doubleNumString = "1234"
println(formatter.numberFromString(doubleNumString)) //prints 1,234 as expected
I've already coded it such that if the string ends in a decimal ("12.") then it won't use this formatter to generate the number and will instead just display the number then the decimal (but I will need to improve that because some languages read right to left).
One solution would be to check if the string contains a period and if so, check if all digits that follow it are 0, and if so then don't run it through the number formatter and instead run only the int value through the formatter then append/prepend the decimal followed by the appropriate number of 0's.
Is there a better/cleaner solution?
As mentioned by Martin R, you can set the minimumFractionDigits and maximumFractionDigits to the same number which will enforce that many fraction digits always be displayed. To know how many to display you need to take a substring after the decimal to the end and count its elements. To know whether or not all of the fraction digits are 0's, I created a helper method that converts that substring to a number and if it equals 0 then you know they were all 0's.
Unfortunately you need to convert the string to a localized number using a couple different NSNumberFormatters based on the original string number. So if it does contain a decimal and everything after it is a 0 then you need to create a different formatter, convert the string to a number, then convert that number to a string in order to display it respecting the user's locale. Otherwise you can just use your original number formatter.
This function takes care of your requirement. pass same for & from locale (e.g. en_US)
+ (NSString*) stringForString:(NSString*) string forLocale:(NSString*) toLocaleCode fromLocal:(NSString*) fromLocaleCode {
NSLocale *fromLocale = [[NSLocale alloc] initWithLocaleIdentifier:fromLocaleCode];
NSNumberFormatter *sourceFormatter = [[NSNumberFormatter alloc] init];
[sourceFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[sourceFormatter setUsesGroupingSeparator:NO];
[sourceFormatter setLocale:fromLocale];
NSNumber *localizedNumber = [sourceFormatter numberFromString:string];
if (!localizedNumber) {
return string;
}
NSLocale *toLocale = [[NSLocale alloc] initWithLocaleIdentifier:toLocaleCode];
NSNumberFormatter *destinationFormatter = [[NSNumberFormatter alloc] init];
[destinationFormatter setNumberStyle:NSNumberFormatterDecimalStyle];
[destinationFormatter setUsesGroupingSeparator:NO];
[destinationFormatter setLocale:toLocale];
NSString *localizedString = [destinationFormatter stringFromNumber:localizedNumber];
//add the zeros which were dropped because of the sourceDecimalString number conversion e.g. 0.20 is converted to 0.2
if (localizedString.length < string.length) {
NSRange rangeOfDecimal = [string rangeOfString:sourceFormatter.decimalSeparator];
if (rangeOfDecimal.location != NSNotFound) {
NSString* sourceDecimalString = [string substringFromIndex:rangeOfDecimal.location];
rangeOfDecimal = [localizedString rangeOfString:destinationFormatter.decimalSeparator];
if (rangeOfDecimal.location != NSNotFound) {
NSString* destinationDecimalString = [localizedString substringFromIndex:rangeOfDecimal.location];
if (destinationDecimalString.length < sourceDecimalString.length) {
int difference = sourceDecimalString.length - destinationDecimalString.length;
int toalDecimalDigits = (destinationDecimalString.length - 1) + difference; //-1 to remove '.'
destinationFormatter.minimumFractionDigits = toalDecimalDigits;
destinationFormatter.maximumFractionDigits = toalDecimalDigits;
localizedString = [destinationFormatter stringFromNumber:localizedNumber];
}
}
else{//this indicates no decimal separator in the return string
int toalDecimalDigits = (sourceDecimalString.length - 1); //-1 to remove '.'
destinationFormatter.minimumFractionDigits = toalDecimalDigits;
destinationFormatter.maximumFractionDigits = toalDecimalDigits;
localizedString = [destinationFormatter stringFromNumber:localizedNumber];
}
}
}
return localizedString;
}

What's the best way to convert an inputed numeric string to a NSDecimalNumber, considering the number of fraction digits for a given locale?

Is there an API for this? Or maybe a better way of doing it?
Here's what I'm trying to acomplish:
In is a numeric string. Out, is a NSDecimalNumber
// this one is for US:
in: 1 out: 0.01
in: 12 out: 0.12
in: 123 out: 1.12
// a diferent locale might have a diferent maximumFractionDigits, like 1
in: 1 out: 0.1
in: 12 out: 1.2
in: 123 out: 12.3
// other locales might have 0, or 3 fraction digits.
Here's how I have it:
// Clear leading zeros
NSNumber *number = [formatter numberFromString:numericString];
numericString = [formatter stringFromNumber:number];
if (maximumFractionDigits == 0) {
return [NSDecimalNumber decimalNumberWithString:numericString];
}
else if (numericString.length <= _currencyFormatter.maximumFractionDigits) {
NSString *zeros = #"";
for (NSInteger i = numericString.length; i < maximumFractionDigits ; i++) {
zeros = [zeros stringByAppendingString:#"0"];
}
return [NSDecimalNumber decimalNumberWithString:[NSString stringWithFormat:#"0.%#%#",zeros,numericString]];
} else {
NSString *decimalString = [NSString stringWithFormat:#"%#.%#",
[_rateInput substringToIndex:numericString.length - maximumFractionDigits],
[_rateInput substringFromIndex:numericString.length - maximumFractionDigits]];
return [NSDecimalNumber decimalNumberWithString: decimalString];
}
While this does seem to work, I was wondering if there is an API for this, or a more simple, less error prone way of doing it?

Resources