用BigDecimal限制重要数字的任何巧妙方法

时间:2011-09-27 16:11:50

标签: java bigdecimal

我想将Java BigDecimal舍入到一定数量的有效数字(非小数位),例如到4位数:

12.3456 => 12.35
123.456 => 123.5
123456 => 123500

等。基本问题是如何找到BigDecimal的数量级,因此我可以决定小数点后使用的位数。

所有我能想到的是一个可怕的循环,除以10直到结果<1,我希望有更好的方法。

顺便说一句,这个数字可能非常大(或非常小)所以我无法将其转换为双倍以使用登录。

7 个答案:

答案 0 :(得分:11)

easierst解决方案是:

  int newScale = 4-bd.precision()+bd.scale();
  BigDecimal bd2 = bd1.setScale(newScale, RoundingMode.HALF_UP);

不需要字符串转换,它完全基于BigDecimal算术,因此尽可能高效,您可以选择RoundingMode并且它很小。如果输出应为String,则只需附加.toPlainString()

答案 1 :(得分:10)

为什么不使用round(MathContext)

BigDecimal value = BigDecimal.valueOf(123456);
BigDecimal wantedValue = value.round(new MathContext(4, RoundingMode.HALF_UP));

答案 2 :(得分:5)

您可以使用以下行:

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);

注意:此解决方案始终向下舍入到最后一位数。

答案 3 :(得分:2)

有人可能会想出一个更好的解决方案,但首先想到的是将它放入StringBuilder中,检查它是否包含'。'并返回一个适当长度的子串。 E.g:

int n = 5;
StringBuilder sb = new StringBuilder();
sb.append("" + number);
if (sb.indexOf(".") > 0)
{
    n++;
}
BigDecimal result = new BigDecimal(sb.substring(0, n));

答案 4 :(得分:0)

对我而言,这似乎很简单: 给定N = 5,D = 123.456789

  1. 获取数字的字符串表示形式“123.456789”
  2. 检索该号码的前N-1个数字,“123.4”
  3. 评估D [N]和D [N + 1],在这种情况下为“5”和“6”
  4. 6符合向上舍入的标准(6> 4),因此携带1并使D [N] = 5 + 1 = 6
  5. D post rounding现在是123.46
  6. 可以使用Math.floor(Math.log(D))计算订单。

    希望这会有所帮助。

答案 5 :(得分:0)

由于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

答案 6 :(得分:0)

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);
}