如何在java中格式化1200到1.2k

Mat*_* B. 150 java number-formatting

我想将以下数字格式化为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
Run Code Online (Sandbox Code Playgroud)

右边的数字是长整数,左边的数字是字符串.我应该如何处理这个问题.我已经为此做了很少的算法,但我认为可能已经发明了一些可以做得更好的事情,并且如果我开始处理数十亿和数万亿,则不需要额外的测试:)

其他要求:

  • 格式最多应包含4个字符
  • 以上意味着1.1k可以11.2k不行.相同的7.8m是可以的19.1m不是.小数点前只允许一位数具有小数点.小数点前的两位数表示小数点后的数字.
  • 不需要四舍五入.(附加k和m显示的数字更多是模拟量表,表示近似而不是精确的逻辑文章.因此,舍入与主要由于变量的性质无关,即使在查看缓存结果时也可以增加或减少几位数.)

ass*_*ias 143

这是一个适用于任何长值的解决方案,我发现它非常易读(核心逻辑在方法的最后三行完成format).

它利用它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;
}
Run Code Online (Sandbox Code Playgroud)

测试代码

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);
  }
}
Run Code Online (Sandbox Code Playgroud)


Eli*_*ine 98

我知道,这看起来更像是一个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));

}
Run Code Online (Sandbox Code Playgroud)

它输出:

1000 => 1k
5821 => 5.8k
10500 => 10k
101800 => 101k
2000000 => 2m
7800000 => 7.8m
92150000 => 92m
123200000 => 123m
9999999 => 9.9m
Run Code Online (Sandbox Code Playgroud)

  • 混淆?请原谅,但你可能会读一本书,并认为你现在可以用不同的方式编写代码.告诉Joel(http://www.joelonsoftware.com/articles/ThePerilsofJavaSchools.html).我敢说你可能写的任何代码都能接近我方法的速度! (29认同)
  • 混淆代码.我们现在不必像这样编码.可能会按预期工作,但我鼓励作者看看[Roger C. Martin:Clean Code](http://www.amazon.com/Clean-Code-Handbook-Software-Craftsmanship/dp/0132350882 ) (15认同)
  • 将d,c,n变量更改为更具可读性(更快理解)的变量使我的视图中的代码变得像样 (11认同)
  • 我不得不同意@ AmosM.Carpenter.4年前我写这个答案时,我对代码可维护性了解甚少.一般而言,优化并不坏,但首先是可读性.顺便说一句,性能并不是那么糟糕:不比maraca写的慢5倍 - 它大致相同(我在这里为基准测试提出了一些解决方案https://github.com/esaounkine/数字格式的基准测试). (8认同)
  • 为什么这种对表现的痴迷?为什么有人想要执行足够多的这些转换以保证甚至考虑性能......?可读性首先,仅在需要时进行性能调整. (4认同)
  • 他的例子显示"10500到11k",所以我想说他想要四舍五入. (3认同)
  • 哦,上帝,c var ...编译器负责缩短var名称.为var名称键入小写字母只是简单的懒惰. (2认同)

jzd*_*jzd 42

这里是一个使用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;
}
Run Code Online (Sandbox Code Playgroud)

输出:

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
Run Code Online (Sandbox Code Playgroud)

  • 轮次160000至200k,并且轮次120000减少到100k (5认同)
  • 这打破了,我输入数字10000000000000.0,它说103. (4认同)

jhu*_*ado 21

需要一些改进,但是:StrictMath来救援!
您可以将后缀放在String或数组中,并根据power或其他类似内容获取fetch'em.
该部门也可以围绕权力进行管理,我认为几乎所有事情都与权力价值有关.希望能帮助到你!

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;  
}
Run Code Online (Sandbox Code Playgroud)

输出:

999
1.2K
98K
911k
1.1米
11B
712B
34吨

  • 提高了可读性,只需要从jzd添加return语句来解决4 char问题.并且记得在超过t时添加后缀以避免AIOOB异常.;) (2认同)
  • 抛出0的异常,对负数不起作用,不正确地绕9,999,999(打印10m)... (2认同)

Dón*_*nal 15

