diff options
-rw-r--r-- | src/com/hp/creals/CR.java | 36 |
1 files changed, 25 insertions, 11 deletions
diff --git a/src/com/hp/creals/CR.java b/src/com/hp/creals/CR.java index 35ab0dc..c5a1c41 100644 --- a/src/com/hp/creals/CR.java +++ b/src/com/hp/creals/CR.java @@ -114,6 +114,8 @@ // Fix a couple of unused variable bugs. Notably selector_sign was // accidentally locally redeclared. (This turns out to be safe but useless.) // hboehm@google.com 11/20/2018. +// Fix an exception-safety issue in gl_pi_CR.approximate. +// hboehm@google.com 3/3/2019. package com.hp.creals; @@ -1577,6 +1579,11 @@ class gl_pi_CR extends slow_CR { private static CR SQRT_HALF = new sqrt_CR(ONE.shiftRight(1)); protected BigInteger approximate(int p) { + // Get us back into a consistent state if the last computation + // was interrupted after pushing onto b_prec. + if (b_prec.size() > b_val.size()) { + b_prec.remove(b_prec.size() - 1); + } // Rough approximations are easy. if (p >= 0) return scale(BigInteger.valueOf(3), -p); // We need roughly log2(p) iterations. Each iteration should @@ -1595,28 +1602,35 @@ class gl_pi_CR extends slow_CR { // Current values correspond to n, next_ values to n + 1 // b_prec.size() == b_val.size() >= n + 1 final BigInteger next_a = a.add(b).shiftRight(1); + final BigInteger next_b; final BigInteger a_diff = a.subtract(next_a); - CR next_b_as_CR; final BigInteger b_prod = a.multiply(b).shiftRight(-eval_prec); - // We the compute square root approximations using a nested + // We compute square root approximations using a nested // temporary CR computation, to avoid implementing BigInteger // square roots separately. final CR b_prod_as_CR = CR.valueOf(b_prod).shiftRight(-eval_prec); if (b_prec.size() == n + 1) { - // Need an n+1st slot. - b_prec.add(null); - b_val.add(null); - next_b_as_CR = b_prod_as_CR.sqrt(); + // Add an n+1st slot. + // Take care to make this exception-safe; b_prec and b_val + // must remain consistent, even if we are interrupted, or run + // out of memory. It's OK to just push on b_prec in that case. + final CR next_b_as_CR = b_prod_as_CR.sqrt(); + next_b = next_b_as_CR.get_appr(eval_prec); + final BigInteger scaled_next_b = scale(next_b, -extra_eval_prec); + b_prec.add(p); + b_val.add(scaled_next_b); } else { // Reuse previous approximation to reduce sqrt iterations, // hopefully to one. - next_b_as_CR = new sqrt_CR(b_prod_as_CR, b_prec.get(n + 1), - b_val.get(n + 1)); + final CR next_b_as_CR = + new sqrt_CR(b_prod_as_CR, + b_prec.get(n + 1), b_val.get(n + 1)); + next_b = next_b_as_CR.get_appr(eval_prec); + // We assume that set() doesn't throw for any reason. + b_prec.set(n + 1, p); + b_val.set(n + 1, scale(next_b, -extra_eval_prec)); } // b_prec.size() == b_val.size() >= n + 2 - final BigInteger next_b = next_b_as_CR.get_appr(eval_prec); - b_prec.set(n + 1, Integer.valueOf(p)); - b_val.set(n + 1, scale(next_b, -extra_eval_prec)); final BigInteger next_t = t.subtract(a_diff.multiply(a_diff) .shiftLeft(n + eval_prec)); // shift dist. usually neg. |