iOS convert large numbers to smaller format
-(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) (log10l(num) / 3.f); //log10l(1000)); NSArray* units = @[@"K",@"M",@"G",@"T",@"P",@"E"]; return [NSString stringWithFormat:@"%@%.1f%@",sign, (num / pow(1000, exp)), [units objectAtIndex:(exp-1)]];}
sample usage
NSLog(@"%@",[self suffixNumber:@100]); // 100NSLog(@"%@",[self suffixNumber:@1000]); // 1.0KNSLog(@"%@",[self suffixNumber:@1500]); // 1.5KNSLog(@"%@",[self suffixNumber:@24000]); // 24.0KNSLog(@"%@",[self suffixNumber:@99900]); // 99.9KNSLog(@"%@",[self suffixNumber:@99999]); // 100.0KNSLog(@"%@",[self suffixNumber:@109999]); // 110.0KNSLog(@"%@",[self suffixNumber:@5109999]); // 5.1MNSLog(@"%@",[self suffixNumber:@8465445223]); // 8.5GNSLog(@"%@",[self suffixNumber:[NSNumber numberWithInt:-120]]); // -120NSLog(@"%@",[self suffixNumber:[NSNumber numberWithLong:-5000000]]); // -5.0MNSLog(@"%@",[self suffixNumber:[NSNumber numberWithDouble:-3.5f]]); // -3NSLog(@"%@",[self suffixNumber:[NSNumber numberWithDouble:-4000.63f]]); // -4.0K
[Update]
Swift version below:
func suffixNumber(number:NSNumber) -> NSString { var num:Double = number.doubleValue; let sign = ((num < 0) ? "-" : "" ); num = fabs(num); if (num < 1000.0){ return "\(sign)\(num)"; } let exp:Int = Int(log10(num) / 3.0 ); //log10(1000)); let units:[String] = ["K","M","G","T","P","E"]; let roundedNum:Double = round(10 * num / pow(1000.0,Double(exp))) / 10; return "\(sign)\(roundedNum)\(units[exp-1])";}
sample usage
print(self.suffixNumber(NSNumber(long: 100))); // 100.0print(self.suffixNumber(NSNumber(long: 1000))); // 1.0Kprint(self.suffixNumber(NSNumber(long: 1500))); // 1.5Kprint(self.suffixNumber(NSNumber(long: 24000))); // 24.0Kprint(self.suffixNumber(NSNumber(longLong: 99900))); // 99.9Kprint(self.suffixNumber(NSNumber(longLong: 99999))); // 100.0Kprint(self.suffixNumber(NSNumber(longLong: 109999))); // 110.0Kprint(self.suffixNumber(NSNumber(longLong: 5109999))); // 5.1Kprint(self.suffixNumber(NSNumber(longLong: 8465445223))); // 8.5Gprint(self.suffixNumber(NSNumber(long: -120))); // -120.0print(self.suffixNumber(NSNumber(longLong: -5000000))); // -5.0Mprint(self.suffixNumber(NSNumber(float: -3.5))); // -3.5print(self.suffixNumber(NSNumber(float: -4000.63))); // -4.0K
Hope it helps
Here my version ! Thanks to previous answers.The goals of this version is :
- Have better threshold control because small number details are more important that very big number details
- Use as much as possible
NSNumberFormatter
to avoid location problems (like comma instead of dot in french) - Avoid ".0" and well rounding numbers, which can be customize using
NSNumberFormatterRoundingMode
You can use all wonderful NSNumberFormatter
options to fulfill your needs, see NSNumberFormatter Class Reference
The code (gist):
extension Int { func formatUsingAbbrevation () -> String { let numFormatter = NSNumberFormatter() typealias Abbrevation = (threshold:Double, divisor:Double, suffix:String) let abbreviations:[Abbrevation] = [(0, 1, ""), (1000.0, 1000.0, "K"), (100_000.0, 1_000_000.0, "M"), (100_000_000.0, 1_000_000_000.0, "B")] // you can add more ! let startValue = Double (abs(self)) let abbreviation:Abbrevation = { var prevAbbreviation = abbreviations[0] for tmpAbbreviation in abbreviations { if (startValue < tmpAbbreviation.threshold) { break } prevAbbreviation = tmpAbbreviation } return prevAbbreviation } () let value = Double(self) / abbreviation.divisor numFormatter.positiveSuffix = abbreviation.suffix numFormatter.negativeSuffix = abbreviation.suffix numFormatter.allowsFloats = true numFormatter.minimumIntegerDigits = 1 numFormatter.minimumFractionDigits = 0 numFormatter.maximumFractionDigits = 1 return numFormatter.stringFromNumber(NSNumber (double:value))! }}let testValue:[Int] = [598, -999, 1000, -1284, 9940, 9980, 39900, 99880, 399880, 999898, 999999, 1456384, 12383474]testValue.forEach() { print ("Value : \($0) -> \($0.formatUsingAbbrevation ())")}
Result :
Value : 598 -> 598Value : -999 -> -999Value : 1000 -> 1KValue : -1284 -> -1.3KValue : 9940 -> 9.9KValue : 9980 -> 10KValue : 39900 -> 39.9KValue : 99880 -> 99.9KValue : 399880 -> 0.4MValue : 999898 -> 1MValue : 999999 -> 1MValue : 1456384 -> 1.5MValue : 12383474 -> 12.4M
I had the same issue and ended up using Kyle's approach but unfortunately it breaks when numbers like 120000 are used, showing 12k instead of 120K and I needed to show small numbers like: 1.1K instead of rounding down to 1K.
So here's my edit from Kyle's original idea:
Results:[self abbreviateNumber:987] ---> 987[self abbreviateNumber:1200] ---> 1.2K[self abbreviateNumber:12000] ----> 12K[self abbreviateNumber:120000] ----> 120K[self abbreviateNumber:1200000] ---> 1.2M[self abbreviateNumber:1340] ---> 1.3K[self abbreviateNumber:132456] ----> 132.5K-(NSString *)abbreviateNumber:(int)num {NSString *abbrevNum;float number = (float)num;//Prevent numbers smaller than 1000 to return NULLif (num >= 1000) { NSArray *abbrev = @[@"K", @"M", @"B"]; for (int i = abbrev.count - 1; i >= 0; i--) { // Convert array index to "1000", "1000000", etc int size = pow(10,(i+1)*3); if(size <= number) { // Removed the round and dec to make sure small numbers are included like: 1.1K instead of 1K number = number/size; NSString *numberString = [self floatToString:number]; // Add the letter for the abbreviation abbrevNum = [NSString stringWithFormat:@"%@%@", numberString, [abbrev objectAtIndex:i]]; } }} else { // Numbers like: 999 returns 999 instead of NULL abbrevNum = [NSString stringWithFormat:@"%d", (int)number];}return abbrevNum;}- (NSString *) floatToString:(float) val {NSString *ret = [NSString stringWithFormat:@"%.1f", val];unichar c = [ret characterAtIndex:[ret length] - 1];while (c == 48) { // 0 ret = [ret substringToIndex:[ret length] - 1]; c = [ret characterAtIndex:[ret length] - 1]; //After finding the "." we know that everything left is the decimal number, so get a substring excluding the "." if(c == 46) { // . ret = [ret substringToIndex:[ret length] - 1]; }}return ret;}
I Hope this can help you guys.