当前答案的问题

  • 许多当前的解决方案使用这些前缀k = 10 3,m = 10 6,b = 10 9,t = 10 12.但是,根据各种 来源,正确的前缀是k = 10 3,M = 10 6,G = 10 9,T = 10 12
  • 缺乏对负数的支持(或者至少缺乏证明支持负数的测试)
  • 缺乏对逆操作的支持,例如将1.1k转换为1100(尽管这超出了原始问题的范围)

Java解决方案

该解决方案(本答案的扩展)解决了上述问题.

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Groovy解决方案

该解决方案最初是用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)
    }
}
Run Code Online (Sandbox Code Playgroud)

测试(Groovy)

测试是用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')
        }
    }
}
Run Code Online (Sandbox Code Playgroud)


Lan*_*dei 12

ICU LIB有数字的一个基于规则的格式,可我认为使用ICU会给你一个可读和maintanable解决方案可用于数字spellout等.

[用法]

正确的类是RuleBasedNumberFormat.格式本身可以存储为单独的文件(或String常量,IIRC).

来自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);
Run Code Online (Sandbox Code Playgroud)

同一页面显示罗马数字,所以我想你的情况也应该是可能的.

  • 如果您需要它进行 Android 开发,它已经包含在框架中。查找“CompactDecimalFormat”。API 级别 24+ (2认同)

Pha*_*inh 9

我将大数转换为小数的函数(2 位数字)。您可以通过改变而改变的位数#.##DecimalFormat

public String formatValue(float value) {
    String arr[] = {"", "K", "M", "B", "T", "P", "E"};
    int index = 0;
    while ((value / 1000) >= 1) {
        value = value / 1000;
        index++;
    }
    DecimalFormat decimalFormat = new DecimalFormat("#.##");
    return String.format("%s %s", decimalFormat.format(value), arr[index]);
}
Run Code Online (Sandbox Code Playgroud)

测试

System.out.println(formatValue(100));     //  100
System.out.println(formatValue(1000));    // 1 K
System.out.println(formatValue(10345));   // 10.35 K
System.out.println(formatValue(10012));   // 10.01 K
System.out.println(formatValue(123456));  // 123.46 K
System.out.println(formatValue(4384324)); // 4.38 M
System.out.println(formatValue(10000000)); // 10 M
System.out.println(formatValue(Long.MAX_VALUE)); // 9.22 E
Run Code Online (Sandbox Code Playgroud)

希望有帮助


mar*_*aca 8

重要提示:double对于数字而言99999999999999999L,为了使用标准而返回的铸件将失败,100P而不是99P因为double使用IEEE标准:

如果具有最多15位有效数字的十进制字符串转换为IEEE 754双精度表示,然后转换回具有相同有效位数的字符串,则最终字符串应与原始字符串匹配.[ long多达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
Run Code Online (Sandbox Code Playgroud)

此解决方案可以切断不需要的数字,适用于所有long.简单但高性能的实现(下面的比较).-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];
    }
}
Run Code Online (Sandbox Code Playgroud)

else if在开始时的测试是必要的,因为最小值是-(2^63)最大值(2^63)-1,因此number = -number如果分配将失败number == Long.MIN_VALUE.如果我们必须进行检查,那么我们也可以包括尽可能多的数字,而不仅仅是检查number == Long.MIN_VALUE.

将这种实现与得到最多投票者(据称是目前最快的)进行比较表明它的速度提高了5倍以上(这取决于测试设置,但是随着数字越多,增益越大,这种实现方式越多做更多的检查,因为它处理所有情况,所以如果另一个将被修复,差异将变得更大).它是快速的,因为没有浮点运算,没有对数,没有幂,没有递归,没有正则表达式,没有复杂的格式化程序和最小化创建的对象数量.


这是测试程序:

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

}
Run Code Online (Sandbox Code Playgroud)

可能的输出:( 2309 vs. 11591当只使用正数时大致相同,在反转执行顺序时更加极端,可能与垃圾收集有关)


Ran*_*niz 8

这是一个没有递归的简短实现,只是一个非常小的循环.与负数不工作,但支持所有积极long到s Long.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);
}
Run Code Online (Sandbox Code Playgroud)

输出:

1k
5.8k
10k
101k
2m
7.8m
92m
123m
9.2e(这是Long.MAX_VALUE)

我还做了一些非常简单的基准测试(格式化1000万随机长片),它比Elijah的实现速度快得多,并且比assylias的实现快一点.

