如何使用 DecimalFormat() 只输出有效的小数位
how to use DecimalFormat() to only output significant fractional digits
给定 0.01835132889570552
,数字 1835
很重要(可能需要四舍五入),数字 132889570552
不重要,所以我们不想把它们放在一起进入我们用户的输入系统。
小数点左边的数字有效,小数点右边的数字最少为4位。
我们需要通过这个测试:
assertEquals("1,835.1329", formatInsignificantDigits(1835.13288957055));
assertEquals("183.5133", formatInsignificantDigits(183.513288957055));
assertEquals("18.3513", formatInsignificantDigits(18.3513288957055));
assertEquals("1.8351", formatInsignificantDigits(1.83513288957055));
assertEquals("0.1835", formatInsignificantDigits(0.183513288957055));
assertEquals("0.01835", formatInsignificantDigits(0.0183513288957055));
assertEquals("0.001835", formatInsignificantDigits(0.00183513288957055));
assertEquals("0.0001835", formatInsignificantDigits(0.000183513288957055));
请注意 183.5133
由 0.0001
四舍五入。
所以问题是:如何写formatInsignificantDigits()
而不诉诸蛮力或字符串手术?我会用一点蛮力回答我自己的问题,然后我们看看其他人想出了什么。
回答“我的小数位数有多重要?”这个问题的数学函数是 Math.log10()
。因此,我们将分数转换为负对数,并将它们转换为正数,表示我们需要填充多少个零。然后我们加 4 得到所有数字,我们设置精度:
public static @NonNull String formatInsignificantDigits(double input) {
DecimalFormat df = new DecimalFormat();
//noinspection NumericCastThatLosesPrecision
int digits = 4 + (int) -Math.log10(input);
df.setMaximumFractionDigits(Math.max(digits, 4));
return df.format(input);
}
有没有人有更简洁的方法来做到这一点?还是我们忽略的DecimalFormat
的函数?
结果这个算法将 -0.03997394115
转换为 "0.04"
,因为向左级联向上舍入,然后 DecimalFormat
丢失了尾随零。在某些情况下这可能是不可接受的...
不确定这在干净/brute-force 连续体中的位置,但您也可以使用 BigDecimal
和 MathContext
来管理精度:
public static String formatInsignificantDigits(double value) {
DecimalFormat format = DecimalFormat.getInstance();
format.setMaximumFractionDigits(Integer.MAX_VALUE);
BigDecimal bigDecimal = new BigDecimal(value);
int wholeDigits = Math.max(bigDecimal.precision() - bigDecimal.scale(), 0);
return format.format(bigDecimal.round(new MathContext(wholeDigits + 4)));
}
给定 0.01835132889570552
,数字 1835
很重要(可能需要四舍五入),数字 132889570552
不重要,所以我们不想把它们放在一起进入我们用户的输入系统。
小数点左边的数字有效,小数点右边的数字最少为4位。
我们需要通过这个测试:
assertEquals("1,835.1329", formatInsignificantDigits(1835.13288957055));
assertEquals("183.5133", formatInsignificantDigits(183.513288957055));
assertEquals("18.3513", formatInsignificantDigits(18.3513288957055));
assertEquals("1.8351", formatInsignificantDigits(1.83513288957055));
assertEquals("0.1835", formatInsignificantDigits(0.183513288957055));
assertEquals("0.01835", formatInsignificantDigits(0.0183513288957055));
assertEquals("0.001835", formatInsignificantDigits(0.00183513288957055));
assertEquals("0.0001835", formatInsignificantDigits(0.000183513288957055));
请注意 183.5133
由 0.0001
四舍五入。
所以问题是:如何写formatInsignificantDigits()
而不诉诸蛮力或字符串手术?我会用一点蛮力回答我自己的问题,然后我们看看其他人想出了什么。
回答“我的小数位数有多重要?”这个问题的数学函数是 Math.log10()
。因此,我们将分数转换为负对数,并将它们转换为正数,表示我们需要填充多少个零。然后我们加 4 得到所有数字,我们设置精度:
public static @NonNull String formatInsignificantDigits(double input) {
DecimalFormat df = new DecimalFormat();
//noinspection NumericCastThatLosesPrecision
int digits = 4 + (int) -Math.log10(input);
df.setMaximumFractionDigits(Math.max(digits, 4));
return df.format(input);
}
有没有人有更简洁的方法来做到这一点?还是我们忽略的DecimalFormat
的函数?
结果这个算法将 -0.03997394115
转换为 "0.04"
,因为向左级联向上舍入,然后 DecimalFormat
丢失了尾随零。在某些情况下这可能是不可接受的...
不确定这在干净/brute-force 连续体中的位置,但您也可以使用 BigDecimal
和 MathContext
来管理精度:
public static String formatInsignificantDigits(double value) {
DecimalFormat format = DecimalFormat.getInstance();
format.setMaximumFractionDigits(Integer.MAX_VALUE);
BigDecimal bigDecimal = new BigDecimal(value);
int wholeDigits = Math.max(bigDecimal.precision() - bigDecimal.scale(), 0);
return format.format(bigDecimal.round(new MathContext(wholeDigits + 4)));
}