10

私のアプリでは、数値をBigDecimalとして処理し、NUMBER(15,5)として格納します。ここで、BigDecimal値が列に適合するかどうかをJavaで適切にチェックする必要があります。これにより、SQLを実行したり、例外をキャッチしたり、ベンダーのエラーコードを確認したりせずに、適切なエラーメッセージを生成できます。私のデータベースはOracle10.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

4 に答える 4

5

次の正規表現もそのトリックを実行します。

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}です。

これは、おそらくより良いクラスであり、テストの部分的なセットを含むテストクラスです。

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

public static boolean isValid(String s) {
    BigDecimal b = new BigDecimal(s);
    Matcher m = p.matcher(b.toPlainString());
    return m.matches();
    }
}

package thop;

import junit.framework.TestCase;

/**
 * Created by IntelliJ IDEA.
 * User: tonyennis
 * Date: Sep 22, 2010
 * Time: 6:01:15 PM
 * To change this template use File | Settings | File Templates.
 */
public class BigTest extends TestCase {

    public void testZero1() {
        assertTrue(Big.isValid("0"));
    }

    public void testZero2() {
        assertTrue(Big.isValid("0."));
    }

    public void testZero3() {
        assertTrue(Big.isValid("0.0"));
    }

    public void testZero4() {
        assertTrue(Big.isValid(".0"));
    }

    public void testTooMuchLeftSide() {
        assertFalse(Big.isValid("12345678901.0"));
    }

    public void testMaxLeftSide() {
        assertTrue(Big.isValid("1234567890.0"));
    }

    public void testMaxLeftSide2() {
        assertTrue(Big.isValid("000001234567890.0"));
    }

    public void testTooMuchScale() {
        assertFalse(Big.isValid("0.123456"));
    }

    public void testScientificNotation1() {
        assertTrue(Big.isValid("123.45e-1"));
    }

    public void testScientificNotation2() {
        assertTrue(Big.isValid("12e4"));
    }
}
于 2010-09-22T21:56:58.217 に答える
3

関数の問題の1つは、制限が厳しすぎる場合があることです。次のことを考慮してください。

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)等しいので、両方とも同じ結果を返すべきではありませんか?

于 2011-02-15T17:43:29.513 に答える
1

さて、誰も別の解決策を思い付かなかったので、私はコードをそのままにしておきます。

この精度/スケールテストを失敗させることはできませんでした。常に正規表現ソリューションと一致したため、両方とも正しい可能性があります(境界をテストし、ランダムに生成された5Mを超える値を使用しました)。精度/スケールソリューションを使用します。85%以上高速であり、失敗する可能性があるため、交換します。

返信ありがとうトニー。


私の以前の「答え」は、まだ歴史の目的のためにここにありますが、私は本当の答えを探しています=)

于 2010-09-24T12:23:16.113 に答える
1

代わりに、数千の乱数をループする場合は、「エッジ」(最大値+ .00001、最大値、最大値-.00001、0、null、最小値-.00001)を強調するテストケースを作成できます。最小値、最小値+ .00001、および小数点の右側に4、5、および6の値がある値。おそらくもっとたくさんあります。

あなたがjunitにそれらを持っているなら、あなたは良いです。

于 2010-09-22T21:44:39.203 に答える