我的:1137.028 ms
Elijah:2664.396 ms
assylias':1373.473 ms

  • 感谢您的注意,它是固定的 (2认同)

Nam*_*man 8

JDK / 12及更高版本中,您现在可以使用NumberFormat.getCompactNumberInstance格式化数字。您可以创建NumberFormat第一个

NumberFormat fmt = NumberFormat.getCompactNumberInstance(Locale.US, NumberFormat.Style.SHORT);
Run Code Online (Sandbox Code Playgroud)

然后将其用于format

fmt.format(1000)
$5 ==> "1K"

fmt.format(10000000)
$9 ==> "10M"

fmt.format(1000000000)
$11 ==> "1B"
Run Code Online (Sandbox Code Playgroud)


pax*_*blo 7

以下代码显示了如何通过轻松扩展来实现此目的.

"魔法"主要在于makeDecimal函数,对于传入的正确值,它保证输出中永远不会有超过四个字符.

它首先提取给定除数的整数和十分之一部分,因此,例如,12,345,678除数1,000,000将给出whole12tenths3.

从那以后,它可以决定是使用规则输出整个部分还是整个部分和十分之一部分:

  • 如果十分之一部分为零,则只输出整个部分和后缀.
  • 如果整个部分大于9,则只输出整个部分和后缀.
  • 否则,输出整个部分,十分之一部分和后缀.

代码如下:

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);
}
Run Code Online (Sandbox Code Playgroud)

然后,使用正确的值调用辅助函数是一件简单的事情,包括一些使开发人员更容易生活的常量:

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");
}
Run Code Online (Sandbox Code Playgroud)

makeDecimal函数执行繁琐的工作这一事实意味着扩展范围999,999,999仅仅是添加额外的一行Xlat,我很容易为你完成.

最后returnXlat,因为你可以在一个64位保持最大值签订了只有大约9.2百万兆不需要条件.

但是,如果根据一些奇怪的要求,Oracle决定添加128位longer类型或1024位damn_long类型,那么你将为它做好准备:-)


最后,您可以使用一些小测试工具来验证功能.

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));
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以从输出中看到它为您提供所需的内容:

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
Run Code Online (Sandbox Code Playgroud)

而且,顺便说一句,请注意,将负数传递给此函数将导致字符串太长,无法满足您的要求,因为它遵循< THOU路径).我认为这没关系,因为你只在问题中提到了非负值.


小智 7

对于任何想要回合的人.这是一个很好的,易于阅读的解决方案,它利用了Java.Lang.Math库

 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);
        }
    }
Run Code Online (Sandbox Code Playgroud)


小智 6

我不知道这是不是最好的方法,但这就是我所做的.

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
Run Code Online (Sandbox Code Playgroud)

---代码---

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
}
Run Code Online (Sandbox Code Playgroud)

  • 仅适用于整数并显示“ 10.0k”而不是“ 10k” (2认同)

小智 6

这是我的代码。干净简单。

public static String getRoughNumber(long value) {
    if (value <= 999) {
        return String.valueOf(value);
    }

    final String[] units = new String[]{"", "K", "M", "B", "P"};
    int digitGroups = (int) (Math.log10(value) / Math.log10(1000));
    return new DecimalFormat("#,##0.#").format(value / Math.pow(1000, digitGroups)) + "" + units[digitGroups];

}
Run Code Online (Sandbox Code Playgroud)


小智 5

我的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");
    }
Run Code Online (Sandbox Code Playgroud)

调整它以使用CS公斤(1,024)而不是公制公斤,或添加更多单位很容易.它将1,000格式化为"1.0 k"而不是"1 k",但我相信这是无关紧要的.

为了满足更具体的要求"不超过四个字符",删除后缀之前的空格并调整中间块,如下所示:

if (value >= unit)
  {
  value /= unit;
  return (value).ToString(value >= unit * 9.95 ? "#,##0" : "#,##0.0") + suffixes[--j];
  }
Run Code Online (Sandbox Code Playgroud)


ste*_*ert 5

我的最爱.您可以使用"k"等作为十进制的指示符,这在电子域中很常见.这将为您提供额外的数字,而无需额外的空间

第二列尝试使用尽可能多的数字

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
Run Code Online (Sandbox Code Playgroud)

这是代码

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?";
}
}
Run Code Online (Sandbox Code Playgroud)