BigDecimal格式

时间:2014-04-14 12:41:14

标签: java bigdecimal

我想将Java中的BigDecimal格式化为8个字符(包括分隔符),舍入HALF-UP。 一些例子:

12345.6789 -> "12345.68"
123        -> "   123.0"
0.12345678 -> "0.123457"
1.5        -> "     1.5"
0.0045     -> "  0.0045"
12         -> "    12.0"

结果必须有前导空格,最多可填充8个字符。 什么是最简单的方法?

1 个答案:

答案 0 :(得分:2)

我很确定这不是最好的方式,但它是单向的。

首先请注意,如果您的号码在该时段之前超过六位数,或者通常在该时段之前超过width - 2位数,则.0的格式将不再有效。

String.format()使用了一半作为舍入方法,因此您可以将其用作第一步,将输出设置为八个字符。

BigDecimal b = new BigDecimal(0.0045);
String format = "%8f";
String str = String.format(format, b);

输出:

12345.678900
123.000000
0.123457
1.500000
0.004500
12.000000
123456.000000

默认情况下String.format()使用BigDecimal的精度为6。要获得自定义精度,您需要知道周期之前有多少位数,并从总宽度中减去此数字(周期本身为+ 1)。

width - digits - 1

要获得位数,您只需检查(number % radix) == number是否适用于radix = 10,100,1000,......只要它适合您就知道数字位数。

public static int digitsBeforePeriod(BigDecimal b) {
    int number = b.intValue();
    int radix = 10;
    int digits = 1;
    while((number % radix) != number) {
        radix *= 10;
        ++digits;
    }
    return digits;
}

所以下一步是修改格式:

BigDecimal b = new BigDecimal(0.0045);
int digits = digitsBeforePeriod(b);
String format = "%8." + (8 - digits - 1) + "f";
String str = String.format(format, b);

输出:

12345.68
123.0000
0.123457
1.500000
0.004500
12.00000
123456.0

仍然有很多0,但现在至少四舍五入是正确的。现在指定如果一个数字是一个整数,你想要用.0后缀打印,否则不打印。

为了实现这一点,可能还存在聪明的格式,但我没有想到更多。这种天真的方式很简单:

while(str.endsWith("0") && !str.endsWith(".0")) {
    str = str.substring(0, str.length()-1);
}

这将删除字符串的最后一个字符,直到字符串完全没有以0结尾或以.0结尾。

现在数字格式正确,但未正确对齐:

12345.68
123.0
0.123457
1.5
0.0045
12.0
123456.0

要对齐它们,只需再次使用String.format()

str = String.format("%" + width + "s", str);

输出:

12345.68
   123.0
0.123457
     1.5
  0.0045
    12.0
123456.0

在上下文中,这看起来如下所示。请注意,我是否包含了一个检查数字,否则数字可以格式化 - 如果不是,它将打印无效。你当然也可以只打印数字,我只是想表明这种格式的局限性。

public static String trimToWidth(BigDecimal b, int width) {
    String str = "Invalid";
    int digits = digitsBeforePeriod(b);

    // -2 for period and 0
    if(digits <= width - 2) {
        // use width and (width - digits - 1) as precision (-1 for period)
        String format = "%" + width + "." + (width - digits - 1) + "f";
        // rounds half-up
        str = String.format(format, b);

        // trim trailing 0s, unless it's .0
        while(str.endsWith("0") && !str.endsWith(".0")) {
            str = str.substring(0, str.length()-1);
        }
    }

    // add spaces in front
    str = String.format("%" + width + "s", str);

    return str;
}

public static int digitsBeforePeriod(BigDecimal b) {
    int number = b.intValue();
    int radix = 10;
    int d = 1;
    while((number % radix) != number) {
        radix *= 10;
        ++d;
    }
    return d;
}

public static void main(String[] args) {
    double[] values = new double[] {
            12345.6789, 123, 0.12345678,
            1.5, 0.0045, 12, 123456, 1234567
    };

    BigDecimal[] bigs = new BigDecimal[values.length];

    for(int i = 0; i < bigs.length; ++i) {
        bigs[i] = new BigDecimal(values[i]);
        System.out.println(trimToWidth(bigs[i], 8));
    }

}