Formatting BigDecimals

Discussion in 'Java' started by RedGrittyBrick, Feb 7, 2008.

  1. I want to print the result of a BigDecimal calculation to two decimal
    places.

    The following code produces an IllegalArgumentException "Digits < 0"

    String.format("%10.2f", new BigDecimal(0.0001));

    Can someone explain where I am going wrong?
     
    RedGrittyBrick, Feb 7, 2008
    #1
    1. Advertisements

  2. RedGrittyBrick

    none Guest

    "f" stands for float, not for BigDecimal.
    you can try java.text.DecimalFormat
     
    none, Feb 7, 2008
    #2
    1. Advertisements

  3. Apparently formatting of BigDecimals checks the value
    with the format and concludes that you need 4 decimals
    and have only room for 2.

    "%10.6f" as format works and so do 0.01 as value.

    You can "force" it to work with .setScale(2, RoundingMode.HALF_UP) !

    Arne
     
    Arne Vajhøj, Feb 7, 2008
    #3
  4. That's not his problem. "f" is the appropriate format conversion
    character for BigDecimal.

    His problem is related to the fact that his BigDecimal has 4 digits to
    the right of the decimal and the format string only has two.
     
    Knute Johnson, Feb 7, 2008
    #4
  5. RedGrittyBrick

    Eric Sosman Guest

    Actually, his BigDecimal has a couple dozen digits to
    the right of the decimal. What I can't fathom is why the
    format doesn't round at the second place:

    "If the precision is less than the number of digits
    which would appear after the decimal point in the
    string returned by Float.toString(float) or
    Double.toString(double) respectively, then the value
    will be rounded using the round half up algorithm."
    -- Javadoc (1.6) for Formatter, description of "f"
    when used with BigDecimal

    There's clearly something wrong with this quote from the
    Javadoc: despite claiming to describe BigDecimal's formatting,
    it's a verbatim quote from the Float and Double section,
    right down to the class names and method signatures ...
     
    Eric Sosman, Feb 7, 2008
    #5
  6. Not exactly:
    String.format("%10.2f", new BigDecimal(1.0001)); // "1.00"
    String.format("%10.2f", new BigDecimal(0.0001)); // IAE
    String.format("%10.2f", 0.0001); // "0.00"
    String.format("%10.2f", 0.0001d); // "0.00"

    All of these have four digits to the right of the decimal point. Only
    one of them throws an exception.

    String.format is documented in the Javadocs to work with BigDecimals
    using the 'f' conversion - but it fails for some values.
     
    RedGrittyBrick, Feb 7, 2008
    #6
  7. I wonder why it thinks I need 4 decimals to display 0.0001 to two
    decimal places if, and only if, the value is a BigDecimal?
    As does 1.0001 as a value. It seems it is related to the way BigDecimal
    stores values internally.
    Thanks, I'll try that.


    I've also found that I can get the result I want by using NumberFormat
    with .setMinimumFractionDigits(2).

    I'm reluctantly beginning to suspect there is a bug in the way
    String.format interacts with BigDecimal, or (indirectly) with
    java.math.MathContext.

    I think it is reasonable to expect String.format("%.2f",...) to return
    "0.00" when fed any value smaller than 0.005. It does for floats and
    doubles.
     
    RedGrittyBrick, Feb 7, 2008
    #7
  8. The two last examples are doubles not big decimals, so I think
    you can ignore them.

    The first example is interesting.

    It seems as if it is only a problem if the the most
    significant digits is being chopped out.

    Arne
     
    Arne Vajhøj, Feb 8, 2008
    #8
  9. RedGrittyBrick

    Stefan Ram Guest

    Here is another test:

    public class Main
    { public static void main( final java.lang.String[] args )
    { final java.math.BigDecimal bigDecimal
    = new java.math.BigDecimal( "0.001234" );
    for( int i = 9; i >= 0; -- i )try
    { final java.lang.String string
    = java.lang.String.format( "%%." + i + "f: %." + i + "f", bigDecimal );
    java.lang.System.out.println( string ); }
    catch( final java.lang.IllegalArgumentException illegalArgumentException )
    { java.lang.System.out.println( illegalArgumentException ); }}}

    %.9f: 0,001234000
    %.8f: 0,00123400
    %.7f: 0,0012340
    %.6f: 0,001234
    %.5f: 0,00123
    %.4f: 0,0012
    %.3f: 0,001
    %.2f: 0,001234
    java.lang.IllegalArgumentException: Digits < 0
    java.lang.IllegalArgumentException: Digits < 0

    java.util.Formatter creates a new java.math.MathContext.
    In the OP's case of "0.0001" with "%10.2f", this will
    try to create a math context with a negative precision
    »compPrec«, which will result in the throw as follows:

    public class Main
    { public static void main( final java.lang.String[] args )
    {
    final java.math.BigDecimal value
    = new java.math.BigDecimal( "0.0001" );

    final int precision = 2; /* as given in "%10.2f" */
    java.lang.System.out.println( "precision = " + precision );

    { /* as calculated in java.util.Formatter */

    final int prec = (precision == -1 ? 6 : precision);
    java.lang.System.out.println( "prec = " + prec );

    final int scale = value.scale();
    java.lang.System.out.println( "scale = " + scale );

    int compPrec = value.precision();
    if( scale > prec )compPrec -= (scale - prec);
    java.lang.System.out.println( "compPrec = " + compPrec );

    final java.math.MathContext mathContext =
    new java.math.MathContext( compPrec ); }}}

    precision = 2
    prec = 2
    scale = 4
    compPrec = -1
    Exception in thread "main" java.lang.IllegalArgumentException: Digits < 0
    at java.math.MathContext.<init>(Unknown Source)

    I have no opinion about whether this is by design or a bug.
     
    Stefan Ram, Feb 8, 2008
    #9
  10. It's related to a known bug:
    http://bugs.sun.com/view_bug.do?bug_id=6476425

    But I don't get the exception for "0.0000" reported there.
    System.out.printf("%.2f %n", new BigDecimal(0.0000)); // "0.00"
    System.out.printf("%.2f %n", new BigDecimal(0.0001)); // IAE

    I've filed a separate bug report.
     
    RedGrittyBrick, Feb 8, 2008
    #10
    1. Advertisements

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments (here). After that, you can post your question and our members will help you out.