简体   繁体   中英

Arithmetic problems with java longs

I have two equations: x * x - D * y * y = 1 and x = sqrt(1 + D * y * y) . Both are algebraic manipulations of the other.

Given D, I need to solve for the smallest integer value of x so that y is also an integer. I loop through possible y values, plug them into the second equation and test if x is an integer. If it is, I return x.

The problem I have is when x, y, and D are plugged into the 1st equation, it does not equal 1.

These are some problematic values:

1. x=335159612 y=42912791 D=61
2. x=372326272 y=35662389 D=109

My intuition is that java's Math.sqrt method does not calculate such a small decimal, however BigDecimal does not have a square root method.

Is my math just wrong? If not, what can I do to accurately calculate x and y?

Edit: Here is the root of the problem along with the method that tests if a double is aa natural number.

public static void main(String[] args){
    long x = 335159612, D = 61, y = 42912791;
    System.out.println(Math.sqrt(D * y * y + 2)); // 3.35159612E8
    System.out.println(x * x - D * y * y); // 3
}

public static boolean isNatural(double d){
    return d == (int)d;
}

This kind of Diophantine's equations is known as Pell's equations.
Wiki .
Mathworld .

Both links contain clues - how to solve this equation using continued fractions.

I think it would be nice to apply some math instead of brutforce/

Be careful with precisions in 'double'.

As per IEEE 754-1985 the double precision provides 16 digits (15,9 to be absolutely precise).

Eg a) SQRT(112331965515990542) is

335159611.99999999701634694576505237017910

Which, when converted into double, gives 3.3515961199999999E8

b) SQRT(112331965515990543)

335159611.99999999850817347288252618840968

Which, when converted into double, gives 3.3515961199999999E8. So, as per IEEE 754-1985 definition, those values are equal. Apparently, any further logical/mathematical checks will be, generally speaking, inaccurate.

To overcome this limitation I recommend BigMath package from www.javasoft.ch

import ch.javasoft.math.BigMath;
import java.math.BigDecimal;

class Tester {
    public static void main(String[] args) {
        long D = 61L, y = 42912791L;
        double a = Math.sqrt(D * y * y + 1);
        double b = Math.sqrt(D * y * y + 2);
        System.out.println(a);
        System.out.println(b);
        System.out.println(a == b);

        BigDecimal bda = BigMath.sqrt(new BigDecimal(D * y * y + 1), 32);
        BigDecimal bdb = BigMath.sqrt(new BigDecimal(D * y * y + 2), 32);
        System.out.println(bda.toString());
        System.out.println(bdb.toString());
        System.out.println(bda.equals(bdb));
    }
}

Result:

3.35159612E8
3.35159612E8
true
335159611.99999999701634694576505237017910
335159611.99999999850817347288252618840968
false

Ps to completely ruin your faith in standard Java maths try this:

System.out.println(0.9200000000000002);
System.out.println(0.9200000000000001);

You will see:

0.9200000000000002
0.9200000000000002

If sqrt is the issue, use the first equation instead. If x is an integer, x^2 will also be an integer; if x is not an integer, then x^2 would also not be an integer, as long as you are using BigDecimals with sufficient scale for your math and not doubles.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM