Determine million, billion, trillion, quadrillion in Java

15,119

Solution 1

The first problem is that float does not have enough precision to represent these numbers. In fact, even double does not have enough precision for values in the Nonillion range - although this may not be so important here, because you obviously want to drop most of the information of this number anyhow.

Nevertheless, I have implemented it here using BigInteger. Converting this to use double should be straightforward, if you don't care about the precision issues.

The basic idea here is to create a NavigableMap from powers of 1000 to the respective number name. This map can be quickly looked up using floorEntry to find the best matching power (and thus, the number name).

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

Solution 2

I wouldn't use a float as it doesn't have much precision. Use double instead.

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

Solution 3

Easy Kotlin version

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

Solution 4

Personally, I use this. If you need decimals in the input numbers, you can use BigDecimal instead.

BigInteger/BigDecimal is better than Float or Double or Long as it can keep even bigger values.

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

I realize this is not the most efficient piece of code due to its length, but it works flawlessly.

All values under 1000 show up as their full value. Upon reaching 1000 it shows as 1.000k. The code is designed to ensure 3 decimal spaces at all times. Changing pvar settings down by one in the if statement(which sets it down to two decimals at all times.) Removing the if statement will set a more dynamic number that changes by max chars.

Some technical info about the max size of the BigINteger. Extracted from this question:

"There is no theoretical limit. The BigInteger class allocates as much memory as it needs for all the bits of data it is asked to hold.

There are, however, some practical limits, dictated by the memory available. And there are further technical limits, although you're very unlikely to be affected: some methods assume that the bits are addressable by int indexes, so things will start to break when you go above Integer.MAX_VALUE bits."

So if you are creating for computers, make sure you have enough memory for those massive numbers.

Share:
15,119
user3707836
Author by

user3707836

Updated on June 23, 2022

Comments

  • user3707836
    user3707836 about 2 years

    From this Standard dictionary numbers, I need a fastest way to convert some number below :

    1000000 = 1 Million
    1435234 = 1.43 Million
    350000000 = 350 Million
    1000000000 = 1 Billion
    1765000000 = 1.76 Billion
    1000000000000 = 1 Trillion
    1345342345000 = 1.34 Trillion
    1000000000000000 = 1 Quadrillion
    100000000000000000 = 100 Quadrillion

    and further.

    I have tried like this below:

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

    but I think my solution is not quite true. So, what is a fastest way to do that in Java? Many Thanks.

  • Marco13
    Marco13 about 10 years
    Maybe I overrated the part of the question that said "...Quadrillion and further" ...
  • Durandal
    Durandal about 10 years
    This surely isn't the fastest, but +1 for the sheer range anyway :)
  • Marco13
    Marco13 about 10 years
    Sure, for the given set of names, a linear array search would probably be faster. But the O(logn) access of the TreeMap will pay out when you want to extend this to en.wikipedia.org/wiki/Graham%27s_number ;-)
  • Durandal
    Durandal about 10 years
    You could get it down to O(1) by just taking the length of numberString for determining the magnitude, then taking just the relevant first 3 (maybe 4 for proper rounding) digits to get the number. Although the limit to 2^31-1 characters length for Strings may get in the way sometimes :)
  • Marco13
    Marco13 about 10 years
    @Durandal That's a neat idea, in fact (ignoring the "theoretical" implications, like that you have to create a string representation, which is O(logn) in length, and the current method solely works on numbers).