【发布时间】:2011-09-27 16:11:50
【问题描述】:
我想将 Java BigDecimal 舍入到一定数量的有效数字(不是小数位),例如到 4 位数:
等等。基本问题是如何找到BigDecimal的数量级,这样我就可以决定在小数点后使用多少位。
我能想到的只是一些可怕的循环,除以 10 直到结果为
顺便说一句,这个数字可能非常大(或非常小),所以我无法将其转换为双精度以使用登录。
【问题讨论】:
标签: java bigdecimal
我想将 Java BigDecimal 舍入到一定数量的有效数字(不是小数位),例如到 4 位数:
等等。基本问题是如何找到BigDecimal的数量级,这样我就可以决定在小数点后使用多少位。
我能想到的只是一些可怕的循环,除以 10 直到结果为
顺便说一句,这个数字可能非常大(或非常小),所以我无法将其转换为双精度以使用登录。
【问题讨论】:
标签: java bigdecimal
为什么不直接使用round(MathContext)?
BigDecimal value = BigDecimal.valueOf(123456);
BigDecimal wantedValue = value.round(new MathContext(4, RoundingMode.HALF_UP));
【讨论】:
MathContext 对我来说是一个盲点,但在这里它是一个很好的匹配。
divide(divisor, scale, ROUNDING_MODE),那么就有一个明确的结束。在这种情况下,计算的结束由 scale 定义,而不是由 precision 定义。我的声明是,MathContext 不允许轻松封装 scale 和舍入模式,仅适用于 precision 和舍入模式。
最简单的解决方案是:
int newScale = 4-bd.precision()+bd.scale();
BigDecimal bd2 = bd1.setScale(newScale, RoundingMode.HALF_UP);
不需要字符串转换,它纯粹基于BigDecimal算法,因此尽可能高效,您可以选择RoundingMode,它很小。如果输出应该是String,只需附加.toPlainString()。
【讨论】:
您可以使用以下几行:
int digitsRemain = 4;
BigDecimal bd = new BigDecimal("12.3456");
int power = bd.precision() - digitsRemain;
BigDecimal unit = bd.ulp().scaleByPowerOfTen(power);
BigDecimal result = bd.divideToIntegralValue(unit).multiply(unit);
注意:此解决方案总是向下舍入到最后一位。
【讨论】:
可能有人会想出更好的解决方案,但首先想到的是将其放入 StringBuilder 中,检查它是否包含 '.'并返回适当长度的子字符串。例如:
int n = 5;
StringBuilder sb = new StringBuilder();
sb.append("" + number);
if (sb.indexOf(".") > 0)
{
n++;
}
BigDecimal result = new BigDecimal(sb.substring(0, n));
【讨论】:
对我来说,这似乎很简单: 给定 N = 5,D = 123.456789
可以使用 Math.floor(Math.log(D)) 计算顺序。
希望这会有所帮助。
【讨论】:
因为 BigDecimal 基本上是一个字符串,所以可能是这样的:
import java.math.BigDecimal;
public class Silly {
public static void main( String[] args ) {
BigDecimal value = new BigDecimal("1.23238756843723E+5");
String valueString = value.toPlainString();
int decimalIndex = valueString.indexOf( '.' );
System.out.println( value + " has " +
(decimalIndex < 0 ? valueString.length() : decimalIndex) +
" digits to the left of the decimal" );
}
}
产生这个:
123238.756843723 has 6 digits to the left of the decimal
【讨论】:
A.H.'s answer 在技术上是正确的,但这里有一个更通用(也更容易理解)的解决方案:
import static org.bitbucket.cowwoc.requirements.core.Requirements.assertThat;
/**
* @param value a BigDecimal
* @param desiredPrecision the desired precision of {@code value}
* @param roundingMode the rounding mode to use
* @return a BigDecimal with the desired precision
* @throws NullPointerException if any of the arguments are null
*/
public BigDecimal setPrecision(BigDecimal value, int desiredPrecision, RoundingMode roundingMode)
{
assertThat("value", value).isNotNull();
assertThat("roundingMode", roundingMode).isNotNull();
int decreaseScaleBy = value.precision() - desiredPrecision;
return value.setScale(value.scale() - decreaseScaleBy, roundingMode);
}
【讨论】: