Formatting BigDecimals

R

RedGrittyBrick

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?
 
N

none

RedGrittyBrick said:
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?

"f" stands for float, not for BigDecimal.
you can try java.text.DecimalFormat
 
A

Arne Vajhøj

RedGrittyBrick said:
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?

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
 
K

Knute Johnson

none said:
"f" stands for float, not for BigDecimal.
you can try java.text.DecimalFormat

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.
 
E

Eric Sosman

Knute said:
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.

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 ...
 
R

RedGrittyBrick

Knute said:
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.

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.
 
R

RedGrittyBrick

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

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?
"%10.6f" as format works and so do 0.01 as value.

As does 1.0001 as a value. It seems it is related to the way BigDecimal
stores values internally.
You can "force" it to work with .setScale(2, RoundingMode.HALF_UP) !

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.
 
A

Arne Vajhøj

RedGrittyBrick said:
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.

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
 
S

Stefan Ram

RedGrittyBrick said:
The following code produces an IllegalArgumentException "Digits < 0"
String.format("%10.2f", new BigDecimal(0.0001));

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.
 
R

RedGrittyBrick

Arne said:
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.

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.
 

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. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,903
Latest member
orderPeak8CBDGummies

Latest Threads

Top