BigDecimal to SQL NUMBER: проверка значения больше точности - PullRequest
9 голосов
/ 22 сентября 2010

В моем приложении я обрабатываю числа как BigDecimal и сохраняю их как NUMBER (15,5). Теперь мне нужно правильно проверить на Java, соответствуют ли значения BigDecimal столбцу, чтобы я мог генерировать правильные сообщения об ошибках без выполнения SQL, перехвата исключений и проверки кода ошибки поставщика. Моя база данных - Oracle 10.3, и такие ошибки вызывают ошибку 1438 .

После некоторого поиска, я не нашел такого кода для этого, поэтому я придумал свой собственный. Но я действительно недоволен этим кодом ... простым, но в то же время достаточно простым, чтобы сомневаться в его правильности. Я проверил это со многими значениями, случайными и границами, и это, кажется, работает. Но так как я действительно плохо разбираюсь в числах, мне нужен более надежный и хорошо проверенный код.

//no constants for easier reading
public boolean testBigDecimal(BigDecimal value) {
    if (value.scale() > 5)
        return false;
    else if (value.precision() - value.scale() > 15 - 5)
        return false;
    else
        return true;
}

Редактировать: Недавние тесты не получили исключения для чисел вне масштаба, просто были бесшумно округлены, и я не уверен, что отличается между не и когда я сделал эти первые тесты. Такое округление неприемлемо, потому что приложение является финансовым, и любое округление / усечение должно быть явным (с помощью методов BigDecimal). Исключение исключено, этот метод тестирования должен гарантировать, что число не слишком велико для достижения требуемой точности, даже если оно не имеет значения. Извините за позднее уточнение.

Спасибо за ваше время.


Мне все еще интересно этот вопрос. Мой код все еще выполняется, и у меня нет каких-либо «доказательств» правильности или неудачной ситуации, или какого-либо стандартного кода для такого рода испытаний.

Итак, я вознаграждаю их, надеюсь, получу что-нибудь из этого.

Ответы [ 4 ]

5 голосов
/ 23 сентября 2010

Следующее регулярное выражение тоже поможет:

public class Big {
    private static final Pattern p = Pattern.compile("[0-9]{0,10}(\\.[0-9]{0,5}){0,1}");

    public static void main(String[] args) {
        BigDecimal b = new BigDecimal("123123.12321");
        Matcher m = p.matcher(b.toString());
        System.out.println(b.toString() + " is valid = " + m.matches());
    }
}

Это может быть еще один способ проверить ваш код или быть кодом.Для регулярного выражения требуется от 0 до 10 цифр, за которыми может следовать десятичная точка и от 0 до 5 цифр.Я не знал, нужен был знак или нет, как я думаю об этом.Подойдет что-то вроде [+-]{0,1} на передний план.

Вот, возможно, лучший класс и класс тестов с частичным набором тестов.

3 голосов
/ 15 февраля 2011

Одна из проблем с вашей функцией заключается в том, что в некоторых случаях она может быть слишком строгой, рассмотрим:

BigDecimal a = new BigDecimal("0.000005"); /* scale 6 */
a = a.multiply(new BigDecimal("2")); /* 0.000010 */
return testBigDecimal(a); /* returns false */

Как видите, шкала не отрегулирована вниз. Сейчас я не могу проверить, происходит ли что-то подобное с высокой точностью (1e11 / 2).

Я бы предложил более прямой маршрут:

public boolean testBigDecimal(BigDecimal value) {
    BigDecimal sqlScale = new BigDecimal(100000);
    BigDecimal sqlPrecision = new BigDecimal("10000000000");
    /* check that value * 1e5 is an integer */
    if (value.multiply(sqlScale)
          .compareTo(value.multiply(sqlScale)
              .setScale(0,BigDecimal.ROUND_UP)) != 0)
        return false;
    /* check that |value| < 1e10 */
    else if (value.abs().compareTo(sqlPrecision) >= 0)
        return false;
    else
        return true;
}

Обновление

Вы спросили в комментарии, выдаст ли база данных ошибку, если мы попытаемся вставить 0,000010. Фактически, база данных никогда не выдаст ошибку, если вы попытаетесь вставить значение с слишком большой точностью, она будет молча округлять вставленное значение.

Поэтому первая проверка не нужна, чтобы избежать ошибки Oracle, я предполагал, что вы выполняли этот тест, чтобы убедиться, что значение, которое вы хотите вставить, равно значению, которое вы фактически вставили. Поскольку 0,000010 и 0,00001 равны (с BigDecimal.compareTo), разве они не должны возвращать один и тот же результат?

1 голос
/ 24 сентября 2010

Ну, поскольку никто не придумал другого решения, я оставляю код как есть.

Я не смог сделать этот тест точности / масштаба неудачным, и он всегда соответствовал решению регулярных выражений, поэтому, возможно, оба они верны (я проверил границы и с более чем 5M случайно сгенерированными значениями). Я буду использовать решение точности / масштаба, так как оно более чем на 85% быстрее, и, если оно выйдет из строя, я заменю его.

Спасибо за ваши ответы, Тони.


Мой предыдущий "ответ", все еще здесь для целей истории, но я ищу реальный ответ =)

1 голос
/ 23 сентября 2010

Вместо этого, зацикливаясь на тысячах случайных чисел, вы можете написать контрольные примеры, которые подчеркивают «ребра» - максимальное значение +.00001, максимальное значение, максимальное значение - .00001, 0, ноль, минимальное значение -.00001, минимальное значение, минимальное значение + .00001 и значения с 4, 5 и 6 значениями справа от десятичной точки.Вероятно, есть еще много.

Если у вас есть такие в junit, вы хороши.

...