Аккуратный способ найти количество значащих цифр в BigDecimal?

Я не хочу ограничивать количество значащих цифр в BigDecimal. Я только хочу найти количество значащих цифр, которые имеет это число.

Есть ли способ сделать это без преобразования числа в строку и подсчета числовых символов?


person 0x56794E    schedule 29.01.2014    source источник


Ответы (3)


Я полагаю, вам нужна комбинация stripTrailingZeros, precision и scale< /a>, как показано здесь:

import java.math.*;

public class Test {

    public static void main(String[] args) {
        test("5000");      // 4
        test("5000.00");   // 4
        test("5000.12");   // 6
        test("35000");     // 5
        test("35000.00");  // 5
        test("35000.12");  // 7
        test("35000.120"); // 7
        test("0.0034");    // 2
        test("1.0034");    // 5
        test("1.00340");   // 5
    }


    private static void test(String input) {
        System.out.println(input + " => " +
            significantDigits(new BigDecimal(input)));
    }

    private static int significantDigits(BigDecimal input) {
        input = input.stripTrailingZeros();
        return input.scale() < 0
            ? input.precision() - input.scale()
            : input.precision(); 
    }
}

Вызов stripTrailingZeros обязателен, так как в противном случае вполне возможно, что BigDecimal будет храниться в "ненормализованной" форме. Например, new BigDecimal(5000) имеет точность 4, а не 1.

Вызов scale() используется для обработки случаев, когда нормализованная форма имеет конечные нули перед десятичной запятой, но ничего после десятичной точки. В этом случае шкала всегда будет отрицательной и указывает количество завершающих нулей.

РЕДАКТИРОВАТЬ: Случаи с конечными нулями, но без десятичной точки, по своей сути неоднозначны - например, нет определенного количества значащих цифр до «5000». В приведенном выше коде все конечные нули перед десятичной точкой рассматриваются как значимые.

person Jon Skeet    schedule 29.01.2014
comment
Вы правы в том, что precision() сам по себе не является ответом, но new BigDecimal(8.6).precision() каким-то образом возвращает 50, а new BigDecimal(8.6).stripTrailingZeroes().precision() также возвращает 50. - person rgettman; 30.01.2014
comment
@rgettman: Это потому, что new BigDecimal(8.6) на самом деле не 8,6 ... это точное значение ближайшего double к 8.6. Если вы ожидали получить 2, вы должны использовать new BigDecimal("8.6"). Просто распечатайте new BigDecimal(8.6), и вы поймете, что я имею в виду. - person Jon Skeet; 30.01.2014
comment
Другая проблема заключается в том, что new BigDecimal("5000.0").stripTrailingZeros().precision() возвращает 1, но в соответствии с принятыми определениями вы хотите, чтобы он возвращал 5 (поскольку вы не добавили бы .0 в конец числа, если бы оно не было значительным). Кроме того, количество значащих цифр в "5000" в любом случае неоднозначно. См. Значимые цифры в Википедии. - person ajb; 30.01.2014
comment
@ajb: А, хороший улов. Я ожидал значение 1, думая об этом больше с научной точки зрения (где 5000 = 5x10^3). - person Jon Skeet; 30.01.2014
comment
@Jon На самом деле, я пытался ограничить количество значащих цифр, чтобы оно соответствовало Decimal (3,2) в базе данных mysql. Но похоже, что хотя я ввожу 300, данные выходят за пределы допустимого диапазона. Любое предложение? - person 0x56794E; 30.01.2014
comment
@abcXYZ: Боюсь, неясно, что вы имеете в виду, но, похоже, это выходит за рамки этого вопроса. Если вас интересует MySQL, я бы задал вопрос о MySQL. - person Jon Skeet; 30.01.2014
comment
@ajb: Смотрите мое редактирование - как вы думаете, это исправляет? (Я думаю, что да, но мне хотелось бы услышать второе мнение.) - person Jon Skeet; 30.01.2014
comment
@abcXYZ: я не понимаю, что вы имеете в виду. Вы на самом деле не сказали нам, чего вы пытаетесь достичь, и, как я уже сказал, этот вопрос касался только BigDecimal. Задать новый вопрос о MySQL... - person Jon Skeet; 30.01.2014
comment
@JonSkeet Я думаю, что правильными ответами для ваших тестовых случаев должны быть 1 (?), 6, 6, 2 (?), 7, 7, 8, 2, 5, 6. (Случаи ? неоднозначны, а BigDecimal нет t способен различать, скажем, 5000 с одной значащей цифрой и 5000 с двумя, например.) - person ajb; 30.01.2014
comment
@ajb: Да, это устраняет неоднозначность, поскольку все цифры до десятичной точки являются значимыми. Я подозреваю, что это настолько хорошо, насколько это возможно :( - person Jon Skeet; 30.01.2014

Следующая модификация ответа Джона возвращает результаты, которые мне кажутся правильными:

private static int significantDigits(BigDecimal input) {
    return input.scale() <= 0
        ? input.precision() + input.stripTrailingZeros().scale()
        : input.precision();
}

(Обратите внимание, что input.stripTrailingZeros().scale() в этих тестах всегда оказывается отрицательным.)

Кроме того, как я отметил выше, BigDecimal не может отличить, скажем, "5000" с одной значащей цифрой от "5000" с двумя, например. Кроме того, согласно определениям, «5000». (с завершающей запятой) должно иметь ровно четыре значащих цифры, но BigDecimal не может с этим справиться. (см. http://en.wikipedia.org/wiki/Significant_figures определения с использованием.)

person ajb    schedule 29.01.2014

Ответ Джона правильный в большинстве случаев, кроме экспоненциального числа:

private static int significantDigits(BigDecimal input) {
return input.scale() <= 0
    ? input.precision() + input.stripTrailingZeros().scale()
    : input.precision();
}

скажем, ввод как 1.230000E17, функция возвращает 18, однако правильные значащие цифры должны быть 7.

person Xin Feng    schedule 23.11.2018