BigDecimal to SQL NUMBER: check for value larger than precision - java

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 unsatisfied with this code... simple, but at the same time simple enough to doubt its correctness. I tested it with many values, random ones and boundaries, and it seems to work. 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;
}
Edit: Recent tests did not got an exception for numbers out of scale, just got silently rounded, and I'm not sure what is different between not and when I made these first tests. Such rounding is unacceptable because the application is financial, and any rounding/truncation must be explicit (through BigDecimal methods). Exception-is-gone aside, this test method must assure that the number is not too large for the desired precision, even if by non-significant digits. Sorry about the late clarification.
Thanks for your time.
I'm still curious about this question. My code is still running, and I haven't got some "proof" of correctness or fail situation, or some standard code for this kind of test.
So, I'm putting a bounty on it, hopefully getting any of these.

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

one of the problems with your function is that in some cases it may be too restrictive, consider:
BigDecimal a = new BigDecimal("0.000005"); /* scale 6 */
a = a.multiply(new BigDecimal("2")); /* 0.000010 */
return testBigDecimal(a); /* returns false */
As you can see, the scale is not adjusted down. I can't test right now if something similar happens with high-end precision (1e11/2).
I would suggest a more direct route:
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;
}
Update
You've asked in a comment if the database would throw an error if we try to insert 0.000010. In fact the database will never throw an error if you try to insert a value with too much precision, it will silently round the inserted value.
The first check is therefore not needed to avoid an Oracle error, I was assuming that you were performing this test to make sure that the value you want to insert is equal to the value you actually inserted. Since 0.000010 and 0.00001 are equal (with BigDecimal.compareTo) shouldn't they both return the same result?

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.

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.
My previous "answer", still here for history purposes, but I'm looking for a real answer =)

Related

I'm working on Euler 12 , the code i have seems to workes properly but too slow , very very slow. How can i modify it to run faster?

Like i sad , i am working on Euler problem 12 https://projecteuler.net/problem=12 , i believe that this program will give the correct answer but is too slow , i tried to wait it out but even after 9min it still cant finish it. How can i modify it to run faster ?
package highlydivisibletriangularnumber_ep12;
public class HighlyDivisibleTriangularNumber_EP12 {
public static void findTriangular(int triangularNum){
triangularValue = triangularNum * (triangularNum + 1)/2;
}
static long triangularValue = 0l;
public static void main(String[] args) {
long n = 1l;
int counter = 0;
int i = 1;
while(true){
findTriangular(i);
while(n<=triangularValue){
if(triangularValue%n==0){
counter++;
}
n++;
}
if(counter>500){
break;
}else{
counter = 0;
}
n=1;
i++;
}
System.out.println(triangularValue);
}
}
Just two simple tricks:
When x%n == 0, then also x%m == 0 with m = x/n. This way you need to consider only n <= Math.ceil(sqrt(x)), which is a huge speed up. With each divisor smaller than the square root, you get another one for free. Beware of the case of equality. The speed gain is huge.
As your x is a product of two numbers i and i+1, you can generate all its divisors as product of the divisors of i and i+1. What makes it more complicated is the fact that in general, the same product can be created using different factors. Can it happen here? Do you need to generate products or can you just count them? Again, the speed gain is huge.
You could use prime factorization, but I'm sure, these tricks alone are sufficient.
It appears to me that your algorithm is a bit too brute-force, and due to this, will consume an enormous amount of cpu time regardless of how you might rearrange it.
What is needed is an algorithm that implements a formula that calculates at least part of the solution, instead of brute-forcing the whole thing.
If you get stuck, you can use your favorite search engine to find a number of solutions, with varying degrees of efficiency.

Avoiding StackOverflowError with adding and subtracting 32 digit numbers

