blob: f85dd3e7887f48e4e655cf9905c058048307153b [file] [log] [blame]
Hans Boehm995e5eb2016-02-08 11:03:01 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.calculator2;
18
19import java.math.BigInteger;
20import com.hp.creals.CR;
21import com.hp.creals.UnaryCRFunction;
22
23/**
24 * Computable real numbers, represented so that we can get exact decidable comparisons
25 * for a number of interesting special cases, including rational computations.
26 *
27 * A real number is represented as the product of two numbers with different representations:
28 * A) A BoundedRational that can only represent a subset of the rationals, but supports
29 * exact computable comparisons.
30 * B) A lazily evaluated "constructive real number" that provides operations to evaluate
31 * itself to any requested number of digits.
32 * Whenever possible, we choose (B) to be one of a small set of known constants about which we
33 * know more. For example, whenever we can, we represent rationals such that (B) is 1.
34 * This scheme allows us to do some very limited symbolic computation on numbers when both
35 * have the same (B) value, as well as in some other situations. We try to maximize that
36 * possibility.
37 *
38 * Arithmetic operations and operations that produce finite approximations may throw unchecked
39 * exceptions produced by the underlying CR and BoundedRational packages, including
40 * CR.PrecisionOverflowException and CR.AbortedException.
41 */
42public class UnifiedReal {
43
44 private final BoundedRational mRatFactor;
45 private final CR mCrFactor;
46 // TODO: It would be helpful to add flags to indicate whether the result is known
47 // irrational, etc. This sometimes happens even if mCrFactor is not one of the known ones.
48 // And exact comparisons between rationals and known irrationals are decidable.
49
50 /**
51 * Perform some nontrivial consistency checks.
52 * @hide
53 */
54 public static boolean enableChecks = true;
55
56 private static void check(boolean b) {
57 if (!b) {
58 throw new AssertionError();
59 }
60 }
61
62 private UnifiedReal(BoundedRational rat, CR cr) {
63 if (rat == null) {
64 throw new ArithmeticException("Building UnifiedReal from null");
65 }
66 // We don't normally traffic in null CRs, and hence don't test explicitly.
67 mCrFactor = cr;
68 mRatFactor = rat;
69 }
70
71 public UnifiedReal(CR cr) {
72 this(BoundedRational.ONE, cr);
73 }
74
75 public UnifiedReal(BoundedRational rat) {
76 this(rat, CR_ONE);
77 }
78
79 public UnifiedReal(BigInteger n) {
80 this(new BoundedRational(n));
81 }
82
83 public UnifiedReal(long n) {
84 this(new BoundedRational(n));
85 }
86
Hans Boehmf74f2b52017-08-23 11:04:28 -070087 public static UnifiedReal valueOf(double x) {
88 if (x == 0.0 || x == 1.0) {
89 return valueOf((long) x);
90 }
91 return new UnifiedReal(BoundedRational.valueOf(x));
92 }
93
94 public static UnifiedReal valueOf(long x) {
95 if (x == 0) {
96 return UnifiedReal.ZERO;
97 } else if (x == 1) {
98 return UnifiedReal.ONE;
99 } else {
100 return new UnifiedReal(BoundedRational.valueOf(x));
101 }
102 }
103
Hans Boehm995e5eb2016-02-08 11:03:01 -0800104 // Various helpful constants
105 private final static BigInteger BIG_24 = BigInteger.valueOf(24);
106 private final static int DEFAULT_COMPARE_TOLERANCE = -1000;
107
108 // Well-known CR constants we try to use in the mCrFactor position:
109 private final static CR CR_ONE = CR.ONE;
110 private final static CR CR_PI = CR.PI;
111 private final static CR CR_E = CR.ONE.exp();
112 private final static CR CR_SQRT2 = CR.valueOf(2).sqrt();
113 private final static CR CR_SQRT3 = CR.valueOf(3).sqrt();
114 private final static CR CR_LN2 = CR.valueOf(2).ln();
115 private final static CR CR_LN3 = CR.valueOf(3).ln();
116 private final static CR CR_LN5 = CR.valueOf(5).ln();
117 private final static CR CR_LN6 = CR.valueOf(6).ln();
118 private final static CR CR_LN7 = CR.valueOf(7).ln();
119 private final static CR CR_LN10 = CR.valueOf(10).ln();
120
121 // Square roots that we try to recognize.
122 // We currently recognize only a small fixed collection, since the sqrt() function needs to
123 // identify numbers of the form <SQRT[i]>*n^2, and we don't otherwise know of a good
124 // algorithm for that.
125 private final static CR sSqrts[] = {
126 null,
127 CR.ONE,
128 CR_SQRT2,
129 CR_SQRT3,
130 null,
131 CR.valueOf(5).sqrt(),
132 CR.valueOf(6).sqrt(),
133 CR.valueOf(7).sqrt(),
134 null,
135 null,
136 CR.valueOf(10).sqrt() };
137
138 // Natural logs of small integers that we try to recognize.
139 private final static CR sLogs[] = {
140 null,
141 null,
142 CR_LN2,
143 CR_LN3,
144 null,
145 CR_LN5,
146 CR_LN6,
147 CR_LN7,
148 null,
149 null,
150 CR_LN10 };
151
152
153 // Some convenient UnifiedReal constants.
154 public static final UnifiedReal PI = new UnifiedReal(CR_PI);
155 public static final UnifiedReal E = new UnifiedReal(CR_E);
156 public static final UnifiedReal ZERO = new UnifiedReal(BoundedRational.ZERO);
157 public static final UnifiedReal ONE = new UnifiedReal(BoundedRational.ONE);
158 public static final UnifiedReal MINUS_ONE = new UnifiedReal(BoundedRational.MINUS_ONE);
159 public static final UnifiedReal TWO = new UnifiedReal(BoundedRational.TWO);
160 public static final UnifiedReal MINUS_TWO = new UnifiedReal(BoundedRational.MINUS_TWO);
161 public static final UnifiedReal HALF = new UnifiedReal(BoundedRational.HALF);
162 public static final UnifiedReal MINUS_HALF = new UnifiedReal(BoundedRational.MINUS_HALF);
163 public static final UnifiedReal TEN = new UnifiedReal(BoundedRational.TEN);
164 public static final UnifiedReal RADIANS_PER_DEGREE
165 = new UnifiedReal(new BoundedRational(1, 180), CR_PI);
166 private static final UnifiedReal SIX = new UnifiedReal(6);
167 private static final UnifiedReal HALF_SQRT2 = new UnifiedReal(BoundedRational.HALF, CR_SQRT2);
168 private static final UnifiedReal SQRT3 = new UnifiedReal(CR_SQRT3);
169 private static final UnifiedReal HALF_SQRT3 = new UnifiedReal(BoundedRational.HALF, CR_SQRT3);
170 private static final UnifiedReal THIRD_SQRT3 = new UnifiedReal(BoundedRational.THIRD, CR_SQRT3);
171 private static final UnifiedReal PI_OVER_2 = new UnifiedReal(BoundedRational.HALF, CR_PI);
172 private static final UnifiedReal PI_OVER_3 = new UnifiedReal(BoundedRational.THIRD, CR_PI);
173 private static final UnifiedReal PI_OVER_4 = new UnifiedReal(BoundedRational.QUARTER, CR_PI);
174 private static final UnifiedReal PI_OVER_6 = new UnifiedReal(BoundedRational.SIXTH, CR_PI);
175
176
177 /**
178 * Given a constructive real cr, try to determine whether cr is the square root of
179 * a small integer. If so, return its square as a BoundedRational. Otherwise return null.
180 * We make this determination by simple table lookup, so spurious null returns are
181 * entirely possible, or even likely.
182 */
183 private static BoundedRational getSquare(CR cr) {
184 for (int i = 0; i < sSqrts.length; ++i) {
185 if (sSqrts[i] == cr) {
186 return new BoundedRational(i);
187 }
188 }
189 return null;
190 }
191
192 /**
Hans Boehmf74f2b52017-08-23 11:04:28 -0700193 * Given a constructive real cr, try to determine whether cr is the logarithm of a small
194 * integer. If so, return exp(cr) as a BoundedRational. Otherwise return null.
Hans Boehm995e5eb2016-02-08 11:03:01 -0800195 * We make this determination by simple table lookup, so spurious null returns are
196 * entirely possible, or even likely.
197 */
198 private BoundedRational getExp(CR cr) {
199 for (int i = 0; i < sLogs.length; ++i) {
200 if (sLogs[i] == cr) {
201 return new BoundedRational(i);
202 }
203 }
204 return null;
205 }
206
207 /**
208 * If the argument is a well-known constructive real, return its name.
209 * The name of "CR_ONE" is the empty string.
210 * No named constructive reals are rational multiples of each other.
211 * Thus two UnifiedReals with different named mCrFactors can be equal only if both
212 * mRatFactors are zero or possibly if one is CR_PI and the other is CR_E.
213 * (The latter is apparently an open problem.)
214 */
215 private static String crName(CR cr) {
216 if (cr == CR_ONE) {
217 return "";
218 }
219 if (cr == CR_PI) {
220 return "\u03C0"; // GREEK SMALL LETTER PI
221 }
222 if (cr == CR_E) {
223 return "e";
224 }
225 for (int i = 0; i < sSqrts.length; ++i) {
226 if (cr == sSqrts[i]) {
227 return "\u221A" /* SQUARE ROOT */ + i;
228 }
229 }
230 for (int i = 0; i < sLogs.length; ++i) {
231 if (cr == sLogs[i]) {
232 return "ln(" + i + ")";
233 }
234 }
235 return null;
236 }
237
238 /**
239 * Would crName() return non-Null?
240 */
241 private static boolean isNamed(CR cr) {
242 if (cr == CR_ONE || cr == CR_PI || cr == CR_E) {
243 return true;
244 }
245 for (CR r: sSqrts) {
246 if (cr == r) {
247 return true;
248 }
249 }
250 for (CR r: sLogs) {
251 if (cr == r) {
252 return true;
253 }
254 }
255 return false;
256 }
257
258 /**
259 * Is cr known to be algebraic (as opposed to transcendental)?
260 * Currently only produces meaningful results for the above known special
261 * constructive reals.
262 */
263 private static boolean definitelyAlgebraic(CR cr) {
264 return cr == CR_ONE || getSquare(cr) != null;
265 }
266
267 /**
268 * Is this number known to be rational?
269 */
270 public boolean definitelyRational() {
271 return mCrFactor == CR_ONE || mRatFactor.signum() == 0;
272 }
273
274 /**
275 * Is this number known to be irrational?
276 * TODO: We could track the fact that something is irrational with an explicit flag, which
277 * could cover many more cases. Whether that matters in practice is TBD.
278 */
279 public boolean definitelyIrrational() {
280 return !definitelyRational() && isNamed(mCrFactor);
281 }
282
283 /**
284 * Is this number known to be algebraic?
285 */
286 public boolean definitelyAlgebraic() {
287 return definitelyAlgebraic(mCrFactor) || mRatFactor.signum() == 0;
288 }
289
290 /**
291 * Is this number known to be transcendental?
292 */
293 public boolean definitelyTranscendental() {
294 return !definitelyAlgebraic() && isNamed(mCrFactor);
295 }
296
297
298 /**
299 * Is it known that the two constructive reals differ by something other than a
300 * a rational factor, i.e. is it known that two UnifiedReals
301 * with those mCrFactors will compare unequal unless both mRatFactors are zero?
302 * If this returns true, then a comparison of two UnifiedReals using those two
303 * mCrFactors cannot diverge, though we don't know of a good runtime bound.
304 */
305 private static boolean definitelyIndependent(CR r1, CR r2) {
306 // The question here is whether r1 = x*r2, where x is rational, where r1 and r2
307 // are in our set of special known CRs, can have a solution.
308 // This cannot happen if one is CR_ONE and the other is not.
309 // (Since all others are irrational.)
310 // This cannot happen for two named square roots, which have no repeated factors.
311 // (To see this, square both sides of the equation and factor. Each prime
312 // factor in the numerator and denominator occurs twice.)
313 // This cannot happen for e or pi on one side, and a square root on the other.
314 // (One is transcendental, the other is algebraic.)
315 // This cannot happen for two of our special natural logs.
316 // (Otherwise ln(m) = (a/b)ln(n) ==> m = n^(a/b) ==> m^b = n^a, which is impossible
317 // because either m or n includes a prime factor not shared by the other.)
318 // This cannot happen for a log and a square root.
319 // (The Lindemann-Weierstrass theorem tells us, among other things, that if
320 // a is algebraic, then exp(a) is transcendental. Thus if l in our finite
321 // set of logs where algebraic, expl(l), must be transacendental.
322 // But exp(l) is an integer. Thus the logs are transcendental. But of course the
323 // square roots are algebraic. Thus they can't be rational multiples.)
324 // Unfortunately, we do not know whether e/pi is rational.
325 if (r1 == r2) {
326 return false;
327 }
328 CR other;
329 if (r1 == CR_E || r1 == CR_PI) {
330 return definitelyAlgebraic(r2);
331 }
332 if (r2 == CR_E || r2 == CR_PI) {
333 return definitelyAlgebraic(r1);
334 }
335 return isNamed(r1) && isNamed(r2);
336 }
337
338 /**
339 * Convert to String reflecting raw representation.
340 * Debug or log messages only, not pretty.
341 */
342 public String toString() {
343 return mRatFactor.toString() + "*" + mCrFactor.toString();
344 }
345
346 /**
347 * Convert to readable String.
348 * Intended for user output. Produces exact expression when possible.
349 */
350 public String toNiceString() {
351 if (mCrFactor == CR_ONE || mRatFactor.signum() == 0) {
352 return mRatFactor.toNiceString();
353 }
354 String name = crName(mCrFactor);
355 if (name != null) {
356 BigInteger bi = BoundedRational.asBigInteger(mRatFactor);
357 if (bi != null) {
358 if (bi.equals(BigInteger.ONE)) {
359 return name;
360 }
361 return mRatFactor.toNiceString() + name;
362 }
363 return "(" + mRatFactor.toNiceString() + ")" + name;
364 }
365 if (mRatFactor.equals(BoundedRational.ONE)) {
366 return mCrFactor.toString();
367 }
368 return crValue().toString();
369 }
370
371 /**
372 * Will toNiceString() produce an exact representation?
373 */
374 public boolean exactlyDisplayable() {
375 return crName(mCrFactor) != null;
376 }
377
378 // Number of extra bits used in evaluation below to prefer truncation to rounding.
379 // Must be <= 30.
380 private final static int EXTRA_PREC = 10;
381
382 /*
383 * Returns a truncated representation of the result.
384 * If exactlyTruncatable(), we round correctly towards zero. Otherwise the resulting digit
385 * string may occasionally be rounded up instead.
Hans Boehm3465c782016-12-12 17:28:10 -0800386 * Always includes a decimal point in the result.
Hans Boehm995e5eb2016-02-08 11:03:01 -0800387 * The result includes n digits to the right of the decimal point.
388 * @param n result precision, >= 0
389 */
390 public String toStringTruncated(int n) {
391 if (mCrFactor == CR_ONE || mRatFactor == BoundedRational.ZERO) {
392 return mRatFactor.toStringTruncated(n);
393 }
394 final CR scaled = CR.valueOf(BigInteger.TEN.pow(n)).multiply(crValue());
395 boolean negative = false;
396 BigInteger intScaled;
397 if (exactlyTruncatable()) {
398 intScaled = scaled.get_appr(0);
399 if (intScaled.signum() < 0) {
400 negative = true;
401 intScaled = intScaled.negate();
402 }
403 if (CR.valueOf(intScaled).compareTo(scaled.abs()) > 0) {
404 intScaled = intScaled.subtract(BigInteger.ONE);
405 }
406 check(CR.valueOf(intScaled).compareTo(scaled.abs()) < 0);
407 } else {
408 // Approximate case. Exact comparisons are impossible.
409 intScaled = scaled.get_appr(-EXTRA_PREC);
410 if (intScaled.signum() < 0) {
411 negative = true;
412 intScaled = intScaled.negate();
413 }
414 intScaled = intScaled.shiftRight(EXTRA_PREC);
415 }
416 String digits = intScaled.toString();
417 int len = digits.length();
418 if (len < n + 1) {
Hans Boehm24c91ed2016-06-30 18:53:44 -0700419 digits = StringUtils.repeat('0', n + 1 - len) + digits;
Hans Boehm995e5eb2016-02-08 11:03:01 -0800420 len = n + 1;
421 }
422 return (negative ? "-" : "") + digits.substring(0, len - n) + "."
423 + digits.substring(len - n);
424 }
425
426 /*
427 * Can we compute correctly truncated approximations of this number?
428 */
429 public boolean exactlyTruncatable() {
430 // If the value is known rational, we can do exact comparisons.
431 // If the value is known irrational, then we can safely compare to rational approximations;
432 // equality is impossible; hence the comparison must converge.
433 // The only problem cases are the ones in which we don't know.
434 return mCrFactor == CR_ONE || mRatFactor == BoundedRational.ZERO || definitelyIrrational();
435 }
436
437 /**
438 * Return a double approximation.
Hans Boehmf74f2b52017-08-23 11:04:28 -0700439 * Rational arguments are currently rounded to nearest, with ties away from zero.
440 * TODO: Improve rounding.
Hans Boehm995e5eb2016-02-08 11:03:01 -0800441 */
442 public double doubleValue() {
443 if (mCrFactor == CR_ONE) {
444 return mRatFactor.doubleValue(); // Hopefully correctly rounded
445 } else {
446 return crValue().doubleValue(); // Approximately correctly rounded
447 }
448 }
449
450 public CR crValue() {
451 return mRatFactor.crValue().multiply(mCrFactor);
452 }
453
454 /**
455 * Are this and r exactly comparable?
456 */
457 public boolean isComparable(UnifiedReal u) {
458 // We check for ONE only to speed up the common case.
459 // The use of a tolerance here means we can spuriously return false, not true.
460 return mCrFactor == u.mCrFactor
461 && (isNamed(mCrFactor) || mCrFactor.signum(DEFAULT_COMPARE_TOLERANCE) != 0)
462 || mRatFactor.signum() == 0 && u.mRatFactor.signum() == 0
463 || definitelyIndependent(mCrFactor, u.mCrFactor)
464 || crValue().compareTo(u.crValue(), DEFAULT_COMPARE_TOLERANCE) != 0;
465 }
466
467 /**
468 * Return +1 if this is greater than r, -1 if this is less than r, or 0 of the two are
469 * known to be equal.
470 * May diverge if the two are equal and !isComparable(r).
471 */
472 public int compareTo(UnifiedReal u) {
473 if (definitelyZero() && u.definitelyZero()) return 0;
474 if (mCrFactor == u.mCrFactor) {
475 int signum = mCrFactor.signum(); // Can diverge if mCRFactor == 0.
476 return signum * mRatFactor.compareTo(u.mRatFactor);
477 }
478 return crValue().compareTo(u.crValue()); // Can also diverge.
479 }
480
481 /**
482 * Return +1 if this is greater than r, -1 if this is less than r, or possibly 0 of the two are
483 * within 2^a of each other.
484 */
485 public int compareTo(UnifiedReal u, int a) {
486 if (isComparable(u)) {
487 return compareTo(u);
488 } else {
489 return crValue().compareTo(u.crValue(), a);
490 }
491 }
492
493 /**
494 * Return compareTo(ZERO, a).
495 */
496 public int signum(int a) {
497 return compareTo(ZERO, a);
498 }
499
500 /**
501 * Return compareTo(ZERO).
502 * May diverge for ZERO argument if !isComparable(ZERO).
503 */
504 public int signum() {
505 return compareTo(ZERO);
506 }
507
508 /**
509 * Equality comparison. May erroneously return true if values differ by less than 2^a,
510 * and !isComparable(u).
511 */
512 public boolean approxEquals(UnifiedReal u, int a) {
513 if (isComparable(u)) {
514 if (definitelyIndependent(mCrFactor, u.mCrFactor)
515 && (mRatFactor.signum() != 0 || u.mRatFactor.signum() != 0)) {
516 // No need to actually evaluate, though we don't know which is larger.
517 return false;
518 } else {
519 return compareTo(u) == 0;
520 }
521 }
522 return crValue().compareTo(u.crValue(), a) == 0;
523 }
524
525 /**
526 * Returns true if values are definitely known to be equal, false in all other cases.
Hans Boehmf74f2b52017-08-23 11:04:28 -0700527 * This does not satisfy the contract for Object.equals().
Hans Boehm995e5eb2016-02-08 11:03:01 -0800528 */
529 public boolean definitelyEquals(UnifiedReal u) {
530 return isComparable(u) && compareTo(u) == 0;
531 }
532
Hans Boehmf74f2b52017-08-23 11:04:28 -0700533 @Override
534 public int hashCode() {
535 // Better useless than wrong. Probably.
536 return 0;
537 }
538
539 @Override
540 public boolean equals(Object r) {
541 if (r == null || !(r instanceof UnifiedReal)) {
542 return false;
543 }
544 // This is almost certainly a programming error. Don't even try.
545 throw new AssertionError("Can't compare UnifiedReals for exact equality");
546 }
547
Hans Boehm995e5eb2016-02-08 11:03:01 -0800548 /**
549 * Returns true if values are definitely known not to be equal, false in all other cases.
Hans Boehm4452c782016-12-07 14:52:05 -0800550 * Performs no approximate evaluation.
Hans Boehm995e5eb2016-02-08 11:03:01 -0800551 */
552 public boolean definitelyNotEquals(UnifiedReal u) {
553 boolean isNamed = isNamed(mCrFactor);
554 boolean uIsNamed = isNamed(u.mCrFactor);
555 if (isNamed && uIsNamed) {
556 if (definitelyIndependent(mCrFactor, u.mCrFactor)) {
557 return mRatFactor.signum() != 0 || u.mRatFactor.signum() != 0;
558 } else if (mCrFactor == u.mCrFactor) {
559 return !mRatFactor.equals(u.mRatFactor);
560 }
561 return !mRatFactor.equals(u.mRatFactor);
562 }
563 if (mRatFactor.signum() == 0) {
564 return uIsNamed && u.mRatFactor.signum() != 0;
565 }
566 if (u.mRatFactor.signum() == 0) {
567 return isNamed && mRatFactor.signum() != 0;
568 }
569 return false;
570 }
571
572 // And some slightly faster convenience functions for special cases:
573
574 public boolean definitelyZero() {
575 return mRatFactor.signum() == 0;
576 }
577
Hans Boehm4452c782016-12-07 14:52:05 -0800578 /**
579 * Can this number be determined to be definitely nonzero without performing approximate
580 * evaluation?
581 */
Hans Boehm995e5eb2016-02-08 11:03:01 -0800582 public boolean definitelyNonZero() {
583 return isNamed(mCrFactor) && mRatFactor.signum() != 0;
584 }
585
586 public boolean definitelyOne() {
587 return mCrFactor == CR_ONE && mRatFactor.equals(BoundedRational.ONE);
588 }
589
590 /**
591 * Return equivalent BoundedRational, if known to exist, null otherwise
592 */
593 public BoundedRational boundedRationalValue() {
594 if (mCrFactor == CR_ONE || mRatFactor.signum() == 0) {
595 return mRatFactor;
596 }
597 return null;
598 }
599
600 /**
601 * Returns equivalent BigInteger result if it exists, null if not.
602 */
603 public BigInteger bigIntegerValue() {
604 final BoundedRational r = boundedRationalValue();
605 return BoundedRational.asBigInteger(r);
606 }
607
608 public UnifiedReal add(UnifiedReal u) {
609 if (mCrFactor == u.mCrFactor) {
610 BoundedRational nRatFactor = BoundedRational.add(mRatFactor, u.mRatFactor);
611 if (nRatFactor != null) {
612 return new UnifiedReal(nRatFactor, mCrFactor);
613 }
614 }
615 if (definitelyZero()) {
616 // Avoid creating new mCrFactor, even if they don't currently match.
617 return u;
618 }
619 if (u.definitelyZero()) {
620 return this;
621 }
622 return new UnifiedReal(crValue().add(u.crValue()));
623 }
624
625 public UnifiedReal negate() {
626 return new UnifiedReal(BoundedRational.negate(mRatFactor), mCrFactor);
627 }
628
629 public UnifiedReal subtract(UnifiedReal u) {
630 return add(u.negate());
631 }
632
633 public UnifiedReal multiply(UnifiedReal u) {
634 // Preserve a preexisting mCrFactor when we can.
635 if (mCrFactor == CR_ONE) {
636 BoundedRational nRatFactor = BoundedRational.multiply(mRatFactor, u.mRatFactor);
637 if (nRatFactor != null) {
638 return new UnifiedReal(nRatFactor, u.mCrFactor);
639 }
640 }
641 if (u.mCrFactor == CR_ONE) {
642 BoundedRational nRatFactor = BoundedRational.multiply(mRatFactor, u.mRatFactor);
643 if (nRatFactor != null) {
644 return new UnifiedReal(nRatFactor, mCrFactor);
645 }
646 }
647 if (definitelyZero() || u.definitelyZero()) {
648 return ZERO;
649 }
650 if (mCrFactor == u.mCrFactor) {
651 BoundedRational square = getSquare(mCrFactor);
652 if (square != null) {
653 BoundedRational nRatFactor = BoundedRational.multiply(
654 BoundedRational.multiply(square, mRatFactor), u.mRatFactor);
655 if (nRatFactor != null) {
656 return new UnifiedReal(nRatFactor);
657 }
658 }
659 }
660 // Probably a bit cheaper to multiply component-wise.
661 BoundedRational nRatFactor = BoundedRational.multiply(mRatFactor, u.mRatFactor);
662 if (nRatFactor != null) {
663 return new UnifiedReal(nRatFactor, mCrFactor.multiply(u.mCrFactor));
664 }
665 return new UnifiedReal(crValue().multiply(u.crValue()));
666 }
667
668 public static class ZeroDivisionException extends ArithmeticException {
669 public ZeroDivisionException() {
670 super("Division by zero");
671 }
672 }
673
674 /**
675 * Return the reciprocal.
676 */
677 public UnifiedReal inverse() {
678 if (definitelyZero()) {
679 throw new ZeroDivisionException();
680 }
681 BoundedRational square = getSquare(mCrFactor);
682 if (square != null) {
683 // 1/sqrt(n) = sqrt(n)/n
684 BoundedRational nRatFactor = BoundedRational.inverse(
685 BoundedRational.multiply(mRatFactor, square));
686 if (nRatFactor != null) {
687 return new UnifiedReal(nRatFactor, mCrFactor);
688 }
689 }
690 return new UnifiedReal(BoundedRational.inverse(mRatFactor), mCrFactor.inverse());
691 }
692
693 public UnifiedReal divide(UnifiedReal u) {
694 if (mCrFactor == u.mCrFactor) {
695 if (u.definitelyZero()) {
696 throw new ZeroDivisionException();
697 }
698 BoundedRational nRatFactor = BoundedRational.divide(mRatFactor, u.mRatFactor);
699 if (nRatFactor != null) {
700 return new UnifiedReal(nRatFactor, CR_ONE);
701 }
702 }
703 return multiply(u.inverse());
704 }
705
Hans Boehmf74f2b52017-08-23 11:04:28 -0700706 /**
707 * Return the square root.
708 * This may fail to return a known rational value, even when the result is rational.
709 */
Hans Boehm995e5eb2016-02-08 11:03:01 -0800710 public UnifiedReal sqrt() {
Hans Boehmf74f2b52017-08-23 11:04:28 -0700711 if (definitelyZero()) {
712 return ZERO;
713 }
Hans Boehm995e5eb2016-02-08 11:03:01 -0800714 if (mCrFactor == CR_ONE) {
715 BoundedRational ratSqrt;
716 // Check for all arguments of the form <perfect rational square> * small_int,
717 // where small_int has a known sqrt. This includes the small_int = 1 case.
718 for (int divisor = 1; divisor < sSqrts.length; ++divisor) {
719 if (sSqrts[divisor] != null) {
720 ratSqrt = BoundedRational.sqrt(
721 BoundedRational.divide(mRatFactor, new BoundedRational(divisor)));
722 if (ratSqrt != null) {
723 return new UnifiedReal(ratSqrt, sSqrts[divisor]);
724 }
725 }
726 }
727 }
728 return new UnifiedReal(crValue().sqrt());
729 }
730
731 /**
732 * Return (this mod 2pi)/(pi/6) as a BigInteger, or null if that isn't easily possible.
733 */
734 private BigInteger getPiTwelfths() {
735 if (definitelyZero()) return BigInteger.ZERO;
736 if (mCrFactor == CR_PI) {
737 BigInteger quotient = BoundedRational.asBigInteger(
738 BoundedRational.multiply(mRatFactor, BoundedRational.TWELVE));
739 if (quotient == null) {
740 return null;
741 }
742 return quotient.mod(BIG_24);
743 }
744 return null;
745 }
746
747 /**
748 * Computer the sin() for an integer multiple n of pi/12, if easily representable.
749 * @param n value between 0 and 23 inclusive.
750 */
751 private static UnifiedReal sinPiTwelfths(int n) {
752 if (n >= 12) {
753 UnifiedReal negResult = sinPiTwelfths(n - 12);
754 return negResult == null ? null : negResult.negate();
755 }
756 switch (n) {
757 case 0:
758 return ZERO;
759 case 2: // 30 degrees
760 return HALF;
761 case 3: // 45 degrees
762 return HALF_SQRT2;
763 case 4: // 60 degrees
764 return HALF_SQRT3;
765 case 6:
766 return ONE;
767 case 8:
768 return HALF_SQRT3;
769 case 9:
770 return HALF_SQRT2;
771 case 10:
772 return HALF;
773 default:
774 return null;
775 }
776 }
777
778 public UnifiedReal sin() {
779 BigInteger piTwelfths = getPiTwelfths();
780 if (piTwelfths != null) {
781 UnifiedReal result = sinPiTwelfths(piTwelfths.intValue());
782 if (result != null) {
783 return result;
784 }
785 }
786 return new UnifiedReal(crValue().sin());
787 }
788
789 private static UnifiedReal cosPiTwelfths(int n) {
790 int sinArg = n + 6;
791 if (sinArg >= 24) {
792 sinArg -= 24;
793 }
794 return sinPiTwelfths(sinArg);
795 }
796
797 public UnifiedReal cos() {
798 BigInteger piTwelfths = getPiTwelfths();
799 if (piTwelfths != null) {
800 UnifiedReal result = cosPiTwelfths(piTwelfths.intValue());
801 if (result != null) {
802 return result;
803 }
804 }
805 return new UnifiedReal(crValue().cos());
806 }
807
808 public UnifiedReal tan() {
809 BigInteger piTwelfths = getPiTwelfths();
810 if (piTwelfths != null) {
811 int i = piTwelfths.intValue();
812 if (i == 6 || i == 18) {
813 throw new ArithmeticException("Tangent undefined");
814 }
815 UnifiedReal top = sinPiTwelfths(i);
816 UnifiedReal bottom = cosPiTwelfths(i);
817 if (top != null && bottom != null) {
818 return top.divide(bottom);
819 }
820 }
821 return sin().divide(cos());
822 }
823
824 // Throw an exception if the argument is definitely out of bounds for asin or acos.
825 private void checkAsinDomain() {
826 if (isComparable(ONE) && (compareTo(ONE) > 0 || compareTo(MINUS_ONE) < 0)) {
827 throw new ArithmeticException("inverse trig argument out of range");
828 }
829 }
830
831 /**
832 * Return asin(n/2). n is between -2 and 2.
833 */
834 public static UnifiedReal asinHalves(int n){
835 if (n < 0) {
836 return (asinHalves(-n).negate());
837 }
838 switch (n) {
839 case 0:
840 return ZERO;
841 case 1:
842 return new UnifiedReal(BoundedRational.SIXTH, CR.PI);
843 case 2:
844 return new UnifiedReal(BoundedRational.HALF, CR.PI);
845 }
846 throw new AssertionError("asinHalves: Bad argument");
847 }
848
849 /**
850 * Return asin of this, assuming this is not an integral multiple of a half.
851 */
852 public UnifiedReal asinNonHalves()
853 {
854 if (compareTo(ZERO, -10) < 0) {
855 return negate().asinNonHalves().negate();
856 }
857 if (definitelyEquals(HALF_SQRT2)) {
858 return new UnifiedReal(BoundedRational.QUARTER, CR_PI);
859 }
860 if (definitelyEquals(HALF_SQRT3)) {
861 return new UnifiedReal(BoundedRational.THIRD, CR_PI);
862 }
863 return new UnifiedReal(crValue().asin());
864 }
865
866 public UnifiedReal asin() {
867 checkAsinDomain();
868 final BigInteger halves = multiply(TWO).bigIntegerValue();
869 if (halves != null) {
870 return asinHalves(halves.intValue());
871 }
872 if (mCrFactor == CR.ONE || mCrFactor != CR_SQRT2 ||mCrFactor != CR_SQRT3) {
873 return asinNonHalves();
874 }
875 return new UnifiedReal(crValue().asin());
876 }
877
878 public UnifiedReal acos() {
879 return PI_OVER_2.subtract(asin());
880 }
881
882 public UnifiedReal atan() {
883 if (compareTo(ZERO, -10) < 0) {
884 return negate().atan().negate();
885 }
886 final BigInteger asBI = bigIntegerValue();
887 if (asBI != null && asBI.compareTo(BigInteger.ONE) <= 0) {
888 final int asInt = asBI.intValue();
889 // These seem to be all rational cases:
890 switch (asInt) {
891 case 0:
892 return ZERO;
893 case 1:
894 return PI_OVER_4;
895 default:
896 throw new AssertionError("Impossible r_int");
897 }
898 }
899 if (definitelyEquals(THIRD_SQRT3)) {
900 return PI_OVER_6;
901 }
902 if (definitelyEquals(SQRT3)) {
903 return PI_OVER_3;
904 }
905 return new UnifiedReal(UnaryCRFunction.atanFunction.execute(crValue()));
906 }
907
908 private static final BigInteger BIG_TWO = BigInteger.valueOf(2);
909
Hans Boehmf74f2b52017-08-23 11:04:28 -0700910 // The (in abs value) integral exponent for which we attempt to use a recursive
911 // algorithm for evaluating pow(). The recursive algorithm works independent of the sign of the
912 // base, and can produce rational results. But it can become slow for very large exponents.
913 private static final BigInteger RECURSIVE_POW_LIMIT = BigInteger.valueOf(1000);
914 // The corresponding limit when we're using rational arithmetic. This should fail fast
915 // anyway, but we avoid ridiculously deep recursion.
916 private static final BigInteger HARD_RECURSIVE_POW_LIMIT = BigInteger.ONE.shiftLeft(1000);
917
Hans Boehm995e5eb2016-02-08 11:03:01 -0800918 /**
Hans Boehmf74f2b52017-08-23 11:04:28 -0700919 * Compute an integral power of a constructive real, using the standard recursive algorithm.
Hans Boehm4452c782016-12-07 14:52:05 -0800920 * exp is known to be positive.
921 */
922 private static CR recursivePow(CR base, BigInteger exp) {
923 if (exp.equals(BigInteger.ONE)) {
924 return base;
925 }
Hans Boehmf74f2b52017-08-23 11:04:28 -0700926 if (exp.testBit(0)) {
Hans Boehm4452c782016-12-07 14:52:05 -0800927 return base.multiply(recursivePow(base, exp.subtract(BigInteger.ONE)));
928 }
929 CR tmp = recursivePow(base, exp.shiftRight(1));
930 if (Thread.interrupted()) {
931 throw new CR.AbortedException();
932 }
933 return tmp.multiply(tmp);
934 }
935
936 /**
Hans Boehmf74f2b52017-08-23 11:04:28 -0700937 * Compute an integral power of a constructive real, using the exp function when
938 * we safely can. Use recursivePow when we can't. exp is known to be nozero.
939 */
940 private UnifiedReal expLnPow(BigInteger exp) {
941 int sign = signum(DEFAULT_COMPARE_TOLERANCE);
942 if (sign > 0) {
943 // Safe to take the log. This avoids deep recursion for huge exponents, which
944 // may actually make sense here.
945 return new UnifiedReal(crValue().ln().multiply(CR.valueOf(exp)).exp());
946 } else if (sign < 0) {
947 CR result = crValue().negate().ln().multiply(CR.valueOf(exp)).exp();
948 if (exp.testBit(0) /* odd exponent */) {
949 result = result.negate();
950 }
951 return new UnifiedReal(result);
952 } else {
953 // Base of unknown sign with integer exponent. Use a recursive computation.
954 // (Another possible option would be to use the absolute value of the base, and then
955 // adjust the sign at the end. But that would have to be done in the CR
956 // implementation.)
957 if (exp.signum() < 0) {
958 // This may be very expensive if exp.negate() is large.
959 return new UnifiedReal(recursivePow(crValue(), exp.negate()).inverse());
960 } else {
961 return new UnifiedReal(recursivePow(crValue(), exp));
962 }
963 }
964 }
965
966
967 /**
Hans Boehm995e5eb2016-02-08 11:03:01 -0800968 * Compute an integral power of this.
Hans Boehm4452c782016-12-07 14:52:05 -0800969 * This recurses roughly as deeply as the number of bits in the exponent, and can, in
970 * ridiculous cases, result in a stack overflow.
Hans Boehm995e5eb2016-02-08 11:03:01 -0800971 */
972 private UnifiedReal pow(BigInteger exp) {
Hans Boehm995e5eb2016-02-08 11:03:01 -0800973 if (exp.equals(BigInteger.ONE)) {
974 return this;
975 }
976 if (exp.signum() == 0) {
Hans Boehmf74f2b52017-08-23 11:04:28 -0700977 // Questionable if base has undefined value or is 0.
978 // Java.lang.Math.pow() returns 1 anyway, so we do the same.
Hans Boehm995e5eb2016-02-08 11:03:01 -0800979 return ONE;
980 }
Hans Boehmf74f2b52017-08-23 11:04:28 -0700981 BigInteger absExp = exp.abs();
982 if (mCrFactor == CR_ONE && absExp.compareTo(HARD_RECURSIVE_POW_LIMIT) <= 0) {
Hans Boehm995e5eb2016-02-08 11:03:01 -0800983 final BoundedRational ratPow = mRatFactor.pow(exp);
Hans Boehmf74f2b52017-08-23 11:04:28 -0700984 // We count on this to fail, e.g. for very large exponents, when it would
985 // otherwise be too expensive.
Hans Boehm995e5eb2016-02-08 11:03:01 -0800986 if (ratPow != null) {
Hans Boehmf74f2b52017-08-23 11:04:28 -0700987 return new UnifiedReal(ratPow);
Hans Boehm995e5eb2016-02-08 11:03:01 -0800988 }
989 }
Hans Boehmf74f2b52017-08-23 11:04:28 -0700990 if (absExp.compareTo(RECURSIVE_POW_LIMIT) > 0) {
991 return expLnPow(exp);
992 }
Hans Boehm995e5eb2016-02-08 11:03:01 -0800993 BoundedRational square = getSquare(mCrFactor);
994 if (square != null) {
995 final BoundedRational nRatFactor =
996 BoundedRational.multiply(mRatFactor.pow(exp), square.pow(exp.shiftRight(1)));
997 if (nRatFactor != null) {
998 if (exp.and(BigInteger.ONE).intValue() == 1) {
999 // Odd power: Multiply by remaining square root.
1000 return new UnifiedReal(nRatFactor, mCrFactor);
1001 } else {
1002 return new UnifiedReal(nRatFactor);
1003 }
1004 }
1005 }
Hans Boehmf74f2b52017-08-23 11:04:28 -07001006 return expLnPow(exp);
Hans Boehm995e5eb2016-02-08 11:03:01 -08001007 }
1008
Hans Boehmf74f2b52017-08-23 11:04:28 -07001009 /**
1010 * Return this ^ expon.
1011 * This is really only well-defined for a positive base, particularly since
1012 * 0^x is not continuous at zero. (0^0 = 1 (as is epsilon^0), but 0^epsilon is 0.
1013 * We nonetheless try to do reasonable things at zero, when we recognize that case.
1014 */
Hans Boehm995e5eb2016-02-08 11:03:01 -08001015 public UnifiedReal pow(UnifiedReal expon) {
1016 if (mCrFactor == CR_E) {
1017 if (mRatFactor.equals(BoundedRational.ONE)) {
1018 return expon.exp();
1019 } else {
1020 UnifiedReal ratPart = new UnifiedReal(mRatFactor).pow(expon);
1021 return expon.exp().multiply(ratPart);
1022 }
1023 }
1024 final BoundedRational expAsBR = expon.boundedRationalValue();
1025 if (expAsBR != null) {
1026 BigInteger expAsBI = BoundedRational.asBigInteger(expAsBR);
1027 if (expAsBI != null) {
1028 return pow(expAsBI);
1029 } else {
1030 // Check for exponent that is a multiple of a half.
1031 expAsBI = BoundedRational.asBigInteger(
1032 BoundedRational.multiply(BoundedRational.TWO, expAsBR));
1033 if (expAsBI != null) {
1034 return pow(expAsBI).sqrt();
1035 }
1036 }
1037 }
Hans Boehmf74f2b52017-08-23 11:04:28 -07001038 // If the exponent were known zero, we would have handled it above.
1039 if (definitelyZero()) {
1040 return ZERO;
1041 }
1042 int sign = signum(DEFAULT_COMPARE_TOLERANCE);
1043 if (sign < 0) {
1044 throw new ArithmeticException("Negative base for pow() with non-integer exponent");
1045 }
Hans Boehm995e5eb2016-02-08 11:03:01 -08001046 return new UnifiedReal(crValue().ln().multiply(expon.crValue()).exp());
1047 }
1048
1049 /**
1050 * Raise the argument to the 16th power.
1051 */
1052 private static long pow16(int n) {
1053 if (n > 10) {
Hans Boehmf74f2b52017-08-23 11:04:28 -07001054 throw new AssertionError("Unexpected pow16 argument");
Hans Boehm995e5eb2016-02-08 11:03:01 -08001055 }
1056 long result = n*n;
1057 result *= result;
1058 result *= result;
1059 result *= result;
1060 return result;
1061 }
1062
1063 /**
1064 * Return the integral log with respect to the given base if it exists, 0 otherwise.
1065 * n is presumed positive.
1066 */
1067 private static long getIntLog(BigInteger n, int base) {
1068 double nAsDouble = n.doubleValue();
1069 double approx = Math.log(nAsDouble)/Math.log(base);
1070 // A relatively quick test first.
1071 // Unfortunately, this doesn't help for values to big to fit in a Double.
1072 if (!Double.isInfinite(nAsDouble) && Math.abs(approx - Math.rint(approx)) > 1.0e-6) {
1073 return 0;
1074 }
1075 long result = 0;
1076 BigInteger remaining = n;
1077 BigInteger bigBase = BigInteger.valueOf(base);
1078 BigInteger base16th = null; // base^16, computed lazily
1079 while (n.mod(bigBase).signum() == 0) {
1080 if (Thread.interrupted()) {
1081 throw new CR.AbortedException();
1082 }
1083 n = n.divide(bigBase);
1084 ++result;
1085 // And try a slightly faster computation for large n:
1086 if (base16th == null) {
1087 base16th = BigInteger.valueOf(pow16(base));
1088 }
1089 while (n.mod(base16th).signum() == 0) {
1090 n = n.divide(base16th);
1091 result += 16;
1092 }
1093 }
1094 if (n.equals(BigInteger.ONE)) {
1095 return result;
1096 }
1097 return 0;
1098 }
1099
1100 public UnifiedReal ln() {
Hans Boehm551f8cb2017-04-19 10:24:37 -07001101 if (mCrFactor == CR_E) {
1102 return new UnifiedReal(mRatFactor, CR_ONE).ln().add(ONE);
1103 }
Hans Boehm995e5eb2016-02-08 11:03:01 -08001104 if (isComparable(ZERO)) {
1105 if (signum() <= 0) {
1106 throw new ArithmeticException("log(non-positive)");
1107 }
1108 int compare1 = compareTo(ONE, DEFAULT_COMPARE_TOLERANCE);
1109 if (compare1 == 0) {
1110 if (definitelyEquals(ONE)) {
1111 return ZERO;
1112 }
1113 } else if (compare1 < 0) {
1114 return inverse().ln().negate();
1115 }
1116 final BigInteger bi = BoundedRational.asBigInteger(mRatFactor);
1117 if (bi != null) {
1118 if (mCrFactor == CR_ONE) {
1119 // Check for a power of a small integer. We can use sLogs[] to return
1120 // a more useful answer for those.
1121 for (int i = 0; i < sLogs.length; ++i) {
1122 if (sLogs[i] != null) {
1123 long intLog = getIntLog(bi, i);
1124 if (intLog != 0) {
1125 return new UnifiedReal(new BoundedRational(intLog), sLogs[i]);
1126 }
1127 }
1128 }
1129 } else {
1130 // Check for n^k * sqrt(n), for which we can also return a more useful answer.
1131 BoundedRational square = getSquare(mCrFactor);
1132 if (square != null) {
1133 int intSquare = square.intValue();
1134 if (sLogs[intSquare] != null) {
1135 long intLog = getIntLog(bi, intSquare);
1136 if (intLog != 0) {
1137 BoundedRational nRatFactor =
1138 BoundedRational.add(new BoundedRational(intLog),
1139 BoundedRational.HALF);
1140 if (nRatFactor != null) {
1141 return new UnifiedReal(nRatFactor, sLogs[intSquare]);
1142 }
1143 }
1144 }
1145 }
1146 }
1147 }
1148 }
1149 return new UnifiedReal(crValue().ln());
1150 }
1151
1152 public UnifiedReal exp() {
1153 if (definitelyEquals(ZERO)) {
1154 return ONE;
1155 }
Hans Boehme7111cf2016-12-12 12:52:49 -08001156 if (definitelyEquals(ONE)) {
1157 // Avoid redundant computations, and ensure we recognize all instances as equal.
1158 return E;
1159 }
Hans Boehm995e5eb2016-02-08 11:03:01 -08001160 final BoundedRational crExp = getExp(mCrFactor);
1161 if (crExp != null) {
Hans Boehm995e5eb2016-02-08 11:03:01 -08001162 boolean needSqrt = false;
1163 BoundedRational ratExponent = mRatFactor;
1164 BigInteger asBI = BoundedRational.asBigInteger(ratExponent);
1165 if (asBI == null) {
1166 // check for multiple of one half.
1167 needSqrt = true;
1168 ratExponent = BoundedRational.multiply(ratExponent, BoundedRational.TWO);
1169 }
1170 BoundedRational nRatFactor = BoundedRational.pow(crExp, ratExponent);
1171 if (nRatFactor != null) {
1172 UnifiedReal result = new UnifiedReal(nRatFactor);
1173 if (needSqrt) {
1174 result = result.sqrt();
1175 }
1176 return result;
1177 }
1178 }
1179 return new UnifiedReal(crValue().exp());
1180 }
1181
1182
1183 /**
1184 * Generalized factorial.
1185 * Compute n * (n - step) * (n - 2 * step) * etc. This can be used to compute factorial a bit
1186 * faster, especially if BigInteger uses sub-quadratic multiplication.
1187 */
1188 private static BigInteger genFactorial(long n, long step) {
1189 if (n > 4 * step) {
1190 BigInteger prod1 = genFactorial(n, 2 * step);
1191 if (Thread.interrupted()) {
1192 throw new CR.AbortedException();
1193 }
1194 BigInteger prod2 = genFactorial(n - step, 2 * step);
1195 if (Thread.interrupted()) {
1196 throw new CR.AbortedException();
1197 }
1198 return prod1.multiply(prod2);
1199 } else {
1200 if (n == 0) {
1201 return BigInteger.ONE;
1202 }
1203 BigInteger res = BigInteger.valueOf(n);
1204 for (long i = n - step; i > 1; i -= step) {
1205 res = res.multiply(BigInteger.valueOf(i));
1206 }
1207 return res;
1208 }
1209 }
1210
1211
1212 /**
1213 * Factorial function.
1214 * Fails if argument is clearly not an integer.
1215 * May round to nearest integer if value is close.
1216 */
1217 public UnifiedReal fact() {
1218 BigInteger asBI = bigIntegerValue();
1219 if (asBI == null) {
1220 asBI = crValue().get_appr(0); // Correct if it was an integer.
1221 if (!approxEquals(new UnifiedReal(asBI), DEFAULT_COMPARE_TOLERANCE)) {
1222 throw new ArithmeticException("Non-integral factorial argument");
1223 }
1224 }
1225 if (asBI.signum() < 0) {
1226 throw new ArithmeticException("Negative factorial argument");
1227 }
1228 if (asBI.bitLength() > 20) {
1229 // Will fail. LongValue() may not work. Punt now.
1230 throw new ArithmeticException("Factorial argument too big");
1231 }
1232 BigInteger biResult = genFactorial(asBI.longValue(), 1);
1233 BoundedRational nRatFactor = new BoundedRational(biResult);
1234 return new UnifiedReal(nRatFactor);
1235 }
1236
1237 /**
1238 * Return the number of decimal digits to the right of the decimal point required to represent
1239 * the argument exactly.
1240 * Return Integer.MAX_VALUE if that's not possible. Never returns a value less than zero, even
1241 * if r is a power of ten.
1242 */
1243 public int digitsRequired() {
1244 if (mCrFactor == CR_ONE || mRatFactor.signum() == 0) {
1245 return BoundedRational.digitsRequired(mRatFactor);
1246 } else {
1247 return Integer.MAX_VALUE;
1248 }
1249 }
1250
1251 /**
1252 * Return an upper bound on the number of leading zero bits.
1253 * These are the number of 0 bits
1254 * to the right of the binary point and to the left of the most significant digit.
1255 * Return Integer.MAX_VALUE if we cannot bound it.
1256 */
1257 public int leadingBinaryZeroes() {
1258 if (isNamed(mCrFactor)) {
1259 // Only ln(2) is smaller than one, and could possibly add one zero bit.
1260 // Adding 3 gives us a somewhat sloppy upper bound.
1261 final int wholeBits = mRatFactor.wholeNumberBits();
1262 if (wholeBits == Integer.MIN_VALUE) {
1263 return Integer.MAX_VALUE;
1264 }
1265 if (wholeBits >= 3) {
1266 return 0;
1267 } else {
1268 return -wholeBits + 3;
1269 }
1270 }
1271 return Integer.MAX_VALUE;
1272 }
1273
1274 /**
1275 * Is the number of bits to the left of the decimal point greater than bound?
1276 * The result is inexact: We roughly approximate the whole number bits.
1277 */
1278 public boolean approxWholeNumberBitsGreaterThan(int bound) {
1279 if (isNamed(mCrFactor)) {
1280 return mRatFactor.wholeNumberBits() > bound;
1281 } else {
1282 return crValue().get_appr(bound - 2).bitLength() > 2;
1283 }
1284 }
1285}