I'd like to format following numbers into the numbers next to them with java:
我想把下面的数字格式化成与java相邻的数字:
1000 to 1k
5821 to 5.8k
10500 to 10k
101800 to 101k
2000000 to 2m
7800000 to 7.8m
92150000 to 92m
123200000 to 123m
The number on the right will be long or integer the number on the left will be string. How should I approach this. I already did little algorithm for this but I thought there might be already something invented out there that does nicer job at it and doesn't require additional testing if I start dealing with billions and trillions :)
右边的数字是长或整数,左边的数字是字符串。我该如何处理这个问题呢?我已经为这个做了一些算法但我想可能已经有一些发明出来了在它上面做的更好而且不需要额外的测试如果我开始处理数十亿和万亿:)
Additional Requirements:
附加要求:
- The format should have maximum of 4 characters
- 该格式最多有4个字符。
- The above means 1.1k is OK 11.2k is not. Same for 7.8m is OK 19.1m is not. Only one digit before decimal point is allowed to have decimal point. Two digits before decimal point means not digits after decimal point.
- 上面的意思是1。1k等于11。2k不是。同样的,780万是可以的。小数点前只有一位数允许有小数点。小数点后的两位数字,指的不是小数点后的数字。
- No rounding is necessary. (Numbers being displayed with k and m appended are more of analog gauge indicating approximation not precise article of logic. Hence rounding is irrelevant mainly due to nature of variable than can increase or decrees several digits even while you are looking at the cached result.)
- 不需要舍入。(用k和m表示的数字更多的是模拟量,表示近似而不是精确的逻辑。因此,四舍五入是不相关的,主要是由于变量的性质,甚至在您查看缓存的结果时,还可以增加或执行数个数字。
19 个解决方案
#1
90
Here is a solution that works for any long value and that I find quite readable (the core logic is done in the bottom three lines of the format
method).
这里有一个对任何长值都有效的解决方案,并且我认为它是可读的(核心逻辑是在格式方法的底部三行中完成的)。
It leverages TreeMap
to find the appropriate suffix. It is surprisingly more efficient than a previous solution I wrote that was using arrays and was more difficult to read.
它利用TreeMap来找到合适的后缀。令人惊讶的是,它比我以前编写的使用数组的解决方案更有效,而且阅读起来更困难。
private static final NavigableMap<Long, String> suffixes = new TreeMap<> ();
static {
suffixes.put(1_000L, "k");
suffixes.put(1_000_000L, "M");
suffixes.put(1_000_000_000L, "G");
suffixes.put(1_000_000_000_000L, "T");
suffixes.put(1_000_000_000_000_000L, "P");
suffixes.put(1_000_000_000_000_000_000L, "E");
}
public static String format(long value) {
//Long.MIN_VALUE == -Long.MIN_VALUE so we need an adjustment here
if (value == Long.MIN_VALUE) return format(Long.MIN_VALUE + 1);
if (value < 0) return "-" + format(-value);
if (value < 1000) return Long.toString(value); //deal with easy case
Entry<Long, String> e = suffixes.floorEntry(value);
Long divideBy = e.getKey();
String suffix = e.getValue();
long truncated = value / (divideBy / 10); //the number part of the output times 10
boolean hasDecimal = truncated < 100 && (truncated / 10d) != (truncated / 10);
return hasDecimal ? (truncated / 10d) + suffix : (truncated / 10) + suffix;
}
Test code
public static void main(String args[]) {
long[] numbers = {0, 5, 999, 1_000, -5_821, 10_500, -101_800, 2_000_000, -7_800_000, 92_150_000, 123_200_000, 9_999_999, 999_999_999_999_999_999L, 1_230_000_000_000_000L, Long.MIN_VALUE, Long.MAX_VALUE};
String[] expected = {"0", "5", "999", "1k", "-5.8k", "10k", "-101k", "2M", "-7.8M", "92M", "123M", "9.9M", "999P", "1.2P", "-9.2E", "9.2E"};
for (int i = 0; i < numbers.length; i++) {
long n = numbers[i];
String formatted = format(n);
System.out.println(n + " => " + formatted);
if (!formatted.equals(expected[i])) throw new AssertionError("Expected: " + expected[i] + " but found: " + formatted);
}
}
#2
86
I know, this looks more like a C program, but it's super lightweight!
我知道,这看起来更像一个C程序,但是它是超级轻量级的!
public static void main(String args[]) {
long[] numbers = new long[]{1000, 5821, 10500, 101800, 2000000, 7800000, 92150000, 123200000, 9999999};
for(long n : numbers) {
System.out.println(n + " => " + coolFormat(n, 0));
}
}
private static char[] c = new char[]{'k', 'm', 'b', 't'};
/**
* Recursive implementation, invokes itself for each factor of a thousand, increasing the class on each invokation.
* @param n the number to format
* @param iteration in fact this is the class from the array c
* @return a String representing the number n formatted in a cool looking way.
*/
private static String coolFormat(double n, int iteration) {
double d = ((long) n / 100) / 10.0;
boolean isRound = (d * 10) %10 == 0;//true if the decimal part is equal to 0 (then it's trimmed anyway)
return (d < 1000? //this determines the class, i.e. 'k', 'm' etc
((d > 99.9 || isRound || (!isRound && d > 9.99)? //this decides whether to trim the decimals
(int) d * 10 / 10 : d + "" // (int) d * 10 / 10 drops the decimal
) + "" + c[iteration])
: coolFormat(d, iteration+1));
}
It outputs:
输出:
1000 => 1k
5821 => 5.8k
10500 => 10k
101800 => 101k
2000000 => 2m
7800000 => 7.8m
92150000 => 92m
123200000 => 123m
9999999 => 9.9m
#3
40
Here a solution that makes use of DecimalFormat's engineering notation:
这里有一个解决方案,利用了DecimalFormat的工程符号:
public static void main(String args[]) {
long[] numbers = new long[]{7, 12, 856, 1000, 5821, 10500, 101800, 2000000, 7800000, 92150000, 123200000, 9999999};
for(long number : numbers) {
System.out.println(number + " = " + format(number));
}
}
private static String[] suffix = new String[]{"","k", "m", "b", "t"};
private static int MAX_LENGTH = 4;
private static String format(double number) {
String r = new DecimalFormat("##0E0").format(number);
r = r.replaceAll("E[0-9]", suffix[Character.getNumericValue(r.charAt(r.length() - 1)) / 3]);
while(r.length() > MAX_LENGTH || r.matches("[0-9]+\\.[a-z]")){
r = r.substring(0, r.length()-2) + r.substring(r.length() - 1);
}
return r;
}
Output:
输出:
7 = 7
12 = 12
856 = 856
1000 = 1k
5821 = 5.8k
10500 = 10k
101800 = 102k
2000000 = 2m
7800000 = 7.8m
92150000 = 92m
123200000 = 123m
9999999 = 10m
#4
16
Need some improvement, but: StrictMath to the rescue!
You can put the suffix in a String or array and fetch'em based on power, or something like that.
The division can also be managed around the power, i think almost everything is about the power value. Hope it helps!
需要一些改进,但是:严格的数学救援!你可以把后缀放在一个字符串或数组中,然后以power为基础,或者类似的东西。这个部门也可以在权力范围内管理,我认为几乎所有的事情都是关于权力价值的。希望它可以帮助!
public static String formatValue(double value) {
int power;
String suffix = " kmbt";
String formattedNumber = "";
NumberFormat formatter = new DecimalFormat("#,###.#");
power = (int)StrictMath.log10(value);
value = value/(Math.pow(10,(power/3)*3));
formattedNumber=formatter.format(value);
formattedNumber = formattedNumber + suffix.charAt(power/3);
return formattedNumber.length()>4 ? formattedNumber.replaceAll("\\.[0-9]+", "") : formattedNumber;
}
outputs:
输出:
999
1.2k
98k
911k
1.1m
11b
712b
34t999 . 1.2k 98k 911k 110 b 712b 34t。
#5
15
Problems with Current Answers
- Many of the current solutions are using these prefixes k=103, m=106, b=109, t=1012. However, according to various sources, the correct prefixes are k=103, M=106, G=109, T=1012
- 当前的许多解决方案都使用这些前缀k=103, m=106, b=109, t=1012。然而,根据各种来源,正确的前缀是k=103, M=106, G=109, T=1012。
- Lack of support for negative numbers (or at least a lack of tests demonstrating that negative numbers are supported)
- 缺乏对负数的支持(或者至少缺少证明负数的测试)
- Lack of support for the inverse operation, e.g. converting 1.1k to 1100 (though this is outside the scope of the original question)
- 对逆操作缺乏支持,例如将1.1k转换为1100(尽管这超出了原始问题的范围)
Java Solution
This solution (an extension of this answer) addresses the above issues.
此解决方案(此答案的扩展)解决了上述问题。
import org.apache.commons.lang.math.NumberUtils;
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.regex.Pattern;
/**
* Converts a number to a string in <a href="http://en.wikipedia.org/wiki/Metric_prefix">metric prefix</a> format.
* For example, 7800000 will be formatted as '7.8M'. Numbers under 1000 will be unchanged. Refer to the tests for further examples.
*/
class RoundedMetricPrefixFormat extends Format {
private static final String[] METRIC_PREFIXES = new String[]{"", "k", "M", "G", "T"};
/**
* The maximum number of characters in the output, excluding the negative sign
*/
private static final Integer MAX_LENGTH = 4;
private static final Pattern TRAILING_DECIMAL_POINT = Pattern.compile("[0-9]+\\.[kMGT]");
private static final Pattern METRIC_PREFIXED_NUMBER = Pattern.compile("\\-?[0-9]+(\\.[0-9])?[kMGT]");
@Override
public StringBuffer format(Object obj, StringBuffer output, FieldPosition pos) {
Double number = Double.valueOf(obj.toString());
// if the number is negative, convert it to a positive number and add the minus sign to the output at the end
boolean isNegative = number < 0;
number = Math.abs(number);
String result = new DecimalFormat("##0E0").format(number);
Integer index = Character.getNumericValue(result.charAt(result.length() - 1)) / 3;
result = result.replaceAll("E[0-9]", METRIC_PREFIXES[index]);
while (result.length() > MAX_LENGTH || TRAILING_DECIMAL_POINT.matcher(result).matches()) {
int length = result.length();
result = result.substring(0, length - 2) + result.substring(length - 1);
}
return output.append(isNegative ? "-" + result : result);
}
/**
* Convert a String produced by <tt>format()</tt> back to a number. This will generally not restore
* the original number because <tt>format()</tt> is a lossy operation, e.g.
*
* <pre>
* {@code
* def formatter = new RoundedMetricPrefixFormat()
* Long number = 5821L
* String formattedNumber = formatter.format(number)
* assert formattedNumber == '5.8k'
*
* Long parsedNumber = formatter.parseObject(formattedNumber)
* assert parsedNumber == 5800
* assert parsedNumber != number
* }
* </pre>
*
* @param source a number that may have a metric prefix
* @param pos if parsing succeeds, this should be updated to the index after the last parsed character
* @return a Number if the the string is a number without a metric prefix, or a Long if it has a metric prefix
*/
@Override
public Object parseObject(String source, ParsePosition pos) {
if (NumberUtils.isNumber(source)) {
// if the value is a number (without a prefix) don't return it as a Long or we'll lose any decimals
pos.setIndex(source.length());
return toNumber(source);
} else if (METRIC_PREFIXED_NUMBER.matcher(source).matches()) {
boolean isNegative = source.charAt(0) == '-';
int length = source.length();
String number = isNegative ? source.substring(1, length - 1) : source.substring(0, length - 1);
String metricPrefix = Character.toString(source.charAt(length - 1));
Number absoluteNumber = toNumber(number);
int index = 0;
for (; index < METRIC_PREFIXES.length; index++) {
if (METRIC_PREFIXES[index].equals(metricPrefix)) {
break;
}
}
Integer exponent = 3 * index;
Double factor = Math.pow(10, exponent);
factor *= isNegative ? -1 : 1;
pos.setIndex(source.length());
Float result = absoluteNumber.floatValue() * factor.longValue();
return result.longValue();
}
return null;
}
private static Number toNumber(String number) {
return NumberUtils.createNumber(number);
}
}
Groovy Solution
The solution was originally written in Groovy as shown below.
解决方案最初是用Groovy编写的,如下所示。
import org.apache.commons.lang.math.NumberUtils
import java.text.DecimalFormat
import java.text.FieldPosition
import java.text.Format
import java.text.ParsePosition
import java.util.regex.Pattern
/**
* Converts a number to a string in <a href="http://en.wikipedia.org/wiki/Metric_prefix">metric prefix</a> format.
* For example, 7800000 will be formatted as '7.8M'. Numbers under 1000 will be unchanged. Refer to the tests for further examples.
*/
class RoundedMetricPrefixFormat extends Format {
private static final METRIC_PREFIXES = ["", "k", "M", "G", "T"]
/**
* The maximum number of characters in the output, excluding the negative sign
*/
private static final Integer MAX_LENGTH = 4
private static final Pattern TRAILING_DECIMAL_POINT = ~/[0-9]+\.[kMGT]/
private static final Pattern METRIC_PREFIXED_NUMBER = ~/\-?[0-9]+(\.[0-9])?[kMGT]/
@Override
StringBuffer format(Object obj, StringBuffer output, FieldPosition pos) {
Double number = obj as Double
// if the number is negative, convert it to a positive number and add the minus sign to the output at the end
boolean isNegative = number < 0
number = Math.abs(number)
String result = new DecimalFormat("##0E0").format(number)
Integer index = Character.getNumericValue(result.charAt(result.size() - 1)) / 3
result = result.replaceAll("E[0-9]", METRIC_PREFIXES[index])
while (result.size() > MAX_LENGTH || TRAILING_DECIMAL_POINT.matcher(result).matches()) {
int length = result.size()
result = result.substring(0, length - 2) + result.substring(length - 1)
}
output << (isNegative ? "-$result" : result)
}
/**
* Convert a String produced by <tt>format()</tt> back to a number. This will generally not restore
* the original number because <tt>format()</tt> is a lossy operation, e.g.
*
* <pre>
* {@code
* def formatter = new RoundedMetricPrefixFormat()
* Long number = 5821L
* String formattedNumber = formatter.format(number)
* assert formattedNumber == '5.8k'
*
* Long parsedNumber = formatter.parseObject(formattedNumber)
* assert parsedNumber == 5800
* assert parsedNumber != number
* }
* </pre>
*
* @param source a number that may have a metric prefix
* @param pos if parsing succeeds, this should be updated to the index after the last parsed character
* @return a Number if the the string is a number without a metric prefix, or a Long if it has a metric prefix
*/
@Override
Object parseObject(String source, ParsePosition pos) {
if (source.isNumber()) {
// if the value is a number (without a prefix) don't return it as a Long or we'll lose any decimals
pos.index = source.size()
toNumber(source)
} else if (METRIC_PREFIXED_NUMBER.matcher(source).matches()) {
boolean isNegative = source[0] == '-'
String number = isNegative ? source[1..-2] : source[0..-2]
String metricPrefix = source[-1]
Number absoluteNumber = toNumber(number)
Integer exponent = 3 * METRIC_PREFIXES.indexOf(metricPrefix)
Long factor = 10 ** exponent
factor *= isNegative ? -1 : 1
pos.index = source.size()
(absoluteNumber * factor) as Long
}
}
private static Number toNumber(String number) {
NumberUtils.createNumber(number)
}
}
Tests (Groovy)
The tests are written in Groovy but can be used to verify either either the Java or Groovy class (because they both have the same name and API).
这些测试是用Groovy编写的,但是可以用来验证Java或Groovy类(因为它们都有相同的名称和API)。
import java.text.Format
import java.text.ParseException
class RoundedMetricPrefixFormatTests extends GroovyTestCase {
private Format roundedMetricPrefixFormat = new RoundedMetricPrefixFormat()
void testNumberFormatting() {
[
7L : '7',
12L : '12',
856L : '856',
1000L : '1k',
(-1000L) : '-1k',
5821L : '5.8k',
10500L : '10k',
101800L : '102k',
2000000L : '2M',
7800000L : '7.8M',
(-7800000L): '-7.8M',
92150000L : '92M',
123200000L : '123M',
9999999L : '10M',
(-9999999L): '-10M'
].each { Long rawValue, String expectedRoundValue ->
assertEquals expectedRoundValue, roundedMetricPrefixFormat.format(rawValue)
}
}
void testStringParsingSuccess() {
[
'7' : 7,
'8.2' : 8.2F,
'856' : 856,
'-856' : -856,
'1k' : 1000,
'5.8k' : 5800,
'-5.8k': -5800,
'10k' : 10000,
'102k' : 102000,
'2M' : 2000000,
'7.8M' : 7800000L,
'92M' : 92000000L,
'-92M' : -92000000L,
'123M' : 123000000L,
'10M' : 10000000L
].each { String metricPrefixNumber, Number expectedValue ->
def parsedNumber = roundedMetricPrefixFormat.parseObject(metricPrefixNumber)
assertEquals expectedValue, parsedNumber
}
}
void testStringParsingFail() {
shouldFail(ParseException) {
roundedMetricPrefixFormat.parseObject('notNumber')
}
}
}
#6
10
The ICU lib has a rule based formatter for numbers, which can be used for number spellout etc. I think using ICU would give you a readable and maintanable solution.
ICU lib有一个用于数字的规则格式化程序,可以用于数字拼写等。我认为使用ICU可以给您一个可读的和可维护的解决方案。
[Usage]
(使用)
The right class is RuleBasedNumberFormat. The format itself can be stored as separate file (or as String constant, IIRC).
正确的类是RuleBasedNumberFormat。格式本身可以存储为单独的文件(或作为字符串常量,IIRC)。
Example from http://userguide.icu-project.org/formatparse/numbers
来自http://userguide.icu-project.org/formatparse/numbers的例子
double num = 2718.28;
NumberFormat formatter =
new RuleBasedNumberFormat(RuleBasedNumberFormat.SPELLOUT);
String result = formatter.format(num);
System.out.println(result);
The same page shows Roman numerals, so I guess your case should be possible, too.
相同的页面显示罗马数字,所以我想您的情况也应该是可能的。
#7
7
Important: Answers casting to double
will fail for numbers like 99999999999999999L
and return 100P
instead of 99P
because double
uses the IEEE
standard:
重要的:对double的回答将会失败,例如9999999999999999999l,并返回100P而不是99P,因为double使用了IEEE标准:
If a decimal string with at most 15 significant digits is converted to IEEE 754 double precision representation and then converted back to a string with the same number of significant digits, then the final string should match the original. [
long
has up to 19 significant digits.]如果以15个有效数字的十进制字符串转换为IEEE 754双精度表示,然后转换为具有相同数目的有效数字的字符串,那么最后的字符串应该与原来的字符串匹配。[长到19位有效数字]
System.out.println((long)(double)99999999999999992L); // 100000000000000000
System.out.println((long)(double)99999999999999991L); // 99999999999999984
// it is even worse for the logarithm:
System.out.println(Math.log10(99999999999999600L)); // 17.0
System.out.println(Math.log10(99999999999999500L)); // 16.999999999999996
This solution cuts off unwanted digits and works for all long
values. Simple but performant implementation (comparison below). -120k can't be expressed with 4 characters, even -0.1M is too long, that's why for negative numbers 5 characters have to be okay:
这个解决方案可以减少不必要的数字,并为所有的长值工作。简单但性能实现(比较之下)。-120k不能用4个字符表示,即使是-0。1m也太长了,这就是为什么负数的5个字符必须是可以的:
private static final char[] magnitudes = {'k', 'M', 'G', 'T', 'P', 'E'}; // enough for long
public static final String convert(long number) {
String ret;
if (number >= 0) {
ret = "";
} else if (number <= -9200000000000000000L) {
return "-9.2E";
} else {
ret = "-";
number = -number;
}
if (number < 1000)
return ret + number;
for (int i = 0; ; i++) {
if (number < 10000 && number % 1000 >= 100)
return ret + (number / 1000) + '.' + ((number % 1000) / 100) + magnitudes[i];
number /= 1000;
if (number < 1000)
return ret + number + magnitudes[i];
}
}
The test in the else if
at the beginning is necessairy because the min is -(2^63)
and the max is (2^63)-1
and therefore the assignment number = -number
would fail if number == Long.MIN_VALUE
. If we have to do a check, then we can as well include as many numbers as possible instead of just checking for number == Long.MIN_VALUE
.
其他的测试如果一开始虽然是大大因为min -(2 ^ 63)和马克斯(2 ^ 63)1,因此作业数量= = = Long.MIN_VALUE数量将会失败如果号码。如果我们要做检查,那么我们可以尽可能地包含尽可能多的数字,而不是仅仅检查number == Long.MIN_VALUE。
The comparison of this implementation with the one who got the most upvotes (said to be the fastest currently) showed that it is more than 5 times faster (it depends on the test settings, but with more numbers the gain gets bigger and this implementation has to do more checks because it handles all cases, so if the other one would be fixed the difference would become even bigger). It is that fast because there are no floating point operations, no logarithm, no power, no recursion, no regex, no sophisticated formatters and minimization of the amount of objects created.
这个实现与人的比较得到了大多数问题(目前是最快的)表明,它是超过5倍(这取决于测试设置,但更多的数字增益变大,该实现必须做更多的检查,因为它处理所有情况下,如果另一个固定的差异会更大)。它的速度很快,因为没有浮点运算,没有对数,没有幂,没有递归,没有regex,没有复杂的格式化,以及最小化创建的对象数量。
Here is the test program:
这是测试程序:
public class Test {
public static void main(String[] args) {
long[] numbers = new long[20000000];
for (int i = 0; i < numbers.length; i++)
numbers[i] = Math.random() < 0.5 ? (long) (Math.random() * Long.MAX_VALUE) : (long) (Math.random() * Long.MIN_VALUE);
System.out.println(convert1(numbers) + " vs. " + convert2(numbers));
}
private static long convert1(long[] numbers) {
long l = System.currentTimeMillis();
for (int i = 0; i < numbers.length; i++)
Converter1.convert(numbers[i]);
return System.currentTimeMillis() - l;
}
private static long convert2(long[] numbers) {
long l = System.currentTimeMillis();
for (int i = 0; i < numbers.length; i++)
Converter2.coolFormat(numbers[i], 0);
return System.currentTimeMillis() - l;
}
}
Possible output: 2309 vs. 11591
(about the same when only using positive numbers and much more extreme when reversing the order of execution, maybe it has something to do with garbage collection)
可能的输出:2309与11591(大约相同,当仅使用正数时,在反转执行顺序时更极端,可能与垃圾收集有关)
#8
7
Here's a short implementation without recursion and just a very small loop. Doesn't work with negative numbers but supports all positive long
s up to Long.MAX_VALUE
:
这里是一个没有递归的简短实现,只是一个很小的循环。不与负数一起工作,但支持所有的正长到长。max_value:
private static final char[] SUFFIXES = {'k', 'm', 'g', 't', 'p', 'e' };
public static String format(long number) {
if(number < 1000) {
// No need to format this
return String.valueOf(number);
}
// Convert to a string
final String string = String.valueOf(number);
// The suffix we're using, 1-based
final int magnitude = (string.length() - 1) / 3;
// The number of digits we must show before the prefix
final int digits = (string.length() - 1) % 3 + 1;
// Build the string
char[] value = new char[4];
for(int i = 0; i < digits; i++) {
value[i] = string.charAt(i);
}
int valueLength = digits;
// Can and should we add a decimal point and an additional number?
if(digits == 1 && string.charAt(1) != '0') {
value[valueLength++] = '.';
value[valueLength++] = string.charAt(1);
}
value[valueLength++] = SUFFIXES[magnitude - 1];
return new String(value, 0, valueLength);
}
Outputs:
输出:
1k
5.8k
10k
101k
2m
7.8m
92m
123m
9.2e (this isLong.MAX_VALUE
)1k 5.8k 101k 101k 2m 78m 92m
I also did some really simple benchmarking (formatting 10 million random longs) and it's considerably faster than Elijah's implementation and slightly faster than assylias' implementation.
我还做了一些非常简单的基准测试(格式化了1000万个随机的longs),它比Elijah的实现要快得多,比assylias的实现要快得多。
Mine: 1137.028 ms
Elijah's: 2664.396 ms
assylias': 1373.473 ms我的是:1137.028以利亚的:2664.396 ms assylias: 1373.473 ms。
#9
7
For anyone that wants to round. This is a great, easy to read solution, that takes advantage of the Java.Lang.Math library
对于任何想圆的人来说。这是一个很好的、易于阅读的解决方案,它利用了Java.Lang。数学库
public static String formatNumberExample(Number number) {
char[] suffix = {' ', 'k', 'M', 'B', 'T', 'P', 'E'};
long numValue = number.longValue();
int value = (int) Math.floor(Math.log10(numValue));
int base = value / 3;
if (value >= 3 && base < suffix.length) {
return new DecimalFormat("~#0.0").format(numValue / Math.pow(10, base * 3)) + suffix[base];
} else {
return new DecimalFormat("#,##0").format(numValue);
}
}
#10
5
My Java is rusty, but here's how I'd implement it in C#:
我的Java已经生锈了,但下面是我如何用c#实现它:
private string FormatNumber(double value)
{
string[] suffixes = new string[] {" k", " m", " b", " t", " q"};
for (int j = suffixes.Length; j > 0; j--)
{
double unit = Math.Pow(1000, j);
if (value >= unit)
return (value / unit).ToString("#,##0.0") + suffixes[--j];
}
return value.ToString("#,##0");
}
It'd be easy to adjust this to use CS kilos (1,024) rather than metric kilos, or to add more units. It formats 1,000 as "1.0 k" rather than "1 k", but I trust that's immaterial.
使用CS公斤(1024)而不是米制公斤,或者增加更多的单位是很容易的。它的格式是“1.0 k”,而不是“1k”,但我相信这是不重要的。
To meet the more specific requirement "no more than four characters", remove the spaces before the suffixes and adjust the middle block like this:
为了满足更具体的要求“不超过四个字符”,在后缀前删除空格,并调整中间块如下:
if (value >= unit)
{
value /= unit;
return (value).ToString(value >= unit * 9.95 ? "#,##0" : "#,##0.0") + suffixes[--j];
}
#11
5
I don't know if it's the best approach but, this is what i did.
我不知道这是不是最好的方法,但这就是我所做的。
7=>7
12=>12
856=>856
1000=>1.0k
5821=>5.82k
10500=>10.5k
101800=>101.8k
2000000=>2.0m
7800000=>7.8m
92150000=>92.15m
123200000=>123.2m
9999999=>10.0m
--- Code---
推荐- - - - - - - - - - - -代码
public String Format(Integer number){
String[] suffix = new String[]{"k","m","b","t"};
int size = (number.intValue() != 0) ? (int) Math.log10(number) : 0;
if (size >= 3){
while (size % 3 != 0) {
size = size - 1;
}
}
double notation = Math.pow(10, size);
String result = (size >= 3) ? + (Math.round((number / notation) * 100) / 100.0d)+suffix[(size/3) - 1] : + number + "";
return result
}
#12
5
My favorite. You could use "k" and so on as indicator for decimal too, as common in the electronic domain. This will give you an extra digit without additional space
我最喜欢的。你也可以用“k”作为十进制的指示器,这在电子领域很常见。这将给你一个额外的数字,没有额外的空间。
Second column tries to use as much digits as possible
第二列尽量使用尽可能多的数字。
1000 => 1.0k | 1000
5821 => 5.8k | 5821
10500 => 10k | 10k5
101800 => 101k | 101k
2000000 => 2.0m | 2m
7800000 => 7.8m | 7m8
92150000 => 92m | 92m1
123200000 => 123m | 123m
9999999 => 9.9m | 9m99
This is the code
这是代码
public class HTTest {
private static String[] unit = {"u", "k", "m", "g", "t"};
/**
* @param args
*/
public static void main(String[] args) {
int[] numbers = new int[]{1000, 5821, 10500, 101800, 2000000, 7800000, 92150000, 123200000, 9999999};
for(int n : numbers) {
System.out.println(n + " => " + myFormat(n) + " | " + myFormat2(n));
}
}
private static String myFormat(int pN) {
String str = Integer.toString(pN);
int len = str.length ()-1;
if (len <= 3) return str;
int level = len / 3;
int mode = len % 3;
switch (mode) {
case 0: return str.substring(0, 1) + "." + str.substring(1, 2) + unit[level];
case 1: return str.substring(0, 2) + unit[level];
case 2: return str.substring(0, 3) + unit[level];
}
return "how that?";
}
private static String trim1 (String pVal) {
if (pVal.equals("0")) return "";
return pVal;
}
private static String trim2 (String pVal) {
if (pVal.equals("00")) return "";
return pVal.substring(0, 1) + trim1(pVal.substring(1,2));
}
private static String myFormat2(int pN) {
String str = Integer.toString(pN);
int len = str.length () - 1;
if (len <= 3) return str;
int level = len / 3;
int mode = len % 3;
switch (mode) {
case 0: return str.substring(0, 1) + unit[level] + trim2(str.substring(1, 3));
case 2: return str.substring(0, 3) + unit[level];
case 1: return str.substring(0, 2) + unit[level] + trim1(str.substring(2, 3));
}
return "how that?";
}
}
#13
4
Staying true to my comment that I'd value readability above performance, here's a version where it should be clear what's happening (assuming you've used BigDecimal
s before) without excessive commenting (I believe in self-documenting code), without worrying about performance (since I can't picture a scenario where you'd want to do this so many millions of times that performance even becomes a consideration).
保持真我的评论,我值可读性以上性能,这是一个版本,它应该清楚发生了什么(假设您已经使用过bigdecimal)没有过多的评论(我相信自我记录的代码),而不用担心性能(因为我无法想象一个场景,你想做这个很多数百万次性能甚至成为考虑)。
This version:
这个版本:
- uses
BigDecimal
s for precision and to avoid rounding issues - 使用BigDecimals来精确和避免四舍五入的问题。
- works for rounding down as requested by the OP
- 按OP要求的舍入工作。
- works for other rounding modes, e.g.
HALF_UP
as in the tests - 为其他的舍入模式工作,例如在测试中使用HALF_UP。
- allows you to adjust the precision (change
REQUIRED_PRECISION
) - 允许您调整精度(更改REQUIRED_PRECISION)
- uses an
enum
to define the thresholds, i.e. could easily be adjusted to use KB/MB/GB/TB instead of k/m/b/t, etc., and could of course be extended beyondTRILLION
if required - 使用枚举来定义阈值,即可以轻松地调整为使用KB/MB/GB/TB,而不是k/m/b/t等,当然,如果需要的话,可以扩展到超过万亿。
- comes with thorough unit tests, since the test cases in the question weren't testing the borders
- 来自于彻底的单元测试,因为测试用例没有测试边界。
- should work for zero and negative numbers
- 应该为零和负数工作吗?
Threshold.java:
Threshold.java:
import java.math.BigDecimal;
public enum Threshold {
TRILLION("1000000000000", 12, 't', null),
BILLION("1000000000", 9, 'b', TRILLION),
MILLION("1000000", 6, 'm', BILLION),
THOUSAND("1000", 3, 'k', MILLION),
ZERO("0", 0, null, THOUSAND);
private BigDecimal value;
private int zeroes;
protected Character suffix;
private Threshold higherThreshold;
private Threshold(String aValueString, int aNumberOfZeroes, Character aSuffix,
Threshold aThreshold) {
value = new BigDecimal(aValueString);
zeroes = aNumberOfZeroes;
suffix = aSuffix;
higherThreshold = aThreshold;
}
public static Threshold thresholdFor(long aValue) {
return thresholdFor(new BigDecimal(aValue));
}
public static Threshold thresholdFor(BigDecimal aValue) {
for (Threshold eachThreshold : Threshold.values()) {
if (eachThreshold.value.compareTo(aValue) <= 0) {
return eachThreshold;
}
}
return TRILLION; // shouldn't be needed, but you might have to extend the enum
}
public int getNumberOfZeroes() {
return zeroes;
}
public String getSuffix() {
return suffix == null ? "" : "" + suffix;
}
public Threshold getHigherThreshold() {
return higherThreshold;
}
}
NumberShortener.java:
NumberShortener.java:
import java.math.BigDecimal;
import java.math.RoundingMode;
public class NumberShortener {
public static final int REQUIRED_PRECISION = 2;
public static BigDecimal toPrecisionWithoutLoss(BigDecimal aBigDecimal,
int aPrecision, RoundingMode aMode) {
int previousScale = aBigDecimal.scale();
int previousPrecision = aBigDecimal.precision();
int newPrecision = Math.max(previousPrecision - previousScale, aPrecision);
return aBigDecimal.setScale(previousScale + newPrecision - previousPrecision,
aMode);
}
private static BigDecimal scaledNumber(BigDecimal aNumber, RoundingMode aMode) {
Threshold threshold = Threshold.thresholdFor(aNumber);
BigDecimal adjustedNumber = aNumber.movePointLeft(threshold.getNumberOfZeroes());
BigDecimal scaledNumber = toPrecisionWithoutLoss(adjustedNumber, REQUIRED_PRECISION,
aMode).stripTrailingZeros();
// System.out.println("Number: <" + aNumber + ">, adjusted: <" + adjustedNumber
// + ">, rounded: <" + scaledNumber + ">");
return scaledNumber;
}
public static String shortenedNumber(long aNumber, RoundingMode aMode) {
boolean isNegative = aNumber < 0;
BigDecimal numberAsBigDecimal = new BigDecimal(isNegative ? -aNumber : aNumber);
Threshold threshold = Threshold.thresholdFor(numberAsBigDecimal);
BigDecimal scaledNumber = aNumber == 0 ? numberAsBigDecimal : scaledNumber(
numberAsBigDecimal, aMode);
if (scaledNumber.compareTo(new BigDecimal("1000")) >= 0) {
scaledNumber = scaledNumber(scaledNumber, aMode);
threshold = threshold.getHigherThreshold();
}
String sign = isNegative ? "-" : "";
String printNumber = sign + scaledNumber.stripTrailingZeros().toPlainString()
+ threshold.getSuffix();
// System.out.println("Number: <" + sign + numberAsBigDecimal + ">, rounded: <"
// + sign + scaledNumber + ">, print: <" + printNumber + ">");
return printNumber;
}
}
(Uncomment the println
statements or change to use your favourite logger to see what it's doing.)
(取消对println语句或更改的注释,以使用您最喜欢的日志记录器来查看它在做什么。)
And finally, the tests in NumberShortenerTest (plain JUnit 4):
最后,在NumberShortenerTest中的测试(简单的JUnit 4):
import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import org.junit.Test;
public class NumberShortenerTest {
private static final long[] NUMBERS_FROM_OP = new long[] { 1000, 5821, 10500, 101800, 2000000, 7800000, 92150000, 123200000 };
private static final String[] EXPECTED_FROM_OP = new String[] { "1k", "5.8k", "10k", "101k", "2m", "7.8m", "92m", "123m" };
private static final String[] EXPECTED_FROM_OP_HALF_UP = new String[] { "1k", "5.8k", "11k", "102k", "2m", "7.8m", "92m", "123m" };
private static final long[] NUMBERS_TO_TEST = new long[] { 1, 500, 999, 1000, 1001, 1009, 1049, 1050, 1099, 1100, 12345, 123456, 999999, 1000000,
1000099, 1000999, 1009999, 1099999, 1100000, 1234567, 999999999, 1000000000, 9123456789L, 123456789123L };
private static final String[] EXPECTED_FROM_TEST = new String[] { "1", "500", "999", "1k", "1k", "1k", "1k", "1k", "1k", "1.1k", "12k", "123k",
"999k", "1m", "1m", "1m", "1m", "1m", "1.1m", "1.2m", "999m", "1b", "9.1b", "123b" };
private static final String[] EXPECTED_FROM_TEST_HALF_UP = new String[] { "1", "500", "999", "1k", "1k", "1k", "1k", "1.1k", "1.1k", "1.1k", "12k",
"123k", "1m", "1m", "1m", "1m", "1m", "1.1m", "1.1m", "1.2m", "1b", "1b", "9.1b", "123b" };
@Test
public void testThresholdFor() {
assertEquals(Threshold.ZERO, Threshold.thresholdFor(1));
assertEquals(Threshold.ZERO, Threshold.thresholdFor(999));
assertEquals(Threshold.THOUSAND, Threshold.thresholdFor(1000));
assertEquals(Threshold.THOUSAND, Threshold.thresholdFor(1234));
assertEquals(Threshold.THOUSAND, Threshold.thresholdFor(9999));
assertEquals(Threshold.THOUSAND, Threshold.thresholdFor(999999));
assertEquals(Threshold.MILLION, Threshold.thresholdFor(1000000));
}
@Test
public void testToPrecision() {
RoundingMode mode = RoundingMode.DOWN;
assertEquals(new BigDecimal("1"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 1, mode));
assertEquals(new BigDecimal("1.2"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 2, mode));
assertEquals(new BigDecimal("1.23"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 3, mode));
assertEquals(new BigDecimal("1.234"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 4, mode));
assertEquals(new BigDecimal("999").toPlainString(), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("999"), 4, mode).stripTrailingZeros()
.toPlainString());
assertEquals(new BigDecimal("999").toPlainString(), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("999"), 2, mode).stripTrailingZeros()
.toPlainString());
assertEquals(new BigDecimal("999").toPlainString(), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("999.9"), 2, mode).stripTrailingZeros()
.toPlainString());
mode = RoundingMode.HALF_UP;
assertEquals(new BigDecimal("1"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 1, mode));
assertEquals(new BigDecimal("1.2"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 2, mode));
assertEquals(new BigDecimal("1.23"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 3, mode));
assertEquals(new BigDecimal("1.235"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 4, mode));
assertEquals(new BigDecimal("999").toPlainString(), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("999"), 4, mode).stripTrailingZeros()
.toPlainString());
assertEquals(new BigDecimal("999").toPlainString(), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("999"), 2, mode).stripTrailingZeros()
.toPlainString());
assertEquals(new BigDecimal("1000").toPlainString(), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("999.9"), 2, mode)
.stripTrailingZeros().toPlainString());
}
@Test
public void testNumbersFromOP() {
for (int i = 0; i < NUMBERS_FROM_OP.length; i++) {
assertEquals("Index " + i + ": " + NUMBERS_FROM_OP[i], EXPECTED_FROM_OP[i],
NumberShortener.shortenedNumber(NUMBERS_FROM_OP[i], RoundingMode.DOWN));
assertEquals("Index " + i + ": " + NUMBERS_FROM_OP[i], EXPECTED_FROM_OP_HALF_UP[i],
NumberShortener.shortenedNumber(NUMBERS_FROM_OP[i], RoundingMode.HALF_UP));
}
}
@Test
public void testBorders() {
assertEquals("Zero: " + 0, "0", NumberShortener.shortenedNumber(0, RoundingMode.DOWN));
assertEquals("Zero: " + 0, "0", NumberShortener.shortenedNumber(0, RoundingMode.HALF_UP));
for (int i = 0; i < NUMBERS_TO_TEST.length; i++) {
assertEquals("Index " + i + ": " + NUMBERS_TO_TEST[i], EXPECTED_FROM_TEST[i],
NumberShortener.shortenedNumber(NUMBERS_TO_TEST[i], RoundingMode.DOWN));
assertEquals("Index " + i + ": " + NUMBERS_TO_TEST[i], EXPECTED_FROM_TEST_HALF_UP[i],
NumberShortener.shortenedNumber(NUMBERS_TO_TEST[i], RoundingMode.HALF_UP));
}
}
@Test
public void testNegativeBorders() {
for (int i = 0; i < NUMBERS_TO_TEST.length; i++) {
assertEquals("Index " + i + ": -" + NUMBERS_TO_TEST[i], "-" + EXPECTED_FROM_TEST[i],
NumberShortener.shortenedNumber(-NUMBERS_TO_TEST[i], RoundingMode.DOWN));
assertEquals("Index " + i + ": -" + NUMBERS_TO_TEST[i], "-" + EXPECTED_FROM_TEST_HALF_UP[i],
NumberShortener.shortenedNumber(-NUMBERS_TO_TEST[i], RoundingMode.HALF_UP));
}
}
}
Feel free to point out in the comments if I missed a significant test case or if expected values should be adjusted.
如果我错过了一个重要的测试用例,或者预期值应该调整,请在评论中指出。
#14
4
The following code shows how you can do this with easy expansion in mind.
下面的代码展示了如何在头脑中轻松地进行扩展。
The "magic" lies mostly in the makeDecimal
function which, for the correct values passed in, guarantees you will never have more than four characters in the output.
“魔法”主要存在于makeDecimal函数中,对于传入的正确值,保证在输出中不会有超过4个字符。
It first extracts the whole and tenths portions for a given divisor so, for example, 12,345,678
with a divisor of 1,000,000
will give a whole
value of 12
and a tenths
value of 3
.
它首先提取一个给定的除数的全部和十分之一部分,例如,12345,678和1,000,000的除数将给出12的整数值和3的十分之一的值。
From that, it can decide whether it outputs just the whole part or both the whole and tenths part, using the rules:
从这一点上,它可以决定它是否只输出整个部分,或者全部或全部的部分,使用规则:
- If tenths part is zero, just output whole part and suffix.
- 如果十分之一是零,则输出整个部分和后缀。
- If whole part is greater than nine, just output whole part and suffix.
- 如果整个部分大于9,则输出整个部分和后缀。
- Otherwise, output whole part, tenths part and suffix.
- 否则,输出整个部分,十分之一部分和后缀。
The code for that follows:
代码如下:
static private String makeDecimal(long val, long div, String sfx) {
val = val / (div / 10);
long whole = val / 10;
long tenths = val % 10;
if ((tenths == 0) || (whole >= 10))
return String.format("%d%s", whole, sfx);
return String.format("%d.%d%s", whole, tenths, sfx);
}
Then, it's a simple matter of calling that helper function with the correct values, including some constants to make life easier for the developer:
然后,简单地用正确的值调用helper函数,包括一些常量,以简化开发人员的生活:
static final long THOU = 1000L;
static final long MILL = 1000000L;
static final long BILL = 1000000000L;
static final long TRIL = 1000000000000L;
static final long QUAD = 1000000000000000L;
static final long QUIN = 1000000000000000000L;
static private String Xlat(long val) {
if (val < THOU) return Long.toString(val);
if (val < MILL) return makeDecimal(val, THOU, "k");
if (val < BILL) return makeDecimal(val, MILL, "m");
if (val < TRIL) return makeDecimal(val, BILL, "b");
if (val < QUAD) return makeDecimal(val, TRIL, "t");
if (val < QUIN) return makeDecimal(val, QUAD, "q");
return makeDecimal(val, QUIN, "u");
}
The fact that the makeDecimal
function does the grunt work means that expanding beyond 999,999,999
is just a matter of adding an extra line to Xlat
, so easy that I've done it for you.
makeDecimal函数完成了grunt工作,这意味着扩展到999,999,999之后只是增加了Xlat的额外行,这很简单,我已经为您做过了。
The final return
in Xlat
doesn't need a conditional since the largest value you can hold in a 64-bit signed long is only about 9.2 quintillion.
Xlat的最终返回不需要条件,因为您可以在64位签名的long中拥有最大的值,只有大约9.2兆。
But if, by some bizarre requirement, Oracle decides to add a 128-bit longer
type or a 1024-bit damn_long
type, you'll be ready for it :-)
但是,如果出于某种奇怪的要求,Oracle决定添加一个128位的长类型或1024位damn_long类型,那么您就可以准备好了:-)
And, finally, a little test harness you can use for validating the functionality.
最后,您可以使用一个小测试工具来验证功能。
public static void main(String[] args) {
long vals[] = {
999L, 1000L, 5821L, 10500L, 101800L, 2000000L,
7800000L, 92150000L, 123200000L, 999999999L,
1000000000L, 1100000000L, 999999999999L,
1000000000000L, 999999999999999L,
1000000000000000L, 9223372036854775807L
};
for (long val: vals)
System.out.println ("" + val + " -> " + Xlat(val));
}
}
You can see from the output that it gives you what you need:
从输出中可以看到,它提供了所需的内容:
999 -> 999
1000 -> 1k
5821 -> 5.8k
10500 -> 10k
101800 -> 101k
2000000 -> 2m
7800000 -> 7.8m
92150000 -> 92m
123200000 -> 123m
999999999 -> 999m
1000000000 -> 1b
1100000000 -> 1.1b
999999999999 -> 999b
1000000000000 -> 1t
999999999999999 -> 999t
1000000000000000 -> 1q
9223372036854775807 -> 9.2u
And, as an aside, be aware that passing in a negative number to this function will result in a string too long for your requirements, since it follows the
< THOU
path). I figured that was okay since you only mention non-negative values in the question.另外,请注意,将负数传递给这个函数会导致字符串太长,无法满足您的需求,因为它会跟随< you path)。我认为这很好,因为你只在问题中提到非负值。
#15
2
Adding my own answer, Java code, self explanatory code..
添加我自己的答案,Java代码,自我解释代码。
import java.math.BigDecimal;
/**
* Method to convert number to formatted number.
*
* @author Gautham PJ
*/
public class ShortFormatNumbers
{
/**
* Main method. Execution starts here.
*/
public static void main(String[] args)
{
// The numbers that are being converted.
int[] numbers = {999, 1400, 2500, 45673463, 983456, 234234567};
// Call the "formatNumber" method on individual numbers to format
// the number.
for(int number : numbers)
{
System.out.println(number + ": " + formatNumber(number));
}
}
/**
* Format the number to display it in short format.
*
* The number is divided by 1000 to find which denomination to be added
* to the number. Dividing the number will give the smallest possible
* value with the denomination.
*
* @param the number that needs to be converted to short hand notation.
* @return the converted short hand notation for the number.
*/
private static String formatNumber(double number)
{
String[] denominations = {"", "k", "m", "b", "t"};
int denominationIndex = 0;
// If number is greater than 1000, divide the number by 1000 and
// increment the index for the denomination.
while(number > 1000.0)
{
denominationIndex++;
number = number / 1000.0;
}
// To round it to 2 digits.
BigDecimal bigDecimal = new BigDecimal(number);
bigDecimal = bigDecimal.setScale(2, BigDecimal.ROUND_HALF_EVEN);
// Add the number with the denomination to get the final value.
String formattedNumber = bigDecimal + denominations[denominationIndex];
return formattedNumber;
}
}
#16
1
try this :
试试这个:
public String Format(Integer number){
String[] suffix = new String[]{"k","m","b","t"};
int size = (number.intValue() != 0) ? (int) Math.log10(number) : 0;
if (size >= 3){
while (size % 3 != 0) {
size = size - 1;
}
}
double notation = Math.pow(10, size);
String result = (size >= 3) ? + (Math.round((number / notation) * 100) / 100.0d)+suffix[(size/3) - 1] : + number + "";
return result
}
#17
1
Currency format 1K 1M 1B 1T in Android
public static String new_format(double number)
{
String[] suffix = new String[]{"","K", "M", "B", "T"};
int MAX_LENGTH = 4;
String r = new DecimalFormat("##0E0").format(number);
r = r.replaceAll("E[0-9]", suffix[Character.getNumericValue(r.charAt(r.length() - 1)) / 3]);
while(r.length() > MAX_LENGTH || r.matches("[0-9]+\\.[a-z]"))
{
r = r.substring(0, r.length()-2) + r.substring(r.length() - 1);
}
return r;
}
#18
0
//code longer but work sure...
public static String formatK(int number) {
if (number < 999) {
return String.valueOf(number);
}
if (number < 9999) {
String strNumber = String.valueOf(number);
String str1 = strNumber.substring(0, 1);
String str2 = strNumber.substring(1, 2);
if (str2.equals("0")) {
return str1 + "k";
} else {
return str1 + "." + str2 + "k";
}
}
if (number < 99999) {
String strNumber = String.valueOf(number);
String str1 = strNumber.substring(0, 2);
return str1 + "k";
}
if (number < 999999) {
String strNumber = String.valueOf(number);
String str1 = strNumber.substring(0, 3);
return str1 + "k";
}
if (number < 9999999) {
String strNumber = String.valueOf(number);
String str1 = strNumber.substring(0, 1);
String str2 = strNumber.substring(1, 2);
if (str2.equals("0")) {
return str1 + "m";
} else {
return str1 + "." + str2 + "m";
}
}
if (number < 99999999) {
String strNumber = String.valueOf(number);
String str1 = strNumber.substring(0, 2);
return str1 + "m";
}
if (number < 999999999) {
String strNumber = String.valueOf(number);
String str1 = strNumber.substring(0, 3);
return str1 + "m";
}
NumberFormat formatterHasDigi = new DecimalFormat("###,###,###");
return formatterHasDigi.format(number);
}
#19
0
This code snippet just deadly simple, and clean code, and totally works:
这个代码片段仅仅是致命的简单,干净的代码,并且完全的工作:
private static char[] c = new char[]{'K', 'M', 'B', 'T'};
private String formatK(double n, int iteration) {
if (n < 1000) {
// print 999 or 999K
if (iteration <= 0) {
return String.valueOf((long) n);
} else {
return String.format("%d%s", Math.round(n), c[iteration-1]);
}
} else if (n < 10000) {
// Print 9.9K
return String.format("%.1f%s", n/1000, c[iteration]);
} else {
// Increase 1 iteration
return formatK(Math.round(n/1000), iteration+1);
}
}
#1
90
Here is a solution that works for any long value and that I find quite readable (the core logic is done in the bottom three lines of the format
method).
这里有一个对任何长值都有效的解决方案,并且我认为它是可读的(核心逻辑是在格式方法的底部三行中完成的)。
It leverages TreeMap
to find the appropriate suffix. It is surprisingly more efficient than a previous solution I wrote that was using arrays and was more difficult to read.
它利用TreeMap来找到合适的后缀。令人惊讶的是,它比我以前编写的使用数组的解决方案更有效,而且阅读起来更困难。
private static final NavigableMap<Long, String> suffixes = new TreeMap<> ();
static {
suffixes.put(1_000L, "k");
suffixes.put(1_000_000L, "M");
suffixes.put(1_000_000_000L, "G");
suffixes.put(1_000_000_000_000L, "T");
suffixes.put(1_000_000_000_000_000L, "P");
suffixes.put(1_000_000_000_000_000_000L, "E");
}
public static String format(long value) {
//Long.MIN_VALUE == -Long.MIN_VALUE so we need an adjustment here
if (value == Long.MIN_VALUE) return format(Long.MIN_VALUE + 1);
if (value < 0) return "-" + format(-value);
if (value < 1000) return Long.toString(value); //deal with easy case
Entry<Long, String> e = suffixes.floorEntry(value);
Long divideBy = e.getKey();
String suffix = e.getValue();
long truncated = value / (divideBy / 10); //the number part of the output times 10
boolean hasDecimal = truncated < 100 && (truncated / 10d) != (truncated / 10);
return hasDecimal ? (truncated / 10d) + suffix : (truncated / 10) + suffix;
}
Test code
public static void main(String args[]) {
long[] numbers = {0, 5, 999, 1_000, -5_821, 10_500, -101_800, 2_000_000, -7_800_000, 92_150_000, 123_200_000, 9_999_999, 999_999_999_999_999_999L, 1_230_000_000_000_000L, Long.MIN_VALUE, Long.MAX_VALUE};
String[] expected = {"0", "5", "999", "1k", "-5.8k", "10k", "-101k", "2M", "-7.8M", "92M", "123M", "9.9M", "999P", "1.2P", "-9.2E", "9.2E"};
for (int i = 0; i < numbers.length; i++) {
long n = numbers[i];
String formatted = format(n);
System.out.println(n + " => " + formatted);
if (!formatted.equals(expected[i])) throw new AssertionError("Expected: " + expected[i] + " but found: " + formatted);
}
}
#2
86
I know, this looks more like a C program, but it's super lightweight!
我知道,这看起来更像一个C程序,但是它是超级轻量级的!
public static void main(String args[]) {
long[] numbers = new long[]{1000, 5821, 10500, 101800, 2000000, 7800000, 92150000, 123200000, 9999999};
for(long n : numbers) {
System.out.println(n + " => " + coolFormat(n, 0));
}
}
private static char[] c = new char[]{'k', 'm', 'b', 't'};
/**
* Recursive implementation, invokes itself for each factor of a thousand, increasing the class on each invokation.
* @param n the number to format
* @param iteration in fact this is the class from the array c
* @return a String representing the number n formatted in a cool looking way.
*/
private static String coolFormat(double n, int iteration) {
double d = ((long) n / 100) / 10.0;
boolean isRound = (d * 10) %10 == 0;//true if the decimal part is equal to 0 (then it's trimmed anyway)
return (d < 1000? //this determines the class, i.e. 'k', 'm' etc
((d > 99.9 || isRound || (!isRound && d > 9.99)? //this decides whether to trim the decimals
(int) d * 10 / 10 : d + "" // (int) d * 10 / 10 drops the decimal
) + "" + c[iteration])
: coolFormat(d, iteration+1));
}
It outputs:
输出:
1000 => 1k
5821 => 5.8k
10500 => 10k
101800 => 101k
2000000 => 2m
7800000 => 7.8m
92150000 => 92m
123200000 => 123m
9999999 => 9.9m
#3
40
Here a solution that makes use of DecimalFormat's engineering notation:
这里有一个解决方案,利用了DecimalFormat的工程符号:
public static void main(String args[]) {
long[] numbers = new long[]{7, 12, 856, 1000, 5821, 10500, 101800, 2000000, 7800000, 92150000, 123200000, 9999999};
for(long number : numbers) {
System.out.println(number + " = " + format(number));
}
}
private static String[] suffix = new String[]{"","k", "m", "b", "t"};
private static int MAX_LENGTH = 4;
private static String format(double number) {
String r = new DecimalFormat("##0E0").format(number);
r = r.replaceAll("E[0-9]", suffix[Character.getNumericValue(r.charAt(r.length() - 1)) / 3]);
while(r.length() > MAX_LENGTH || r.matches("[0-9]+\\.[a-z]")){
r = r.substring(0, r.length()-2) + r.substring(r.length() - 1);
}
return r;
}
Output:
输出:
7 = 7
12 = 12
856 = 856
1000 = 1k
5821 = 5.8k
10500 = 10k
101800 = 102k
2000000 = 2m
7800000 = 7.8m
92150000 = 92m
123200000 = 123m
9999999 = 10m
#4
16
Need some improvement, but: StrictMath to the rescue!
You can put the suffix in a String or array and fetch'em based on power, or something like that.
The division can also be managed around the power, i think almost everything is about the power value. Hope it helps!
需要一些改进,但是:严格的数学救援!你可以把后缀放在一个字符串或数组中,然后以power为基础,或者类似的东西。这个部门也可以在权力范围内管理,我认为几乎所有的事情都是关于权力价值的。希望它可以帮助!
public static String formatValue(double value) {
int power;
String suffix = " kmbt";
String formattedNumber = "";
NumberFormat formatter = new DecimalFormat("#,###.#");
power = (int)StrictMath.log10(value);
value = value/(Math.pow(10,(power/3)*3));
formattedNumber=formatter.format(value);
formattedNumber = formattedNumber + suffix.charAt(power/3);
return formattedNumber.length()>4 ? formattedNumber.replaceAll("\\.[0-9]+", "") : formattedNumber;
}
outputs:
输出:
999
1.2k
98k
911k
1.1m
11b
712b
34t999 . 1.2k 98k 911k 110 b 712b 34t。
#5
15
Problems with Current Answers
- Many of the current solutions are using these prefixes k=103, m=106, b=109, t=1012. However, according to various sources, the correct prefixes are k=103, M=106, G=109, T=1012
- 当前的许多解决方案都使用这些前缀k=103, m=106, b=109, t=1012。然而,根据各种来源,正确的前缀是k=103, M=106, G=109, T=1012。
- Lack of support for negative numbers (or at least a lack of tests demonstrating that negative numbers are supported)
- 缺乏对负数的支持(或者至少缺少证明负数的测试)
- Lack of support for the inverse operation, e.g. converting 1.1k to 1100 (though this is outside the scope of the original question)
- 对逆操作缺乏支持,例如将1.1k转换为1100(尽管这超出了原始问题的范围)
Java Solution
This solution (an extension of this answer) addresses the above issues.
此解决方案(此答案的扩展)解决了上述问题。
import org.apache.commons.lang.math.NumberUtils;
import java.text.DecimalFormat;
import java.text.FieldPosition;
import java.text.Format;
import java.text.ParsePosition;
import java.util.regex.Pattern;
/**
* Converts a number to a string in <a href="http://en.wikipedia.org/wiki/Metric_prefix">metric prefix</a> format.
* For example, 7800000 will be formatted as '7.8M'. Numbers under 1000 will be unchanged. Refer to the tests for further examples.
*/
class RoundedMetricPrefixFormat extends Format {
private static final String[] METRIC_PREFIXES = new String[]{"", "k", "M", "G", "T"};
/**
* The maximum number of characters in the output, excluding the negative sign
*/
private static final Integer MAX_LENGTH = 4;
private static final Pattern TRAILING_DECIMAL_POINT = Pattern.compile("[0-9]+\\.[kMGT]");
private static final Pattern METRIC_PREFIXED_NUMBER = Pattern.compile("\\-?[0-9]+(\\.[0-9])?[kMGT]");
@Override
public StringBuffer format(Object obj, StringBuffer output, FieldPosition pos) {
Double number = Double.valueOf(obj.toString());
// if the number is negative, convert it to a positive number and add the minus sign to the output at the end
boolean isNegative = number < 0;
number = Math.abs(number);
String result = new DecimalFormat("##0E0").format(number);
Integer index = Character.getNumericValue(result.charAt(result.length() - 1)) / 3;
result = result.replaceAll("E[0-9]", METRIC_PREFIXES[index]);
while (result.length() > MAX_LENGTH || TRAILING_DECIMAL_POINT.matcher(result).matches()) {
int length = result.length();
result = result.substring(0, length - 2) + result.substring(length - 1);
}
return output.append(isNegative ? "-" + result : result);
}
/**
* Convert a String produced by <tt>format()</tt> back to a number. This will generally not restore
* the original number because <tt>format()</tt> is a lossy operation, e.g.
*
* <pre>
* {@code
* def formatter = new RoundedMetricPrefixFormat()
* Long number = 5821L
* String formattedNumber = formatter.format(number)
* assert formattedNumber == '5.8k'
*
* Long parsedNumber = formatter.parseObject(formattedNumber)
* assert parsedNumber == 5800
* assert parsedNumber != number
* }
* </pre>
*
* @param source a number that may have a metric prefix
* @param pos if parsing succeeds, this should be updated to the index after the last parsed character
* @return a Number if the the string is a number without a metric prefix, or a Long if it has a metric prefix
*/
@Override
public Object parseObject(String source, ParsePosition pos) {
if (NumberUtils.isNumber(source)) {
// if the value is a number (without a prefix) don't return it as a Long or we'll lose any decimals
pos.setIndex(source.length());
return toNumber(source);
} else if (METRIC_PREFIXED_NUMBER.matcher(source).matches()) {
boolean isNegative = source.charAt(0) == '-';
int length = source.length();
String number = isNegative ? source.substring(1, length - 1) : source.substring(0, length - 1);
String metricPrefix = Character.toString(source.charAt(length - 1));
Number absoluteNumber = toNumber(number);
int index = 0;
for (; index < METRIC_PREFIXES.length; index++) {
if (METRIC_PREFIXES[index].equals(metricPrefix)) {
break;
}
}
Integer exponent = 3 * index;
Double factor = Math.pow(10, exponent);
factor *= isNegative ? -1 : 1;
pos.setIndex(source.length());
Float result = absoluteNumber.floatValue() * factor.longValue();
return result.longValue();
}
return null;
}
private static Number toNumber(String number) {
return NumberUtils.createNumber(number);
}
}
Groovy Solution
The solution was originally written in Groovy as shown below.
解决方案最初是用Groovy编写的,如下所示。
import org.apache.commons.lang.math.NumberUtils
import java.text.DecimalFormat
import java.text.FieldPosition
import java.text.Format
import java.text.ParsePosition
import java.util.regex.Pattern
/**
* Converts a number to a string in <a href="http://en.wikipedia.org/wiki/Metric_prefix">metric prefix</a> format.
* For example, 7800000 will be formatted as '7.8M'. Numbers under 1000 will be unchanged. Refer to the tests for further examples.
*/
class RoundedMetricPrefixFormat extends Format {
private static final METRIC_PREFIXES = ["", "k", "M", "G", "T"]
/**
* The maximum number of characters in the output, excluding the negative sign
*/
private static final Integer MAX_LENGTH = 4
private static final Pattern TRAILING_DECIMAL_POINT = ~/[0-9]+\.[kMGT]/
private static final Pattern METRIC_PREFIXED_NUMBER = ~/\-?[0-9]+(\.[0-9])?[kMGT]/
@Override
StringBuffer format(Object obj, StringBuffer output, FieldPosition pos) {
Double number = obj as Double
// if the number is negative, convert it to a positive number and add the minus sign to the output at the end
boolean isNegative = number < 0
number = Math.abs(number)
String result = new DecimalFormat("##0E0").format(number)
Integer index = Character.getNumericValue(result.charAt(result.size() - 1)) / 3
result = result.replaceAll("E[0-9]", METRIC_PREFIXES[index])
while (result.size() > MAX_LENGTH || TRAILING_DECIMAL_POINT.matcher(result).matches()) {
int length = result.size()
result = result.substring(0, length - 2) + result.substring(length - 1)
}
output << (isNegative ? "-$result" : result)
}
/**
* Convert a String produced by <tt>format()</tt> back to a number. This will generally not restore
* the original number because <tt>format()</tt> is a lossy operation, e.g.
*
* <pre>
* {@code
* def formatter = new RoundedMetricPrefixFormat()
* Long number = 5821L
* String formattedNumber = formatter.format(number)
* assert formattedNumber == '5.8k'
*
* Long parsedNumber = formatter.parseObject(formattedNumber)
* assert parsedNumber == 5800
* assert parsedNumber != number
* }
* </pre>
*
* @param source a number that may have a metric prefix
* @param pos if parsing succeeds, this should be updated to the index after the last parsed character
* @return a Number if the the string is a number without a metric prefix, or a Long if it has a metric prefix
*/
@Override
Object parseObject(String source, ParsePosition pos) {
if (source.isNumber()) {
// if the value is a number (without a prefix) don't return it as a Long or we'll lose any decimals
pos.index = source.size()
toNumber(source)
} else if (METRIC_PREFIXED_NUMBER.matcher(source).matches()) {
boolean isNegative = source[0] == '-'
String number = isNegative ? source[1..-2] : source[0..-2]
String metricPrefix = source[-1]
Number absoluteNumber = toNumber(number)
Integer exponent = 3 * METRIC_PREFIXES.indexOf(metricPrefix)
Long factor = 10 ** exponent
factor *= isNegative ? -1 : 1
pos.index = source.size()
(absoluteNumber * factor) as Long
}
}
private static Number toNumber(String number) {
NumberUtils.createNumber(number)
}
}
Tests (Groovy)
The tests are written in Groovy but can be used to verify either either the Java or Groovy class (because they both have the same name and API).
这些测试是用Groovy编写的,但是可以用来验证Java或Groovy类(因为它们都有相同的名称和API)。
import java.text.Format
import java.text.ParseException
class RoundedMetricPrefixFormatTests extends GroovyTestCase {
private Format roundedMetricPrefixFormat = new RoundedMetricPrefixFormat()
void testNumberFormatting() {
[
7L : '7',
12L : '12',
856L : '856',
1000L : '1k',
(-1000L) : '-1k',
5821L : '5.8k',
10500L : '10k',
101800L : '102k',
2000000L : '2M',
7800000L : '7.8M',
(-7800000L): '-7.8M',
92150000L : '92M',
123200000L : '123M',
9999999L : '10M',
(-9999999L): '-10M'
].each { Long rawValue, String expectedRoundValue ->
assertEquals expectedRoundValue, roundedMetricPrefixFormat.format(rawValue)
}
}
void testStringParsingSuccess() {
[
'7' : 7,
'8.2' : 8.2F,
'856' : 856,
'-856' : -856,
'1k' : 1000,
'5.8k' : 5800,
'-5.8k': -5800,
'10k' : 10000,
'102k' : 102000,
'2M' : 2000000,
'7.8M' : 7800000L,
'92M' : 92000000L,
'-92M' : -92000000L,
'123M' : 123000000L,
'10M' : 10000000L
].each { String metricPrefixNumber, Number expectedValue ->
def parsedNumber = roundedMetricPrefixFormat.parseObject(metricPrefixNumber)
assertEquals expectedValue, parsedNumber
}
}
void testStringParsingFail() {
shouldFail(ParseException) {
roundedMetricPrefixFormat.parseObject('notNumber')
}
}
}
#6
10
The ICU lib has a rule based formatter for numbers, which can be used for number spellout etc. I think using ICU would give you a readable and maintanable solution.
ICU lib有一个用于数字的规则格式化程序,可以用于数字拼写等。我认为使用ICU可以给您一个可读的和可维护的解决方案。
[Usage]
(使用)
The right class is RuleBasedNumberFormat. The format itself can be stored as separate file (or as String constant, IIRC).
正确的类是RuleBasedNumberFormat。格式本身可以存储为单独的文件(或作为字符串常量,IIRC)。
Example from http://userguide.icu-project.org/formatparse/numbers
来自http://userguide.icu-project.org/formatparse/numbers的例子
double num = 2718.28;
NumberFormat formatter =
new RuleBasedNumberFormat(RuleBasedNumberFormat.SPELLOUT);
String result = formatter.format(num);
System.out.println(result);
The same page shows Roman numerals, so I guess your case should be possible, too.
相同的页面显示罗马数字,所以我想您的情况也应该是可能的。
#7
7
Important: Answers casting to double
will fail for numbers like 99999999999999999L
and return 100P
instead of 99P
because double
uses the IEEE
standard:
重要的:对double的回答将会失败,例如9999999999999999999l,并返回100P而不是99P,因为double使用了IEEE标准:
If a decimal string with at most 15 significant digits is converted to IEEE 754 double precision representation and then converted back to a string with the same number of significant digits, then the final string should match the original. [
long
has up to 19 significant digits.]如果以15个有效数字的十进制字符串转换为IEEE 754双精度表示,然后转换为具有相同数目的有效数字的字符串,那么最后的字符串应该与原来的字符串匹配。[长到19位有效数字]
System.out.println((long)(double)99999999999999992L); // 100000000000000000
System.out.println((long)(double)99999999999999991L); // 99999999999999984
// it is even worse for the logarithm:
System.out.println(Math.log10(99999999999999600L)); // 17.0
System.out.println(Math.log10(99999999999999500L)); // 16.999999999999996
This solution cuts off unwanted digits and works for all long
values. Simple but performant implementation (comparison below). -120k can't be expressed with 4 characters, even -0.1M is too long, that's why for negative numbers 5 characters have to be okay:
这个解决方案可以减少不必要的数字,并为所有的长值工作。简单但性能实现(比较之下)。-120k不能用4个字符表示,即使是-0。1m也太长了,这就是为什么负数的5个字符必须是可以的:
private static final char[] magnitudes = {'k', 'M', 'G', 'T', 'P', 'E'}; // enough for long
public static final String convert(long number) {
String ret;
if (number >= 0) {
ret = "";
} else if (number <= -9200000000000000000L) {
return "-9.2E";
} else {
ret = "-";
number = -number;
}
if (number < 1000)
return ret + number;
for (int i = 0; ; i++) {
if (number < 10000 && number % 1000 >= 100)
return ret + (number / 1000) + '.' + ((number % 1000) / 100) + magnitudes[i];
number /= 1000;
if (number < 1000)
return ret + number + magnitudes[i];
}
}
The test in the else if
at the beginning is necessairy because the min is -(2^63)
and the max is (2^63)-1
and therefore the assignment number = -number
would fail if number == Long.MIN_VALUE
. If we have to do a check, then we can as well include as many numbers as possible instead of just checking for number == Long.MIN_VALUE
.
其他的测试如果一开始虽然是大大因为min -(2 ^ 63)和马克斯(2 ^ 63)1,因此作业数量= = = Long.MIN_VALUE数量将会失败如果号码。如果我们要做检查,那么我们可以尽可能地包含尽可能多的数字,而不是仅仅检查number == Long.MIN_VALUE。
The comparison of this implementation with the one who got the most upvotes (said to be the fastest currently) showed that it is more than 5 times faster (it depends on the test settings, but with more numbers the gain gets bigger and this implementation has to do more checks because it handles all cases, so if the other one would be fixed the difference would become even bigger). It is that fast because there are no floating point operations, no logarithm, no power, no recursion, no regex, no sophisticated formatters and minimization of the amount of objects created.
这个实现与人的比较得到了大多数问题(目前是最快的)表明,它是超过5倍(这取决于测试设置,但更多的数字增益变大,该实现必须做更多的检查,因为它处理所有情况下,如果另一个固定的差异会更大)。它的速度很快,因为没有浮点运算,没有对数,没有幂,没有递归,没有regex,没有复杂的格式化,以及最小化创建的对象数量。
Here is the test program:
这是测试程序:
public class Test {
public static void main(String[] args) {
long[] numbers = new long[20000000];
for (int i = 0; i < numbers.length; i++)
numbers[i] = Math.random() < 0.5 ? (long) (Math.random() * Long.MAX_VALUE) : (long) (Math.random() * Long.MIN_VALUE);
System.out.println(convert1(numbers) + " vs. " + convert2(numbers));
}
private static long convert1(long[] numbers) {
long l = System.currentTimeMillis();
for (int i = 0; i < numbers.length; i++)
Converter1.convert(numbers[i]);
return System.currentTimeMillis() - l;
}
private static long convert2(long[] numbers) {
long l = System.currentTimeMillis();
for (int i = 0; i < numbers.length; i++)
Converter2.coolFormat(numbers[i], 0);
return System.currentTimeMillis() - l;
}
}
Possible output: 2309 vs. 11591
(about the same when only using positive numbers and much more extreme when reversing the order of execution, maybe it has something to do with garbage collection)
可能的输出:2309与11591(大约相同,当仅使用正数时,在反转执行顺序时更极端,可能与垃圾收集有关)
#8
7
Here's a short implementation without recursion and just a very small loop. Doesn't work with negative numbers but supports all positive long
s up to Long.MAX_VALUE
:
这里是一个没有递归的简短实现,只是一个很小的循环。不与负数一起工作,但支持所有的正长到长。max_value:
private static final char[] SUFFIXES = {'k', 'm', 'g', 't', 'p', 'e' };
public static String format(long number) {
if(number < 1000) {
// No need to format this
return String.valueOf(number);
}
// Convert to a string
final String string = String.valueOf(number);
// The suffix we're using, 1-based
final int magnitude = (string.length() - 1) / 3;
// The number of digits we must show before the prefix
final int digits = (string.length() - 1) % 3 + 1;
// Build the string
char[] value = new char[4];
for(int i = 0; i < digits; i++) {
value[i] = string.charAt(i);
}
int valueLength = digits;
// Can and should we add a decimal point and an additional number?
if(digits == 1 && string.charAt(1) != '0') {
value[valueLength++] = '.';
value[valueLength++] = string.charAt(1);
}
value[valueLength++] = SUFFIXES[magnitude - 1];
return new String(value, 0, valueLength);
}
Outputs:
输出:
1k
5.8k
10k
101k
2m
7.8m
92m
123m
9.2e (this isLong.MAX_VALUE
)1k 5.8k 101k 101k 2m 78m 92m
I also did some really simple benchmarking (formatting 10 million random longs) and it's considerably faster than Elijah's implementation and slightly faster than assylias' implementation.
我还做了一些非常简单的基准测试(格式化了1000万个随机的longs),它比Elijah的实现要快得多,比assylias的实现要快得多。
Mine: 1137.028 ms
Elijah's: 2664.396 ms
assylias': 1373.473 ms我的是:1137.028以利亚的:2664.396 ms assylias: 1373.473 ms。
#9
7
For anyone that wants to round. This is a great, easy to read solution, that takes advantage of the Java.Lang.Math library
对于任何想圆的人来说。这是一个很好的、易于阅读的解决方案,它利用了Java.Lang。数学库
public static String formatNumberExample(Number number) {
char[] suffix = {' ', 'k', 'M', 'B', 'T', 'P', 'E'};
long numValue = number.longValue();
int value = (int) Math.floor(Math.log10(numValue));
int base = value / 3;
if (value >= 3 && base < suffix.length) {
return new DecimalFormat("~#0.0").format(numValue / Math.pow(10, base * 3)) + suffix[base];
} else {
return new DecimalFormat("#,##0").format(numValue);
}
}
#10
5
My Java is rusty, but here's how I'd implement it in C#:
我的Java已经生锈了,但下面是我如何用c#实现它:
private string FormatNumber(double value)
{
string[] suffixes = new string[] {" k", " m", " b", " t", " q"};
for (int j = suffixes.Length; j > 0; j--)
{
double unit = Math.Pow(1000, j);
if (value >= unit)
return (value / unit).ToString("#,##0.0") + suffixes[--j];
}
return value.ToString("#,##0");
}
It'd be easy to adjust this to use CS kilos (1,024) rather than metric kilos, or to add more units. It formats 1,000 as "1.0 k" rather than "1 k", but I trust that's immaterial.
使用CS公斤(1024)而不是米制公斤,或者增加更多的单位是很容易的。它的格式是“1.0 k”,而不是“1k”,但我相信这是不重要的。
To meet the more specific requirement "no more than four characters", remove the spaces before the suffixes and adjust the middle block like this:
为了满足更具体的要求“不超过四个字符”,在后缀前删除空格,并调整中间块如下:
if (value >= unit)
{
value /= unit;
return (value).ToString(value >= unit * 9.95 ? "#,##0" : "#,##0.0") + suffixes[--j];
}
#11
5
I don't know if it's the best approach but, this is what i did.
我不知道这是不是最好的方法,但这就是我所做的。
7=>7
12=>12
856=>856
1000=>1.0k
5821=>5.82k
10500=>10.5k
101800=>101.8k
2000000=>2.0m
7800000=>7.8m
92150000=>92.15m
123200000=>123.2m
9999999=>10.0m
--- Code---
推荐- - - - - - - - - - - -代码
public String Format(Integer number){
String[] suffix = new String[]{"k","m","b","t"};
int size = (number.intValue() != 0) ? (int) Math.log10(number) : 0;
if (size >= 3){
while (size % 3 != 0) {
size = size - 1;
}
}
double notation = Math.pow(10, size);
String result = (size >= 3) ? + (Math.round((number / notation) * 100) / 100.0d)+suffix[(size/3) - 1] : + number + "";
return result
}
#12
5
My favorite. You could use "k" and so on as indicator for decimal too, as common in the electronic domain. This will give you an extra digit without additional space
我最喜欢的。你也可以用“k”作为十进制的指示器,这在电子领域很常见。这将给你一个额外的数字,没有额外的空间。
Second column tries to use as much digits as possible
第二列尽量使用尽可能多的数字。
1000 => 1.0k | 1000
5821 => 5.8k | 5821
10500 => 10k | 10k5
101800 => 101k | 101k
2000000 => 2.0m | 2m
7800000 => 7.8m | 7m8
92150000 => 92m | 92m1
123200000 => 123m | 123m
9999999 => 9.9m | 9m99
This is the code
这是代码
public class HTTest {
private static String[] unit = {"u", "k", "m", "g", "t"};
/**
* @param args
*/
public static void main(String[] args) {
int[] numbers = new int[]{1000, 5821, 10500, 101800, 2000000, 7800000, 92150000, 123200000, 9999999};
for(int n : numbers) {
System.out.println(n + " => " + myFormat(n) + " | " + myFormat2(n));
}
}
private static String myFormat(int pN) {
String str = Integer.toString(pN);
int len = str.length ()-1;
if (len <= 3) return str;
int level = len / 3;
int mode = len % 3;
switch (mode) {
case 0: return str.substring(0, 1) + "." + str.substring(1, 2) + unit[level];
case 1: return str.substring(0, 2) + unit[level];
case 2: return str.substring(0, 3) + unit[level];
}
return "how that?";
}
private static String trim1 (String pVal) {
if (pVal.equals("0")) return "";
return pVal;
}
private static String trim2 (String pVal) {
if (pVal.equals("00")) return "";
return pVal.substring(0, 1) + trim1(pVal.substring(1,2));
}
private static String myFormat2(int pN) {
String str = Integer.toString(pN);
int len = str.length () - 1;
if (len <= 3) return str;
int level = len / 3;
int mode = len % 3;
switch (mode) {
case 0: return str.substring(0, 1) + unit[level] + trim2(str.substring(1, 3));
case 2: return str.substring(0, 3) + unit[level];
case 1: return str.substring(0, 2) + unit[level] + trim1(str.substring(2, 3));
}
return "how that?";
}
}
#13
4
Staying true to my comment that I'd value readability above performance, here's a version where it should be clear what's happening (assuming you've used BigDecimal
s before) without excessive commenting (I believe in self-documenting code), without worrying about performance (since I can't picture a scenario where you'd want to do this so many millions of times that performance even becomes a consideration).
保持真我的评论,我值可读性以上性能,这是一个版本,它应该清楚发生了什么(假设您已经使用过bigdecimal)没有过多的评论(我相信自我记录的代码),而不用担心性能(因为我无法想象一个场景,你想做这个很多数百万次性能甚至成为考虑)。
This version:
这个版本:
- uses
BigDecimal
s for precision and to avoid rounding issues - 使用BigDecimals来精确和避免四舍五入的问题。
- works for rounding down as requested by the OP
- 按OP要求的舍入工作。
- works for other rounding modes, e.g.
HALF_UP
as in the tests - 为其他的舍入模式工作,例如在测试中使用HALF_UP。
- allows you to adjust the precision (change
REQUIRED_PRECISION
) - 允许您调整精度(更改REQUIRED_PRECISION)
- uses an
enum
to define the thresholds, i.e. could easily be adjusted to use KB/MB/GB/TB instead of k/m/b/t, etc., and could of course be extended beyondTRILLION
if required - 使用枚举来定义阈值,即可以轻松地调整为使用KB/MB/GB/TB,而不是k/m/b/t等,当然,如果需要的话,可以扩展到超过万亿。
- comes with thorough unit tests, since the test cases in the question weren't testing the borders
- 来自于彻底的单元测试,因为测试用例没有测试边界。
- should work for zero and negative numbers
- 应该为零和负数工作吗?
Threshold.java:
Threshold.java:
import java.math.BigDecimal;
public enum Threshold {
TRILLION("1000000000000", 12, 't', null),
BILLION("1000000000", 9, 'b', TRILLION),
MILLION("1000000", 6, 'm', BILLION),
THOUSAND("1000", 3, 'k', MILLION),
ZERO("0", 0, null, THOUSAND);
private BigDecimal value;
private int zeroes;
protected Character suffix;
private Threshold higherThreshold;
private Threshold(String aValueString, int aNumberOfZeroes, Character aSuffix,
Threshold aThreshold) {
value = new BigDecimal(aValueString);
zeroes = aNumberOfZeroes;
suffix = aSuffix;
higherThreshold = aThreshold;
}
public static Threshold thresholdFor(long aValue) {
return thresholdFor(new BigDecimal(aValue));
}
public static Threshold thresholdFor(BigDecimal aValue) {
for (Threshold eachThreshold : Threshold.values()) {
if (eachThreshold.value.compareTo(aValue) <= 0) {
return eachThreshold;
}
}
return TRILLION; // shouldn't be needed, but you might have to extend the enum
}
public int getNumberOfZeroes() {
return zeroes;
}
public String getSuffix() {
return suffix == null ? "" : "" + suffix;
}
public Threshold getHigherThreshold() {
return higherThreshold;
}
}
NumberShortener.java:
NumberShortener.java:
import java.math.BigDecimal;
import java.math.RoundingMode;
public class NumberShortener {
public static final int REQUIRED_PRECISION = 2;
public static BigDecimal toPrecisionWithoutLoss(BigDecimal aBigDecimal,
int aPrecision, RoundingMode aMode) {
int previousScale = aBigDecimal.scale();
int previousPrecision = aBigDecimal.precision();
int newPrecision = Math.max(previousPrecision - previousScale, aPrecision);
return aBigDecimal.setScale(previousScale + newPrecision - previousPrecision,
aMode);
}
private static BigDecimal scaledNumber(BigDecimal aNumber, RoundingMode aMode) {
Threshold threshold = Threshold.thresholdFor(aNumber);
BigDecimal adjustedNumber = aNumber.movePointLeft(threshold.getNumberOfZeroes());
BigDecimal scaledNumber = toPrecisionWithoutLoss(adjustedNumber, REQUIRED_PRECISION,
aMode).stripTrailingZeros();
// System.out.println("Number: <" + aNumber + ">, adjusted: <" + adjustedNumber
// + ">, rounded: <" + scaledNumber + ">");
return scaledNumber;
}
public static String shortenedNumber(long aNumber, RoundingMode aMode) {
boolean isNegative = aNumber < 0;
BigDecimal numberAsBigDecimal = new BigDecimal(isNegative ? -aNumber : aNumber);
Threshold threshold = Threshold.thresholdFor(numberAsBigDecimal);
BigDecimal scaledNumber = aNumber == 0 ? numberAsBigDecimal : scaledNumber(
numberAsBigDecimal, aMode);
if (scaledNumber.compareTo(new BigDecimal("1000")) >= 0) {
scaledNumber = scaledNumber(scaledNumber, aMode);
threshold = threshold.getHigherThreshold();
}
String sign = isNegative ? "-" : "";
String printNumber = sign + scaledNumber.stripTrailingZeros().toPlainString()
+ threshold.getSuffix();
// System.out.println("Number: <" + sign + numberAsBigDecimal + ">, rounded: <"
// + sign + scaledNumber + ">, print: <" + printNumber + ">");
return printNumber;
}
}
(Uncomment the println
statements or change to use your favourite logger to see what it's doing.)
(取消对println语句或更改的注释,以使用您最喜欢的日志记录器来查看它在做什么。)
And finally, the tests in NumberShortenerTest (plain JUnit 4):
最后,在NumberShortenerTest中的测试(简单的JUnit 4):
import static org.junit.Assert.*;
import java.math.BigDecimal;
import java.math.RoundingMode;
import org.junit.Test;
public class NumberShortenerTest {
private static final long[] NUMBERS_FROM_OP = new long[] { 1000, 5821, 10500, 101800, 2000000, 7800000, 92150000, 123200000 };
private static final String[] EXPECTED_FROM_OP = new String[] { "1k", "5.8k", "10k", "101k", "2m", "7.8m", "92m", "123m" };
private static final String[] EXPECTED_FROM_OP_HALF_UP = new String[] { "1k", "5.8k", "11k", "102k", "2m", "7.8m", "92m", "123m" };
private static final long[] NUMBERS_TO_TEST = new long[] { 1, 500, 999, 1000, 1001, 1009, 1049, 1050, 1099, 1100, 12345, 123456, 999999, 1000000,
1000099, 1000999, 1009999, 1099999, 1100000, 1234567, 999999999, 1000000000, 9123456789L, 123456789123L };
private static final String[] EXPECTED_FROM_TEST = new String[] { "1", "500", "999", "1k", "1k", "1k", "1k", "1k", "1k", "1.1k", "12k", "123k",
"999k", "1m", "1m", "1m", "1m", "1m", "1.1m", "1.2m", "999m", "1b", "9.1b", "123b" };
private static final String[] EXPECTED_FROM_TEST_HALF_UP = new String[] { "1", "500", "999", "1k", "1k", "1k", "1k", "1.1k", "1.1k", "1.1k", "12k",
"123k", "1m", "1m", "1m", "1m", "1m", "1.1m", "1.1m", "1.2m", "1b", "1b", "9.1b", "123b" };
@Test
public void testThresholdFor() {
assertEquals(Threshold.ZERO, Threshold.thresholdFor(1));
assertEquals(Threshold.ZERO, Threshold.thresholdFor(999));
assertEquals(Threshold.THOUSAND, Threshold.thresholdFor(1000));
assertEquals(Threshold.THOUSAND, Threshold.thresholdFor(1234));
assertEquals(Threshold.THOUSAND, Threshold.thresholdFor(9999));
assertEquals(Threshold.THOUSAND, Threshold.thresholdFor(999999));
assertEquals(Threshold.MILLION, Threshold.thresholdFor(1000000));
}
@Test
public void testToPrecision() {
RoundingMode mode = RoundingMode.DOWN;
assertEquals(new BigDecimal("1"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 1, mode));
assertEquals(new BigDecimal("1.2"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 2, mode));
assertEquals(new BigDecimal("1.23"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 3, mode));
assertEquals(new BigDecimal("1.234"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 4, mode));
assertEquals(new BigDecimal("999").toPlainString(), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("999"), 4, mode).stripTrailingZeros()
.toPlainString());
assertEquals(new BigDecimal("999").toPlainString(), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("999"), 2, mode).stripTrailingZeros()
.toPlainString());
assertEquals(new BigDecimal("999").toPlainString(), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("999.9"), 2, mode).stripTrailingZeros()
.toPlainString());
mode = RoundingMode.HALF_UP;
assertEquals(new BigDecimal("1"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 1, mode));
assertEquals(new BigDecimal("1.2"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 2, mode));
assertEquals(new BigDecimal("1.23"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 3, mode));
assertEquals(new BigDecimal("1.235"), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("1.23456"), 4, mode));
assertEquals(new BigDecimal("999").toPlainString(), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("999"), 4, mode).stripTrailingZeros()
.toPlainString());
assertEquals(new BigDecimal("999").toPlainString(), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("999"), 2, mode).stripTrailingZeros()
.toPlainString());
assertEquals(new BigDecimal("1000").toPlainString(), NumberShortener.toPrecisionWithoutLoss(new BigDecimal("999.9"), 2, mode)
.stripTrailingZeros().toPlainString());
}
@Test
public void testNumbersFromOP() {
for (int i = 0; i < NUMBERS_FROM_OP.length; i++) {
assertEquals("Index " + i + ": " + NUMBERS_FROM_OP[i], EXPECTED_FROM_OP[i],
NumberShortener.shortenedNumber(NUMBERS_FROM_OP[i], RoundingMode.DOWN));
assertEquals("Index " + i + ": " + NUMBERS_FROM_OP[i], EXPECTED_FROM_OP_HALF_UP[i],
NumberShortener.shortenedNumber(NUMBERS_FROM_OP[i], RoundingMode.HALF_UP));
}
}
@Test
public void testBorders() {
assertEquals("Zero: " + 0, "0", NumberShortener.shortenedNumber(0, RoundingMode.DOWN));
assertEquals("Zero: " + 0, "0", NumberShortener.shortenedNumber(0, RoundingMode.HALF_UP));
for (int i = 0; i < NUMBERS_TO_TEST.length; i++) {
assertEquals("Index " + i + ": " + NUMBERS_TO_TEST[i], EXPECTED_FROM_TEST[i],
NumberShortener.shortenedNumber(NUMBERS_TO_TEST[i], RoundingMode.DOWN));
assertEquals("Index " + i + ": " + NUMBERS_TO_TEST[i], EXPECTED_FROM_TEST_HALF_UP[i],
NumberShortener.shortenedNumber(NUMBERS_TO_TEST[i], RoundingMode.HALF_UP));
}
}
@Test
public void testNegativeBorders() {
for (int i = 0; i < NUMBERS_TO_TEST.length; i++) {
assertEquals("Index " + i + ": -" + NUMBERS_TO_TEST[i], "-" + EXPECTED_FROM_TEST[i],
NumberShortener.shortenedNumber(-NUMBERS_TO_TEST[i], RoundingMode.DOWN));
assertEquals("Index " + i + ": -" + NUMBERS_TO_TEST[i], "-" + EXPECTED_FROM_TEST_HALF_UP[i],
NumberShortener.shortenedNumber(-NUMBERS_TO_TEST[i], RoundingMode.HALF_UP));
}
}
}
Feel free to point out in the comments if I missed a significant test case or if expected values should be adjusted.
如果我错过了一个重要的测试用例,或者预期值应该调整,请在评论中指出。
#14
4
The following code shows how you can do this with easy expansion in mind.
下面的代码展示了如何在头脑中轻松地进行扩展。
The "magic" lies mostly in the makeDecimal
function which, for the correct values passed in, guarantees you will never have more than four characters in the output.
“魔法”主要存在于makeDecimal函数中,对于传入的正确值,保证在输出中不会有超过4个字符。
It first extracts the whole and tenths portions for a given divisor so, for example, 12,345,678
with a divisor of 1,000,000
will give a whole
value of 12
and a tenths
value of 3
.
它首先提取一个给定的除数的全部和十分之一部分,例如,12345,678和1,000,000的除数将给出12的整数值和3的十分之一的值。
From that, it can decide whether it outputs just the whole part or both the whole and tenths part, using the rules:
从这一点上,它可以决定它是否只输出整个部分,或者全部或全部的部分,使用规则:
- If tenths part is zero, just output whole part and suffix.
- 如果十分之一是零,则输出整个部分和后缀。
- If whole part is greater than nine, just output whole part and suffix.
- 如果整个部分大于9,则输出整个部分和后缀。
- Otherwise, output whole part, tenths part and suffix.
- 否则,输出整个部分,十分之一部分和后缀。
The code for that follows:
代码如下:
static private String makeDecimal(long val, long div, String sfx) {
val = val / (div / 10);
long whole = val / 10;
long tenths = val % 10;
if ((tenths == 0) || (whole >= 10))
return String.format("%d%s", whole, sfx);
return String.format("%d.%d%s", whole, tenths, sfx);
}
Then, it's a simple matter of calling that helper function with the correct values, including some constants to make life easier for the developer:
然后,简单地用正确的值调用helper函数,包括一些常量,以简化开发人员的生活:
static final long THOU = 1000L;
static final long MILL = 1000000L;
static final long BILL = 1000000000L;
static final long TRIL = 1000000000000L;
static final long QUAD = 1000000000000000L;
static final long QUIN = 1000000000000000000L;
static private String Xlat(long val) {
if (val < THOU) return Long.toString(val);
if (val < MILL) return makeDecimal(val, THOU, "k");
if (val < BILL) return makeDecimal(val, MILL, "m");
if (val < TRIL) return makeDecimal(val, BILL, "b");
if (val < QUAD) return makeDecimal(val, TRIL, "t");
if (val < QUIN) return makeDecimal(val, QUAD, "q");
return makeDecimal(val, QUIN, "u");
}
The fact that the makeDecimal
function does the grunt work means that expanding beyond 999,999,999
is just a matter of adding an extra line to Xlat
, so easy that I've done it for you.
makeDecimal函数完成了grunt工作,这意味着扩展到999,999,999之后只是增加了Xlat的额外行,这很简单,我已经为您做过了。
The final return
in Xlat
doesn't need a conditional since the largest value you can hold in a 64-bit signed long is only about 9.2 quintillion.
Xlat的最终返回不需要条件,因为您可以在64位签名的long中拥有最大的值,只有大约9.2兆。
But if, by some bizarre requirement, Oracle decides to add a 128-bit longer
type or a 1024-bit damn_long
type, you'll be ready for it :-)
但是,如果出于某种奇怪的要求,Oracle决定添加一个128位的长类型或1024位damn_long类型,那么您就可以准备好了:-)
And, finally, a little test harness you can use for validating the functionality.
最后,您可以使用一个小测试工具来验证功能。
public static void main(String[] args) {
long vals[] = {
999L, 1000L, 5821L, 10500L, 101800L, 2000000L,
7800000L, 92150000L, 123200000L, 999999999L,
1000000000L, 1100000000L, 999999999999L,
1000000000000L, 999999999999999L,
1000000000000000L, 9223372036854775807L
};
for (long val: vals)
System.out.println ("" + val + " -> " + Xlat(val));
}
}
You can see from the output that it gives you what you need:
从输出中可以看到,它提供了所需的内容:
999 -> 999
1000 -> 1k
5821 -> 5.8k
10500 -> 10k
101800 -> 101k
2000000 -> 2m
7800000 -> 7.8m
92150000 -> 92m
123200000 -> 123m
999999999 -> 999m
1000000000 -> 1b
1100000000 -> 1.1b
999999999999 -> 999b
1000000000000 -> 1t
999999999999999 -> 999t
1000000000000000 -> 1q
9223372036854775807 -> 9.2u
And, as an aside, be aware that passing in a negative number to this function will result in a string too long for your requirements, since it follows the
< THOU
path). I figured that was okay since you only mention non-negative values in the question.另外,请注意,将负数传递给这个函数会导致字符串太长,无法满足您的需求,因为它会跟随< you path)。我认为这很好,因为你只在问题中提到非负值。
#15
2
Adding my own answer, Java code, self explanatory code..
添加我自己的答案,Java代码,自我解释代码。
import java.math.BigDecimal;
/**
* Method to convert number to formatted number.
*
* @author Gautham PJ
*/
public class ShortFormatNumbers
{
/**
* Main method. Execution starts here.
*/
public static void main(String[] args)
{
// The numbers that are being converted.
int[] numbers = {999, 1400, 2500, 45673463, 983456, 234234567};
// Call the "formatNumber" method on individual numbers to format
// the number.
for(int number : numbers)
{
System.out.println(number + ": " + formatNumber(number));
}
}
/**
* Format the number to display it in short format.
*
* The number is divided by 1000 to find which denomination to be added
* to the number. Dividing the number will give the smallest possible
* value with the denomination.
*
* @param the number that needs to be converted to short hand notation.
* @return the converted short hand notation for the number.
*/
private static String formatNumber(double number)
{
String[] denominations = {"", "k", "m", "b", "t"};
int denominationIndex = 0;
// If number is greater than 1000, divide the number by 1000 and
// increment the index for the denomination.
while(number > 1000.0)
{
denominationIndex++;
number = number / 1000.0;
}
// To round it to 2 digits.
BigDecimal bigDecimal = new BigDecimal(number);
bigDecimal = bigDecimal.setScale(2, BigDecimal.ROUND_HALF_EVEN);
// Add the number with the denomination to get the final value.
String formattedNumber = bigDecimal + denominations[denominationIndex];
return formattedNumber;
}
}
#16
1
try this :
试试这个:
public String Format(Integer number){
String[] suffix = new String[]{"k","m","b","t"};
int size = (number.intValue() != 0) ? (int) Math.log10(number) : 0;
if (size >= 3){
while (size % 3 != 0) {
size = size - 1;
}
}
double notation = Math.pow(10, size);
String result = (size >= 3) ? + (Math.round((number / notation) * 100) / 100.0d)+suffix[(size/3) - 1] : + number + "";
return result
}
#17
1
Currency format 1K 1M 1B 1T in Android
public static String new_format(double number)
{
String[] suffix = new String[]{"","K", "M", "B", "T"};
int MAX_LENGTH = 4;
String r = new DecimalFormat("##0E0").format(number);
r = r.replaceAll("E[0-9]", suffix[Character.getNumericValue(r.charAt(r.length() - 1)) / 3]);
while(r.length() > MAX_LENGTH || r.matches("[0-9]+\\.[a-z]"))
{
r = r.substring(0, r.length()-2) + r.substring(r.length() - 1);
}
return r;
}
#18
0
//code longer but work sure...
public static String formatK(int number) {
if (number < 999) {
return String.valueOf(number);
}
if (number < 9999) {
String strNumber = String.valueOf(number);
String str1 = strNumber.substring(0, 1);
String str2 = strNumber.substring(1, 2);
if (str2.equals("0")) {
return str1 + "k";
} else {
return str1 + "." + str2 + "k";
}
}
if (number < 99999) {
String strNumber = String.valueOf(number);
String str1 = strNumber.substring(0, 2);
return str1 + "k";
}
if (number < 999999) {
String strNumber = String.valueOf(number);
String str1 = strNumber.substring(0, 3);
return str1 + "k";
}
if (number < 9999999) {
String strNumber = String.valueOf(number);
String str1 = strNumber.substring(0, 1);
String str2 = strNumber.substring(1, 2);
if (str2.equals("0")) {
return str1 + "m";
} else {
return str1 + "." + str2 + "m";
}
}
if (number < 99999999) {
String strNumber = String.valueOf(number);
String str1 = strNumber.substring(0, 2);
return str1 + "m";
}
if (number < 999999999) {
String strNumber = String.valueOf(number);
String str1 = strNumber.substring(0, 3);
return str1 + "m";
}
NumberFormat formatterHasDigi = new DecimalFormat("###,###,###");
return formatterHasDigi.format(number);
}
#19
0
This code snippet just deadly simple, and clean code, and totally works:
这个代码片段仅仅是致命的简单,干净的代码,并且完全的工作:
private static char[] c = new char[]{'K', 'M', 'B', 'T'};
private String formatK(double n, int iteration) {
if (n < 1000) {
// print 999 or 999K
if (iteration <= 0) {
return String.valueOf((long) n);
} else {
return String.format("%d%s", Math.round(n), c[iteration-1]);
}
} else if (n < 10000) {
// Print 9.9K
return String.format("%.1f%s", n/1000, c[iteration]);
} else {
// Increase 1 iteration
return formatK(Math.round(n/1000), iteration+1);
}
}