I seem to be unable to stumbling into some recursion problems with my latest code. I am adding and subtracting custom numbers of 32 digits. The numbers are stored in an array.
Here's the bit of code to illustrate my problems:
public Custom subtract(Custom e) {
if (isPositive(this) && (!isPositive(e))) {
return add(temp);
}
In the add method:
if (isPositive(this)) {
if (!isPositive(e)) {
if (this.e.array.length >= e2.array.length) {
return subtract(e);
The problem is, the length will always be constant and the sign will always be a constant too for the passed in parameters. I think my best bet would be to make copies of each Custom Object and changing the signs to satisfy an add or subtract but in doing so, I overwrite existing, correct values. Does anyone have any advice?
Try:
public Custom subtract(Custom e) {
if (isPositive(this) && (!isPositive(e))) {
Custom temp = new Custom(negate(e));
return add(temp);
}
Where negate() returns its parameter with the sign flipped.

For some reason, the program class is not executing the code

I tried to write code to get the largest prime factor of a big number (in this case, 600851475143).
After writing four different methods (2 to show results, and 2 with other calculations), I have written the program class, and tried to run it.
When I run it, the result should appear in the console, but nothing shows up. I tried to make the variable that should be printed public, and just print it manually, but it didn't work. Eventually, I wrote the simplest System.out.print() command in the main method, but nothing appeared in the console.
I have no idea what the problem is. Does anyone here have a clue?
The class:
public class Problem3 {
public float sum1;
public float sum2;
private float num = 600851475143f;
public void methodGuy(){
while(sum1==0){
for(int i=2; i<num/2; i++){
if(num%i==0){
sum1=num/i;
} else {}
}
}
}
public void show1(){
System.out.println("the result of Guys method is: " + sum1);
}
public void methodOr(){
for(int i=2; i<num/2; i++){
for(float x=num/2; x>2; x=x-1){
if(i*x==num){
sum2=x;
}
}
}
}
public void show2(){
System.out.println("the result of Ors method is: " + sum2);
}
}
The program class:
public class Program {
public static void main(String[] args) {
Problem3 x = new Problem3();
x.methodGuy();
x.show1();
x.methodOr();
x.show2();
}
}
methodGuy will never terminate because int's can't get that big.
The largest possible value of an int is 2147483647. If your int is that number, and you add one to it, the number will wrap around and become negative. So after i++, the next value will be -2147483648. Since the loop will continue as long as i is less than 600851475143/2 = 300425237071.5, and since i will always be less than that, your loop is infinite.
Best would be to make both i and num have type long, instead of int or float. Even if you do that, your loop will probably run for a very long time.
In fact, if there are no factors, the loop will be infinite, because sum will never be set to something other than 0, and then since you say while (sum==0), the loop will just start over again and do the same thing infinitely. So aside from the wraparound problem, your algorithm still needs work.
Further note: You definitely do not want to use float for this, because the number 600851475143 cannot be represented exactly. The actual value of num will be 600851480576.

Recursive Exponent Method stack overflow

I already searched everywhere for a solution for my problem, but didn't get one. So what I'm trying to do ist use recursion to find out whats a passed integer variable's base to the power of the passed exponent. So for example 3² is 9. My solution really looks like what I found in these forums, but it constantly gives me a stack overflow error. Here is what I have so far.(To make it easier, I tried it with the ints directly not using scanner to test my recursion) Any idea?
public class Power {
public static int exp(int x,int n) {
n = 3;
x = 2;
if (x == 0) {
return 1;
}
else {
return n * exp(n,x-1);
}
}
public static void main(String[] args) {
System.out.println(exp(2,3));
}
}
Well, you've got three problems.
First, inside of the method, you're reassigning x and n. So, regardless of what you pass in, x is always 2, and n is always 3. This is the main cause of your infinite recursion - as far as the method is concerned, those values never update. Remove those assignments from your code.
Next, your base case is incorrect - you want to stop when n == 0. Change your if statement to reflect that.
Third, your recursive step is wrong. You want to call your next method with a reduction to n, not to x. It should read return x * exp(x, n-1); instead.

Test Case failing when expected is equal to output

Before I get into detail, YES this is a HOMEWORK ASSIGNMENT. NO I DON'T WANT ANSWERS, JUST TIPS and/or Suggestions to try this or that.
The problem introduces with this:
Create a class, ExactNumber, that uses two long properties named left
and right (representing the portion of the number that is to the left
and right of the decimal point respectively). For example, 3.75 would
be represented by new ExactNumber(3, 7500000000000000L). Note the L on
the end which tells Java the large number is a long. This translates
to: 3 + 7500000000000000/10000000000000000 = 3.75
Here is my code:
public class ExactNumber {
private long left;
private long right;
public ExactNumber(long left, long right) {
this.left = left;
this.right = right;
}
public String toString() {
return String.valueOf(doubleValue());
}
public double doubleValue() {
return ((double) left + (double) (right/ 100000000000000L) / 100);
}
public int compareTo (ExactNumber exactNumber) {
if(exactNumber.left < left) {
return 1;
}
else if (exactNumber.left == left) {
if (exactNumber.right < right) {
return 1;
}
else if (exactNumber.right == right) {
return 0;
}
else {
return -1;
}
}
else {
return -1;
}
}
public boolean equal(ExactNumber thisobject) {
if (thisobject instanceof ExactNumber) {
if (thisobject.doubleValue() == this.doubleValue()) {
return true;
}
else {
return false;
}
}
else {
return false;
}
}
public double add(ExactNumber exactNumber) {;
return ((left+exactNumber.left) + (double)((right+exactNumber.right)*1E-16));
}
}
My problem are the tests coming up as an error when the expected value is equal to the actual value. Here are the test cases (NOTE: there are more test cases, but they pass the JUnit test):
public class TestExactNumber extends TestCase {
ExactNumber threesevenfive = new ExactNumber(3, 7500000000000000L);
ExactNumber threesevenfive_andalittlebit = new ExactNumber(3, 7500000000000001L);
ExactNumber threesevenfive_dupe = new ExactNumber(3, 7500000000000000L);
ExactNumber ten = new ExactNumber(10, 0);
ExactNumber thirteensevenfive = new ExactNumber(13, 7500000000000000L);
ExactNumber sevenfifty = new ExactNumber(7, 5000000000000000L);
public void test_equals() {
assertFalse(threesevenfive.equals(threesevenfive_andalittlebit));
assertEquals(threesevenfive, threesevenfive_dupe);
}
public void test_add() {
assertEquals(threesevenfive.add(ten), thirteensevenfive);
assertEquals(threesevenfive.add(threesevenfive), sevenfifty);
The assertEquals above failed in the JUnit test, but says like (for an example) expected = 13.75 and actual = 13.75.
Any tips or hints at what I need to do with my code is greatly appreciated. And thank you in advanced.
NOTES:
According to my instructor, I should not be using the doubleValue method to implement my equals method. I know that I do have it in my code, but that was prior to the tip the instructor gave me and I am just unsure about how to change it.
I am using eclipse for java to code this.
Your equal Method is never used. The Java Method used by assertEquals() is called equalS (and you have to override the equals() method derived from Object).
Therefore, the assertion will use equals inherited from Object, which will compare the actual instances rather than using YOUR equal method which will compare the objet values. And since they are two different INSTANCES, they are not equal.
Finally, the two instances will be plotted with toString() resulting in expected = 13.75 and actual = 13.75. (Because your toString() returns only the values, ignoring the difference between instances)
Your Instructors Response:
A Long in Java is a 64 bit long number. Double in Java is implemented with the IEEE754 Standard, which only leaves 52 bit for the mantissa. Meaning: Any conversion of a Long Number to a double, where the Long Number has set bits on bit 53 to 63 - will cause the exponent to be shifted in a way, that you loose precision arround the LSBs - resulting in an unprecice Double Value.
Therefore comparing the double values to determine equality is not sufficent for your desired Design of a "Exact Number".
Example:
Long bigLong = 1L<<51; //picked 51: 52 and 53 already causing rounding issues.
Long long1 = bigLong + 1L;
Long long2 = bigLong + 2L;
System.out.println(long1+" -> " + long1.doubleValue());
System.out.println(long2+" -> " + long2.doubleValue());
//false, enough precision to preserve bit "0" and "1".
System.out.println(long1.doubleValue()==long2.doubleValue());
Output:
2251799813685262 -> 2.251799813685262E15
2251799813685263 -> 2.251799813685263E15
false
When setting bit 54:
Long bigLong = 1L<<54;
Long long1 = bigLong + 1L;
Long long2 = bigLong + 2L;
System.out.println(long1+" -> " + long1.doubleValue());
System.out.println(long2+" -> " + long2.doubleValue());
System.out.println(long1.doubleValue()==long2.doubleValue());
Output:
18014398509481985 -> 1.8014398509481984E16
18014398509481986 -> 1.8014398509481984E16
true
Note the Exponent beeing increased from 15 to 16, which will cut off the difference of "1" between both longs.
To solve this, you can compare left1 to left2 and right1 to right2 without converting it to a double.
Your equal method should ideally test every necessary value in your class. In this case, it should be checking to see if your left and right values are the same between the two objects. If they are the same, then you can consider the objects to be equal.
In your case, you should probably put a debug point in your equals method to see why the function is returning back a false.
Try using Eclipse's built in functionality to create equals and hashcode methods for you. You can create that by going to Source->Generate hashCode() and equals(). The methods will be very different from what you have created.
Another thing, in your AssertEquals method, make sure both the values passed in are of the same type. In your case, you're checking a Double with an ExactNumber object. They will definitely not be the same. You need to either
Change your Add method to return a ExactNumber object
Have a method in your ExactNumber class called getDouble() and use that as the second parameter instead.
Hope this helps.

Categories