【问题标题】:Determine million, billion, trillion, quadrillion in Java在 Java 中确定百万、十亿、万亿、千万亿
【发布时间】:2014-06-26 15:29:10
【问题描述】:

从这个Standard dictionary numbers,我需要一种最快的方法来转换下面的一些数字:

1000000 = 100 万
1435234 = 143 万
350000000 = 3.5亿
1000000000 = 10 亿
1765000000 = 17.6亿
1000000000000 = 1 万亿
1345342345000 = 1.34 万亿
1000000000000000 = 1 万亿
100000000000000000 = 100 万亿

还有更多。

我在下面尝试过这样的:

public String truncateNumber(float floatNumber) {
    long million = 1000000L;
    long billion = 1000000000L;
    long trillion = 1000000000000L;
    long number = Math.round(floatNumber);
    if ((number >= million) && (number < billion)) {
        float fraction = calculateFraction(number, million);
        return Float.toString(fraction) + "M";
    } else if ((number >= billion) && (number < trillion)) {
        float fraction = calculateFraction(number, billion);
        return Float.toString(fraction) + "B";
    }
    return Long.toString(number);
}

public float calculateFraction(long number, long divisor) {
    long truncate = (number * 10L + (divisor / 2L)) / divisor;
    float fraction = (float) truncate * 0.10F;
    return fraction;
}

但我认为我的解决方案并不完全正确。那么,在 Java 中最快的方法是什么?非常感谢。

