views:

65

answers:

3

In my app, I handle numbers as BigDecimal and store them as NUMBER(15,5). Now I'd need to properly check on Java if the BigDecimal values would fit the column, so that I can generate proper error messages without executing the SQL, catching exceptions and verifying the vendor error code. My database is Oracle 10.3, and such errors cause error 1438.

After some googling, I found no such code for that, so I came up with my own. But I'm really unhappy with this code... really simple, but feels so weak it will fail as soon as I look at it. I tested it with some values and it could successfully detect error situations, but as I'm really bad with numbers, I'd like some more robust and well-tested code.

//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;
}

Thanks for your time.

A: 

Instead if looping over thousands of random numbers, you could write test cases that stress the 'edges' - the maximum value +.00001, the maximum value, the maximum value - .00001, 0, null, the minimum value -.00001, the minimum value, the minimum value + .00001, and values with 4, 5, and 6 values to the right of the decimal point. There are probably many more.

If you have those in junit, you're good.

Tony Ennis
I did tested them, and it was just OK. Still, I'm afraid to label this function 'correct'; not that I like to overcomplicate things, but it feels I'm not correctily covering all the inputs.
mdrg
A: 

The following regexp would do the trick too:

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());
    }
}

This could be another way to test your code or it could be the code. The regexp requires between 0 and 10 digits optionally followed by a decimal point and 0 to 5 more digits. I didn't know if a sign was needed or not, as I think about it. Tacking something like [+-]{0,1} to the front will do.

Here is a better class, maybe, and a test class with a partial set of tests.

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"));
    }
}
Tony Ennis
I didn't want to use regexp for that, as BigDecimal also accepts exponents (123.45e-1), and dealing with that looks messy... that's exactly with exponential notation that I get most weird cases. But thanks for your suggestions.
mdrg
Good catch on the exponent bug. I changed `BigDecimal().toString` to `.toPlainString()` and now the exponent issue is handled. I added the code change and new test cases above ;-) There is a simple brutal honesty of a regexp. This may not be the right job for it, however. I believe the "Code Complete" book recommends that while testing, to code complex tasks using two different techniques - if their answers diverge, you've got a bug. And you're concerned about the correctness of your code...
Tony Ennis
Heh, I got a downvote for a one-line rock-solid solution complete with test cases. Tough crowd!
Tony Ennis
A: 

Well, since nobody came up with another solution, I'm leaving the code as it is.

I couldn't make this precision/scale test fail, and it always matched the regex solution, so maybe both are correct (I tested the boundaries and with over 5M randomly generated values). I'll use the precision/scale solution, as it is over 85% faster, and may it fail I replace it.

Thanks for your replies Tony.

mdrg
Yer welcome :-)
Tony Ennis