Round BigDecimal В зависимости от значения десятого места

Допустим, у меня есть BigDecimal, который может быть любым количеством значений. Если значение на десятом месте меньше 3 (то есть 12,2, я хочу округлить до 12). Если значение десятого места больше или равно 3 (то есть 11,3), я хочу округлить десятое значение до 5 (так что значение здесь в конечном итоге будет 11,5). Имеет ли BigDecimal существующую функциональность, которая позволила бы легко реализовать это. Если нет, то каковы хорошие способы добиться этого в противном случае?

2 ответа

Вы можете сначала масштабировать BigDecimal до 10 десятичных цифр, используя setScale(), Затем вы можете манипулировать последней цифрой unscaledValue():

public class BigDecimalRounder
{
    public static BigDecimal roundToNearestHalf(BigDecimal value)
    {
        // Scale to 10 decimal digits and get the underlying BigInteger:
        BigInteger digits = value.setScale(10, BigDecimal.ROUND_HALF_UP).unscaledValue();

        // Act upon the last digit of the BigInteger:
        BigInteger lastDigit = digits.mod(BigInteger.TEN);
        int intDigit = lastDigit.intValue();
        BigInteger newDigit;

        if (intDigit >= 0 && intDigit <= 2)
            newDigit = BigInteger.ZERO;          // round down
        else if (intDigit >= 3 && intDigit <= 7)
            newDigit = BigInteger.valueOf(5);    // round to nearest 5
        else
            newDigit = BigInteger.TEN;           // round up

        // Assemble a new BigInteger, removing the original last digit and 
        // adding the new one (either 0, 5 or 10) 
        BigInteger newValue = digits.subtract(lastDigit).add(newDigit);

        //Assemble a new BigDecimal with the modified digits.
        return new BigDecimal(newValue, 10);
    }

    public static void main(String[] args)
    {
        BigDecimal mainValue = new BigDecimal("1.2399997990");
        BigDecimal addend = BigDecimal.ONE.movePointLeft(10);
        for (int i = 1; i <= 12; i++)
        {
            System.out.println("" + mainValue + " --> " + roundToNearestHalf(mainValue));
            mainValue = mainValue.add(addend);
        }
    }

}

Выход:

1.2399997990 --> 1.2399997990
1.2399997991 --> 1.2399997990
1.2399997992 --> 1.2399997990
1.2399997993 --> 1.2399997995
1.2399997994 --> 1.2399997995
1.2399997995 --> 1.2399997995
1.2399997996 --> 1.2399997995
1.2399997997 --> 1.2399997995
1.2399997998 --> 1.2399998000
1.2399997999 --> 1.2399998000
1.2399998000 --> 1.2399998000
1.2399998001 --> 1.2399998000

Вы можете создать свою собственную версию BigDecimal и переопределить round метод, но это поможет только при использовании этого метода. Для округления, которое он выполняет естественным образом (при выполнении обычной математики), нет простого способа изменить его поведение округления по сравнению с тем, что он уже предлагает. Если вы посмотрите на реализацию BigDecimal, многие из методов, которые выполняют округление, являются статическими, поэтому вы не сможете их переопределить.

Ниже описано, как вы можете переопределить round метод.

class MyDecimal extends BigDecimal {

    public MyDecimal(BigInteger val) {
        super(val);
    }

    @Override
    public BigDecimal round(MathContext mc) {

        /* implement your rounding algorithm here*/
    }

}
Другие вопросы по тегам