在我的应用中,我将数字作为BigDecimal处理,并将它们存储为NUMBER(15,5)。现在我需要正确检查Java是否BigDecimal值适合列,以便我可以生成正确的错误消息而不执行SQL,捕获异常并验证供应商错误代码。我的数据库是Oracle 10.3,这样的错误会导致error 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方法)。除了例外之外,这种测试方法必须确保数字对于期望的精度而言不是太大,即使是非有效数字。对于迟到的澄清感到抱歉。
感谢您的时间。
我仍然对这个问题感到好奇。我的代码仍在运行,我没有得到正确或失败情况的“证据”,或者这种测试的一些标准代码。
所以,我正在给它一个赏金,希望得到其中任何一个。
答案 0 :(得分: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"));
}
}
答案 1 :(得分:3)
您的功能的一个问题是,在某些情况下,它可能过于严格,请考虑:
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
),它们不应该返回相同的结果吗?
答案 2 :(得分:1)
相反,如果循环遍历数千个随机数,您可以编写强调“边缘”的测试用例 - 最大值+.00001,最大值,最大值 - .00001,0,null,最小值 - .00001,最小值,最小值+ .00001,以及小数点右侧有4,5和6值的值。可能还有更多。
如果你有那些在junit,你很好。
答案 3 :(得分:1)
好吧,既然没有人想出另一种解决方案,我就会把代码留下来。
我无法使这个精度/比例测试失败,并且它始终与正则表达式解决方案匹配,因此可能两者都是正确的(我测试了边界并且随机生成了超过5M的值)。我将使用精度/比例解决方案,因为它的速度提高了85%以上,并且可能会失败我会更换它。
感谢您回复Tony。
我以前的“回答”,仍然出于历史目的,但我正在寻找一个真正的答案=)