0

I want to produce a (non-localized) string representing a double to 'n' decimal places, preferably rounded up. E.g. to four decimal places, 1.234567 -> "1.2346"

I know I can do this with BigDecimal, DecimalFormat or String.format(), but in my project I'm constrained and not able to use them or any third-party library. Is it possible to write a simple function to do it? How?

Update (more detail): The reason for this question is that I want a solution that uses identical code in GWT and in Java, and requires a minimal amount of code and libraries. It is vital that the number as formatted does not change based on the browser's locale (E.g. I don't want "1,2346" when a French user runs it).

In GWT, I have written this Javascript wrapper function:

/**
 * Convert a double to a string with a specified number of decimal places.
 * The reason we need this function is that by default GWT localizes
 * number formatting.
 *
 * @param d double value
 * @param decimalPlaces number of decimal places to include
 * @return non-localized string representation of {@code d}
 */
public static native String toFixed(double d, int decimalPlaces) /*-{
    return d.toFixed(decimalPlaces);
}-*/;

I want to replace it with some simple Java, so I can use the same code on both client and server.

public static String toFixed(double d, int decimalPlaces) {
    // TODO
}
ᴇʟᴇvᴀтᴇ
  • 12,285
  • 4
  • 43
  • 66
  • Ugly, but convert it to String, round, and cut decimal points or append zeroes if applicable – meskobalazs Feb 05 '15 at 13:23
  • 2
    Of course it's possible, it has been done before. Seeing how you have 4k rep, you should know by now that you should show your attempt or at least show research into the subject. Show effort, is my point – Vince Feb 05 '15 at 13:24
  • Why cant you use `Double.toString(double d)` ? Surely core classes are not considered third-party api. – vikingsteve Feb 05 '15 at 13:25
  • Take a look here: http://stackoverflow.com/questions/153724/how-to-round-a-number-to-n-decimal-places-in-java – Danilo Muñoz Feb 05 '15 at 13:27
  • Do a toString on the double and count how many chars after the decimal place or you multiply by 10k integer and convert to an integer. I'm not go to show the code as this seem scholastic. – Adam Gent Feb 05 '15 at 13:29
  • You're asking a question here for which you already have the solution, but you don't want to post the solution? Am I misunderstanding something? (And the question prohibits the best answers but doesn't say why - how can this question and the answers be useful for other people that way?) – Erwin Bolwidt Feb 05 '15 at 13:35
  • @vikingsteve Because `toString()` shows large and small numbers in exponential format `9.962E-4`. – ᴇʟᴇvᴀтᴇ Feb 05 '15 at 13:56
  • And what is wrong with "DecimalFormat" ? – Neil Stockton Feb 05 '15 at 13:57
  • @Neil It's not available in GWT and GWT's [NumberFormat](http://www.gwtproject.org/javadoc/latest/com/google/gwt/i18n/client/NumberFormat.html) is Locale-specific and doesn't seem to have any way to override it with a specific locale. Also, it brings in a fair amount of Javascript wrapper code that I don't want. – ᴇʟᴇvᴀтᴇ Feb 05 '15 at 14:00
  • "DecimalFormat" is part of the JDK, not GWT (a library), so perhaps you're actually talking about GAE here ? – Neil Stockton Feb 05 '15 at 14:02
  • GWT compiles Java into JavaScript and emulates the JDK libraries in JavaScript. GWT has implementations of most of the core JDK classes, but not all. Many of the implementations are incomplete or slightly different. I'm not using GAE. – ᴇʟᴇvᴀтᴇ Feb 05 '15 at 14:05

2 Answers2

1

Since you're in GWT, you can use NumberFormat if you first call NumberFormat.setForcedLatinDigits(true) to force the use of latin digits and separators.

If you want a specific locale other than the latin defaults, you'll have to create a class that extends NumberFormat and overrides getFormat to call the protected NumberFormat constructor and provide a NumberConstants implementation

Bjartr
  • 492
  • 3
  • 18
0

I have written the following function. It uses long internally so won't work for some very large or very small double values, but that's OK for my use cases:

public static String toFixed(double d, int decimalPlaces) {
    if (decimalPlaces < 0 || decimalPlaces > 8) {
        throw new IllegalArgumentException("Unsupported number of "
                + "decimal places: " + decimalPlaces);
    }
    String s = "" + Math.round(d * Math.pow(10, decimalPlaces));
    int len = s.length();
    int decimalPosition = len - decimalPlaces;
    StringBuilder result = new StringBuilder();
    if (decimalPlaces == 0) {
        return s;
    } else if (decimalPosition > 0) {
        // Insert a dot in the right place
        result.append(s.substring(0, decimalPosition));
        result.append(".");
        result.append(s.substring(decimalPosition));
    } else {
        result.append("0.");
        // Insert leading zeroes into the decimal part
        while (decimalPosition++ < 0) {
            result.append("0");
        }
        result.append(s);
    }
    return result.toString();
}

Tests:

@Test
public void formatsDoubleToSpecifiedNumberOfDecimalPlaces() {
    assertEquals("100.0000", toFixed(100d, 4));
    assertEquals("10.0000", toFixed(10d, 4));
    assertEquals("1.0000", toFixed(1d, 4));
    assertEquals("0.1000", toFixed(0.1d, 4));
    assertEquals("0.0100", toFixed(0.01d, 4));
    assertEquals("0.0010", toFixed(0.001d, 4));
    assertEquals("0.0001", toFixed(0.0001d, 4));
    assertEquals("0.0000", toFixed(0.00001d, 4));
    assertEquals("0.0000", toFixed(0.000001d, 4));

    assertEquals("12", toFixed(12.3456789d, 0));
    assertEquals("12.3", toFixed(12.3456789d, 1));
    assertEquals("12.35", toFixed(12.3456789d, 2));
    assertEquals("12.346", toFixed(12.3456789d, 3));
}

@Test
public void usesDotAsDecimalSeparatorRegardlessOfLocale() {
    Locale saved = Locale.getDefault();
    Locale.setDefault(Locale.FRANCE);
    try {
        assertEquals("123.4568", toFixed(123.456789d, 4));
    } finally {
        Locale.setDefault(saved);
    }
}

If you can improve on this, please comment or post a better answer. Thanks.

ᴇʟᴇvᴀтᴇ
  • 12,285
  • 4
  • 43
  • 66