Я думаю, нет, если вы не можете полностью определить и контролировать все математические вычисления до такой степени, чтобы исключить все округления.
Альтернативой может быть, возможно, использование Rational. Вот тот, который я подхватил просто как эксперимент. Я сомневаюсь, является ли это оптимальным или даже эффективным, но это, безусловно, возможно.
class Rational {
private int n; // Numerator.
private int d; // Denominator.
Rational(int n, int d) {
int gcd = gcd(n, d);
this.n = n / gcd;
this.d = d / gcd;
}
Rational add(Rational r) {
int lcm = lcm(d, r.d);
return new Rational((n * lcm) / d + (r.n * lcm) / r.d, lcm);
}
Rational sub(Rational r) {
int lcm = lcm(d, r.d);
return new Rational((n * lcm) / d - (r.n * lcm) / r.d, lcm);
}
Rational mul(Rational r) {
return new Rational(n * r.n, d * r.d);
}
Rational div(Rational r) {
return new Rational(n * r.d, d * r.n);
}
@Override
public String toString() {
return n + "/" + d;
}
/**
* Returns the least common multiple between two integer values.
*
* @param a the first integer value.
* @param b the second integer value.
* @return the least common multiple between a and b.
* @throws ArithmeticException if the lcm is too large to store as an int
* @since 1.1
*/
public static int lcm(int a, int b) {
return Math.abs(mulAndCheck(a / gcd(a, b), b));
}
/**
* Multiply two integers, checking for overflow.
*
* @param x a factor
* @param y a factor
* @return the product <code>x*y</code>
* @throws ArithmeticException if the result can not be represented as an
* int
* @since 1.1
*/
public static int mulAndCheck(int x, int y) {
long m = ((long) x) * ((long) y);
if (m < Integer.MIN_VALUE || m > Integer.MAX_VALUE) {
throw new ArithmeticException("overflow: mul");
}
return (int) m;
}
/**
* <p>
* Gets the greatest common divisor of the absolute value of two numbers,
* using the "binary gcd" method which avoids division and modulo
* operations. See Knuth 4.5.2 algorithm B. This algorithm is due to Josef
* Stein (1961).
* </p>
*
* @param u a non-zero number
* @param v a non-zero number
* @return the greatest common divisor, never zero
* @since 1.1
*/
public static int gcd(int u, int v) {
if (u * v == 0) {
return (Math.abs(u) + Math.abs(v));
}
// keep u and v negative, as negative integers range down to
// -2^31, while positive numbers can only be as large as 2^31-1
// (i.e. we can't necessarily negate a negative number without
// overflow)
/* assert u!=0 && v!=0; */
if (u > 0) {
u = -u;
} // make u negative
if (v > 0) {
v = -v;
} // make v negative
// B1. [Find power of 2]
int k = 0;
while ((u & 1) == 0 && (v & 1) == 0 && k < 31) { // while u and v are
// both even...
u /= 2;
v /= 2;
k++; // cast out twos.
}
if (k == 31) {
throw new ArithmeticException("overflow: gcd is 2^31");
}
// B2. Initialize: u and v have been divided by 2^k and at least
// one is odd.
int t = ((u & 1) == 1) ? v : -(u / 2)/* B3 */;
// t negative: u was odd, v may be even (t replaces v)
// t positive: u was even, v is odd (t replaces u)
do {
/* assert u<0 && v<0; */
// B4/B3: cast out twos from t.
while ((t & 1) == 0) { // while t is even..
t /= 2; // cast out twos
}
// B5 [reset max(u,v)]
if (t > 0) {
u = -t;
} else {
v = t;
}
// B6/B3. at this point both u and v should be odd.
t = (v - u) / 2;
// |u| larger: t positive (replace u)
// |v| larger: t negative (replace v)
} while (t != 0);
return -u * (1 << k); // gcd is u*2^k
}
static void test() {
Rational r13 = new Rational(1, 3);
Rational r29 = new Rational(2, 9);
Rational r39 = new Rational(3, 9);
Rational r12 = new Rational(1, 2);
Rational r59 = r13.add(r29);
Rational r19 = r29.mul(r12);
Rational r23 = r39.div(r12);
Rational r16 = r12.sub(r13);
System.out.println("1/3 = " + r13);
System.out.println("2/9 = " + r29);
System.out.println("1/3 = " + r39);
System.out.println("5/9 = " + r59);
System.out.println("1/9 = " + r19);
System.out.println("2/3 = " + r23);
System.out.println("1/6 = " + r16);
}
}
Я нашел код lcm и gcd в java2 . Возможно, их можно улучшить.