【问题讨论】:

    标签: java


    【解决方案1】:

    第一个问题是float 没有足够的精度来表示这些数字。事实上,即使double 对 Nonillion 范围内的值也没有足够的精度——尽管这在这里可能不是那么重要,因为无论如何你显然都想删除这个数字的大部分信息。

    不过,我在这里使用BigInteger 实现了它。如果您不关心精度问题,将其转换为使用 double 应该很简单。

    这里的基本思想是从 1000 的幂到相应的数字名称创建一个 NavigableMap。可以使用floorEntry 快速查找此地图,以找到最佳匹配功率(以及号码名称)。

    import java.math.BigInteger;
    import java.util.Map.Entry;
    import java.util.NavigableMap;
    import java.util.TreeMap;
    
    public class NumberNames
    {
        public static void main(String[] args)
        {
            test("100", "Nearly nothing");
            test("1000", "1 Thousand");
            test("1230", "1.23 Thousand");
            test("1000000", "1 Million");
            test("1435234", "1.43 Million");
            test("350000000", "350 Million");
            test("1000000000", "1 Billion");
            test("1765000000", "1.76 Billion");
            test("1000000000000", "1 Trillion");
            test("1345342345000", "1.34 Trillion");
            test("1000000000000000", "1 Quadrillion");
            test("100000000000000000", "100 Quadrillion");
            test("1230000000000000000000000000000000000000000000000000000000000000", "1.23 Vigintillion");
        }
    
        private static void test(String numberString, String string)
        {
            BigInteger number = new BigInteger(numberString);
            System.out.println(number+" is "+createString(number)+" should be "+string);
        }
    
    
    
        private static final String NAMES[] = new String[]{
            "Thousand",
            "Million",
            "Billion",
            "Trillion",
            "Quadrillion",
            "Quintillion",
            "Sextillion",
            "Septillion",
            "Octillion",
            "Nonillion",
            "Decillion",
            "Undecillion",
            "Duodecillion",
            "Tredecillion",
            "Quattuordecillion",
            "Quindecillion",
            "Sexdecillion",
            "Septendecillion",
            "Octodecillion",
            "Novemdecillion",
            "Vigintillion",
        };
        private static final BigInteger THOUSAND = BigInteger.valueOf(1000);
        private static final NavigableMap<BigInteger, String> MAP;
        static
        {
            MAP = new TreeMap<BigInteger, String>();
            for (int i=0; i<NAMES.length; i++)
            {
                MAP.put(THOUSAND.pow(i+1), NAMES[i]);
            }
        }
    
        public static String createString(BigInteger number)
        {
            Entry<BigInteger, String> entry = MAP.floorEntry(number);
            if (entry == null)
            {
                return "Nearly nothing";
            }
            BigInteger key = entry.getKey();
            BigInteger d = key.divide(THOUSAND);
            BigInteger m = number.divide(d);
            float f = m.floatValue() / 1000.0f;
            float rounded = ((int)(f * 100.0))/100.0f;
            if (rounded % 1 == 0)
            {
                return ((int)rounded) + " "+entry.getValue();
            }
            return rounded+" "+entry.getValue();
        }
    }
    

    【讨论】:

    • 这肯定不是 最快的,但无论如何 +1 绝对范围 :)
    • 当然,对于给定的名称集,线性数组搜索可能会更快。但是,当您想将其扩展到 en.wikipedia.org/wiki/Graham%27s_number 时,TreeMap 的 O(logn) 访问将得到回报;-)
    • 你可以通过取 numberString 的长度来确定大小,然后只取相关的前 3 位(可能是 4 位进行正确舍入)来获得数字,从而将其降低到 O(1)。 虽然字符串的 2^31-1 个字符长度的限制有时会妨碍 :)
    • @Durandal 事实上,这是一个好主意(忽略“理论”含义,就像你 必须 创建一个长度为 O(logn) 的字符串表示形式) , 而当前方法仅适用于 numbers)。
    【解决方案2】:

    我不会使用float,因为它的精度不高。改用双精度。

    static final long MILLION = 1000000L;
    static final long BILLION = 1000000000L;
    static final long TRILLION = 1000000000000L;
    
    public static String truncateNumber(double x) {
        return x < MILLION ?  String.valueOf(x) :
               x < BILLION ?  x / MILLION + "M" :
               x < TRILLION ? x / BILLION + "B" : 
                              x / TRILLION + "T";
    }
    

    【讨论】:

    • 也许我高估了问题的一部分,即 "...Quadrillion and more" ...
    【解决方案3】:

    就个人而言,我使用它。如果您在输入数字中需要小数,您可以使用 BigDecimal。

    BigInteger/BigDecimal 比 Float 或 Double 或 Long 更好,因为它可以保持更大的值。

     public static String customFormat(String pattern, BigInteger value) {
            //To force the output to be equal if the language is set to english, spanish, norwegian or japanese.
            NumberFormat nf = NumberFormat.getNumberInstance(Locale.ENGLISH);
            DecimalFormat df = (DecimalFormat)nf;
            df.applyPattern(pattern);
            String output = df.format(value);
            return output;
    
    
        }
        public static String numberConverter(BigInteger input) {
            String points = customFormat("###,###,###,###,###,###,###,###,###.###", input);
            String[] letters = new String[]{"Kc","Mc","Gc","Tc","Pc","Ec","Zc","Yc","Bc"};//your value names. Is not limited to two letters. Can also be more based on your maximum amount
            int size = points.length();
            String after = points;
            if (size > 3) {
                int firstPoint = points.indexOf(".");
    
                String re = points;
                re = points.substring(0,3);
                System.out.println(re);
                int pVar = 7;
                if(re.contains(",")){
                    String[] parts = re.split(",");
                    if(parts[0].length() == 2){
                        pVar = 6;
                    }else if(parts[0].length() == 1){
                        pVar = 5;
                    }
                }
    
                after = points.substring(0, pVar);
                int x = (size - firstPoint - 4/*3*/)/5;
                String bafter = after + " " + letters[x];//adds the value designation to the letter.
                after = bafter;
            }
    
            return after;
        }
    

    我意识到这不是最有效的代码,因为它很长,但它完美无缺。

    1000 以下的所有值都显示为其完整值。达到 1000 后,它显示为 1.000k。该代码旨在始终确保 3 个小数位。在 if 语句中将 pvar 设置减一(始终将其设置为两位小数。)删除 if 语句将设置一个更动态的数字,该数字会以最大字符数变化。

    有关 BigINteger 最大大小的一些技术信息。摘自this question

    "没有理论上的限制。BigInteger 类为其要求保存的所有数据位分配所需的内存。

    但是,存在一些实际限制,取决于可用内存。还有进一步的技术限制,尽管您不太可能受到影响:一些方法假设这些位可以通过 int 索引寻址,因此当您超过 Integer.MAX_VALUE 位时,事情就会开始中断。”

    因此,如果您是为计算机创作,请确保您有足够的内存来存储这些海量数字。

    【讨论】:

      【解决方案4】:

      简单的 Kotlin 版本

          const val MILLION = 1000000L
          const val BILLION = 1000000000L
          const val TRILLION = 1000000000000L
      
          fun appendMillions(x: Long): String? {
              return when {
                  x < MILLION -> x.toString()
                  x < BILLION -> "${x.times(100).div(MILLION).times(0.01)}M"
                  x < TRILLION -> "${x.times(100).div(BILLION).times(0.01)}B"
                  else -> "${x.times(100).div(TRILLION).times(0.01)}T"
              }
          }
      

      【讨论】:

        猜你喜欢
        • 2021-12-14
        • 1970-01-01
        • 2020-12-06
        • 2022-01-24
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2020-09-02
        相关资源
        最近更新 更多