简体   繁体   中英

Java: Format double to String with K, M, B

999 to 999 1000 to 1k

1500000 to 1.5m

and so on, I would like to not lose any precision at all

Also, need to convert them back to their original value

1.5m to 1500000 etc

The highest it would go is 11 digits max

Thanks

How about this:

import static java.lang.Double.parseDouble;
import static java.util.regex.Pattern.compile;

import java.util.regex.Matcher;
import java.util.regex.Pattern;

...

private static final Pattern REGEX = compile("(\\d+(?:\\.\\d+)?)([KMG]?)");
private static final String[] KMG = new String[] {"", "K", "M", "G"};

static String formatDbl(double d) {
  int i = 0;
  while (d >= 1000) { i++; d /= 1000; }
  return d + KMG[i];
}

static double parseDbl(String s) {
  final Matcher m = REGEX.matcher(s);
  if (!m.matches()) throw new RuntimeException("Invalid number format " + s);
  int i = 0;
  long scale = 1;
  while (!m.group(2).equals(KMG[i])) { i++; scale *= 1000; }
  return parseDouble(m.group(1)) * scale;
}

If they weren't final you could extend java.lang.Integer etc. to override the toString() method. Is it worth creating a java.lang.Number subclass? Probably not. You could create your own classes: MyInteger, MyFloat etc using composition (they'll have an attribute to hold the numeric value) and override the toString() method to return the formats you want.

For the other way around, you could create factory methods in your MyXXX classes that creates objects containing the numeric value of the string (such as "1m").

The good thing is that kind of work lends itself well for unit testing.

You could probably obtain what you want by using directly a NumberFormat subclass but depending on how you're going to use it, the above design could be better.

static String[] prefixes = {"k","M","G"};

// Formats a double to a String with SI units
public static String format(double d) {
    String result = String.valueOf(d);
    // Get the prefix to use
    int prefixIndex = (int) (Math.log10(d) / Math.log10(10)) / 3;
    // We only have a limited number of prefixes
    if (prefixIndex > prefixes.length)
        prefixIndex = prefixes.length;
    // Divide the input to the appropriate prefix and add the prefix character on the end
    if (prefixIndex > 0)
        result = String.valueOf(d / Math.pow(10,prefixIndex*3)) + prefixes[prefixIndex-1];
    // Return result
    return result;
}
// Parses a String formatted with SI units to a double
public static double parse(String s) {
    // Retrieve the double part of the String (will throw an exception if not a double)
    double result = Double.parseDouble(s.substring(0,s.length()-1));
    // Retrieve the prefix index used
    int prefixIndex = Arrays.asList(prefixes).indexOf(s.substring(s.length()-1)) + 1;
    // Multiply the input to the appropriate prefix used
    if (prefixIndex > 0)
        result = result * Math.pow(10,prefixIndex*3);
    return result;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM