I'm writing an app that needs to output Decimals of varying lengths, and varying scale to strings without the decimal point for writing to a flat file as input to another system. e.g.
12345 -> Length:10, Scale:2 -> 0001234500
123.45 -> Length:10, Scale:2 -> 0000012345
123.45 -> Length:10, Scale:3 -> 0000123450
-123.45 -> Length:10, Scale:3, signed:true -> -000123450
123.45 -> Length:10, Scale:3, signed:true -> +000123450
The function I have written to handle this is below and is going to be called hundreds of thousands of times so I want to make sure that there's not a better, more efficient way to do this. I've looked at ways to get DecimalFormat to do more for me but I can't see it handling my need to format with decimal places but without decimal point.
protected String getFormattedDecimal( String value, int len, int scale, Boolean signed ) throws Exception{
StringBuffer retVal = new StringBuffer();
//Need a BigDecimal to facilitiate correct formatting
BigDecimal bd = new BigDecimal( value );
//set the scale to ensure that the correct number of zeroes
//at the end in case of rounding
bd = bd.setScale( scale );
//taking it that if its supposed to have negative it'll be part of the num
if ( ( bd.compareTo( BigDecimal.ZERO ) >= 0 ) && signed ){
retVal.append( "+" );
}
StringBuffer sbFormat = new StringBuffer();
for (int i = 0; i < len; i++)
{
sbFormat.append('0');
}
DecimalFormat df = new DecimalFormat( sbFormat.toString() );
retVal.append( df.format( bd.unscaledValue() ) );
return retVal.toString();
}
format("%. 2f", 1.23456); This will format the floating point number 1.23456 up-to 2 decimal places, because we have used two after decimal point in formatting instruction %.
ToString("F2") will always show 2 decimal places like 123.40 . You'll have to use 0.
The string "#. ##" indicate that we are formatting up-to 2 decimal points, "#. ###" indicates formatting number up-to 3 decimal places. By the way, even DecimalFormat rounds the number if next decimal point is more than 5.
2f", val); In short, the %. 2f syntax tells Java to return your variable ( val ) with 2 decimal places ( . 2 ) in decimal representation of a floating-point number ( f ) from the start of the format specifier ( % ).
My performance-enhanced implementation is below. It is about 4.5 times as fast as the DecimalFormatter-based solution: running on my machine, using Eclipse with a decent home-brewed test harness, the results are:
Some notes:
Also: in order to mimic the behavior of the original (but not given in the comments), this:
package com.pragmaticsoftwaredevelopment.stackoverflow;
...
final static String formatterZeroes="00000000000000000000000000000000000000000000000000000000000";
protected static String getFormattedDecimal ( String value, int len, int scale, Boolean signed ) throws IllegalArgumentException {
if (value.length() == 0) throw new IllegalArgumentException ("Cannot format a zero-length value");
if (len <= 0) throw new IllegalArgumentException ("Illegal length (" + len + ")");
StringBuffer retVal = new StringBuffer();
String sign=null;
int numStartIdx;
if ("+-".indexOf(value.charAt(0)) < 0) {
numStartIdx=0;
} else {
numStartIdx=1;
if (value.charAt(0) == '-')
sign = "-";
}
if (signed && (value.charAt(0) != '-'))
sign = "+";
if (sign==null)
sign="";
retVal.append(sign);
int dotIdx = value.indexOf('.');
int requestedWholePartLength = (len-scale);
if (dotIdx < 0) {
int wholePartPadLength = (requestedWholePartLength - ((value.length()-numStartIdx)));
if (wholePartPadLength > 0)
retVal.append(formatterZeroes.substring(0, wholePartPadLength));
retVal.append (value.substring(numStartIdx));
if (scale > 0)
retVal.append(formatterZeroes.substring(0, scale));
}
else {
int wholePartPadLength = (requestedWholePartLength - (dotIdx - numStartIdx));
if (wholePartPadLength > 0)
retVal.append(formatterZeroes.substring(0, wholePartPadLength));
retVal.append (value.substring(numStartIdx, dotIdx));
retVal.append (value.substring (dotIdx+1));
int fractionalPartPadLength = (scale - (value.length() - 1 - dotIdx));
if (fractionalPartPadLength > 0)
retVal.append(formatterZeroes.substring(0, fractionalPartPadLength));
}
return retVal.toString();
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With