views:

174

answers:

4

In Java, I am trying to get DecimalFormat to enforce the sign on an exponent sign. When it is positive I need a plus sign to appear. From what I have read this seems like a no brainer, but for myself it always throws up an error. I appreciate that there may be other methods to achieve my goal, but I would like to understand why in this specific method the error is occurring.

Double result = 123.456;
String sresult;

//This works
NumberFormat formatter = new DecimalFormat("0.00000E00");
        sresult = formatter.format(result); 
        System.out.println(sresult);        //1.23456E02

//This doesn't work
formatter = new DecimalFormat("0.00000E+00"); //Want to enforce the sign to appear
        sresult = formatter.format(result); 
        System.out.println(sresult);        //Expected 1.23456E+02 but error occurs

The error which is thrown up:

Exception in thread "main" java.lang.IllegalArgumentException:
Malformed exponential pattern "0.00000E+00"
    at java.text.DecimalFormat.applyPattern(Unknown Source)
    at java.text.DecimalFormat.(Unknown Source)
    at deccheck.main(deccheck.java:13)

I appreciate any insight. Thanks, Mark

+3  A: 

Easy way:

formatter = new DecimalFormat("0.00000E00"); // Want to enforce the sign to appear
sresult = formatter.format(result);
if (!sresult.contains("E-")) { //don't blast a negative sign
    sresult = sresult.replace("E", "E+");
}
System.out.println(sresult);

Outputs 1.23456E+02 for your example.

But I don't believe there's a way to do it from inside the DecimalFormat pattern. Or at least the javadoc doesn't indicate there is one.

Edit: trashgod brings up a good point. You'd probably want to get positive and negative signs from DecimalFormatSymbols if you plan on localizing this to different regions.

Edit 2: Andrei pointed out that E is also a localization variable. Shows what I know about localization.

j flemm
In the case of localization, E should also be used from DecimalFormatSymbols.getExponentSeparator(). The problem with this, is that you can't really use them directly, without checking if you should escape them in the regex or not (you would need an extra check to see if it's a special character).
Andrei Fierbinteanu
@Andrei: +1 Yup, I forgot about that too.
j flemm
Thanks. Thats great.
Mark
+1  A: 

I don't think the '+' character is an accepted character in a pattern (after studying the javadocs). And the pattern for for the exponential part si described as:

 Exponent:
         E MinimumExponent
 MinimumExponent:
         0 MinimumExponent(opt)

If you add anything there it will confuse the parser. I think your only option is to give it the normal pattern (without '+') and then get the String and use regex to add the '+' there.

sresult = formatter.format(result);
sresult = sresult.replaceAll("E([^\\-]+)", "E+$1");

The best option might be extending DecimalFormat and doing this in the overwritten format method

Andrei Fierbinteanu
+1  A: 

@j flemm's approach is appealing because "Negative exponents are formatted using the localized minus sign, not the prefix and suffix from the pattern."—DecimalFormat.

trashgod
Thank you everybody. I appreciate all of the comments.I thought the javadocs did mention the ability to use the + sign, but I was confused with the information presented at this link:http://www.docjar.com/docs/api/java/text/DecimalFormat.html I shall implement the simple method as suggested by j flemm as the localization should not affect me.
Mark
@Mark: It looks like a feature of Apache Harmony http://harmony.apache.org/. Be sure to document the potential localization problem in your code.
trashgod
+2  A: 

I would like to extend the solution by j flemm. The "E" and "-" are no constants, they can be set as DecimalFormatSymbols. Therefore the substitutions must respect this:

public static String hoola(final String s, final DecimalFormatSymbols symbols) {
    String result;
    final String expo = symbols.getExponentSeparator();
    final char minus = symbols.getMinusSign();
    if (!s.contains(expo + minus)) { // don't blast a negative sign
    result = s.replace(expo, expo + '+');
    } else {result=s;}
    return result;
}

/**
 * @param args
 */
public static void main(final String[] args) {
    final DecimalFormat decForm = (DecimalFormat) NumberFormat
            .getInstance(Locale.GERMAN);
    final DecimalFormatSymbols newSymbols = new DecimalFormatSymbols(
            Locale.GERMAN);
    newSymbols.setExponentSeparator("*10^");
    newSymbols.setMinusSign('\u2212');
    decForm.setDecimalFormatSymbols(newSymbols);
    decForm.applyPattern("0.00000E00");
    System.out.println(hoola(decForm.format(1234.567), decForm
            .getDecimalFormatSymbols()));
    System.out.println(hoola(decForm.format(000.00567), decForm
            .getDecimalFormatSymbols()));
}

Result is:

  • 1,23457*10^+03
  • 5,67000*10^−03
Michael Konietzka
And yes, "bad" patterns will break this code. A safe way to implement the desireed behaviour would be to implement an own extension of NumberFormat for eg.
Michael Konietzka
I like it. Might as well pull the `+` symbol in `hoola(..)` from the `DecimalFormatSymbols` too.
j flemm
@j flemm: I wanted to, but the `+` is not changeable if I interpret the javadocs correctly.
Michael Konietzka
@Michael: Good point. I just noticed that too. It makes sense since `DecimalFormat` doesn't use `+`. It's probably in one of the other localization constant classes.
j flemm