Me gustaría convertir un valor BigDecimal en una cadena que no tenga más de un cierto número de caracteres, cambiando a notación científica si es necesario. ¿Cómo puedo hacer esto?
En otras palabras, ¿cómo puedo escribir la formatDecimal(BigDecimal number, int maxChars)
función que pasaría las siguientes pruebas:
final int maxChars = 6;
assertEquals(
"0.3333",
formatDecimal(new BigDecimal(0.3333333333333), maxChars)
);
assertEquals(
"1.6E+6",
formatDecimal(new BigDecimal(1555555), maxChars)
);
assertEquals(
"1.6E-5",
formatDecimal(new BigDecimal(0.0000155), maxChars)
);
assertEquals(
"1234.6",
formatDecimal(new BigDecimal(1234.56789), maxChars)
);
assertEquals(
"123",
formatDecimal(new BigDecimal(123), maxChars)
);
assertEquals(
"0",
formatDecimal(new BigDecimal(0), maxChars)
);
En mi aplicación, maxChars
realmente no necesita ser un parámetro: se solucionará en el momento de la compilación (a 18), por lo que una solución que use, por ejemplo, DecimalFormat("#.####")
podría funcionar, siempre que el cambio a la notación científica se pueda administrar correctamente.
Una solución trivial si desea limitar solo el número de decimales sería
public static String formatDecimal(BigDecimal b, int max) {
return b.setScale(max, RoundingMode.HALF_EVEN).stripTrailingZeros().toEngineeringString();
}
Ahora, también desea limitar el número de dígitos de fracción dependiendo de los dígitos en la parte entera. Creo que es posible jugar mejor con las clases DecimalFormat y MessageFormat, esto es lo que puedo ofrecer, pasa los casos de prueba, pero no creo que sea tan robusto. Puede intentar crear un mejor algoritmo para manejar todos los casos diferentes.
public static String formatDecimal(BigDecimal b, int max) {
// trivial case
String bs = b.stripTrailingZeros().toPlainString();
if (bs.length() <= max) {
return bs;
}
// determine the max integer = 1.0Emax
String maxInteger = "1" + StringUtils.repeat("0", max - 1);
// determine the min fraction = 1.0E-max
String minFraction = "0." + StringUtils.repeat("0", max - 2) + "1";
// get the integer part
String integerPart = String.valueOf(b.intValue());
// make the pattern like ###.### with the correct repetition
String pattern = StringUtils.repeat("#", max - integerPart.length()) + "." + StringUtils.repeat("#", max - 1 - integerPart.length());
// play with Message format, using a choice to determine when to use the exponential format
MessageFormat fmt = new MessageFormat( //
"{0,choice," + minFraction + "<{0,number,'0.#E0'}|0.1#{0,number,'" + pattern + "'}|" + maxInteger + "<{0,number,'0.#E0'}}" //
);
// time to format the number
return fmt.format(new Object[] {b});
}
Otra solución usando if / else podría iniciarse así:
public static String formatDecimal(BigDecimal b, int max) {
// trivial case
if (b.toPlainString().length() <= max)
return b.toPlainString();
else {
// between 0 and 1, or superior than 1*10^max, better use the exponential notation
if ((b.compareTo(BigDecimal.ZERO) > 0 && b.compareTo(new BigDecimal("0.01")) < 0) //
|| (b.compareTo(new BigDecimal("1" + StringUtils.repeat("0", max - 1))) > 0)) {
return new DecimalFormat("#.#E0").format(b);
} else { // set Scale for fraction, better keep integer part safe
String sb = b.toPlainString();
return b.setScale(max - sb.indexOf(".") - 1, RoundingMode.HALF_EVEN).stripTrailingZeros().toPlainString();
}
}
}
public static String formatDecimal(BigDecimal bd, int maxChars)
{
String convert = bd.toString();
String result=" ";
char[] cArray = convert.toCharArray();
int decPos = -1;
int chrsBefore = 0;
int chrsAfter = 0;
int zeroesAfter = 0;
int currentPos = 0;
boolean zeroStreakAlive = true;
if (cArray.length>maxChars){
for (char c : cArray){
if (c=='.')
decPos = currentPos;
else if (decPos == -1)
chrsBefore++;
else if (zeroStreakAlive && c=='0'){
zeroesAfter++;
chrsAfter++;
}
else
chrsAfter++;
currentPos++;
}
if (chrsBefore>maxChars)
result = cArray[0] + "." + cArray[1] + "E+" + (chrsBefore-1);
else if (zeroesAfter >= maxChars-2)
result = cArray[zeroesAfter+2] + "." + cArray[zeroesAfter+3] + "E-" + (chrsAfter-2);
else
//your logic here probably using DecimalFormat for a normally rounded number to 6 places(including decimal point if one exists).
}
return result;
}
}
Esto es lo que se me ocurrió. No todo es abarcativo, ya que hay muchos casos que abordar. No sé exactamente cómo quieres que se desarrollen todos, así que no puedo hacerlos todos, pero deberías ser capaz de tener la idea y comenzar desde aquí.
Instalé QGIS a través de ppa: ubuntugis / ubuntugis-inestable. Mi Ubuntu 12.04 viene con OpenJDK 1.7.0. Cuando inicio qgis desde la línea de comando, se devuelve el siguiente mensaje de error. /usr/bin/qgis.bin: ...
Estoy tratando de descubrir cómo hacer texto no editable (no un JTextField) cuyo color de fondo cambia cuando el mouse pasa sobre él. Intenté usar JButton implementando ActionListener y ocultando ...
Yo uso Apache POI en un proyecto Java. He trabajado en una página horizontal, con el siguiente código: private void changeOrientation (documento XWPFDocument, orientación de cadena) {CTDocument1 doc = ...
Tengo una aplicación web basada en Java EE que se ejecuta en Tomcat y Spring 3.0. Mi página web envía una solicitud para eliminar un gran conjunto de registros. Mientras la solicitud se ejecuta en segundo plano, se agota el tiempo de espera ...