All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.numdata.oss.BigRational Maven / Gradle / Ivy

There is a newer version: 1.22
Show newest version
/* BigRational.java -- dynamically sized big rational numbers.
**
** Copyright (C) 2002-2010 Eric Laroche.  All rights reserved.
**
** @author Eric Laroche 
** @version @(#)$Id: BigRational.java,v 1.3 2010/03/24 20:11:34 laroche Exp $
**
** This program is free software;
** you can redistribute it and/or modify it.
**
** This program is distributed in the hope that it will be useful,
** but WITHOUT ANY WARRANTY; without even the implied warranty of
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
**
*/

package com.numdata.oss;

import java.math.*;

/**
 * Implements dynamically sized arbitrary precision immutable rational numbers.
 *
 * Dynamically sized means that (provided enough memory) the BigRational numbers
 * can't overflow (nor underflow); this characteristic is different from Java's
 * data types int and long, but common with BigInteger (which however implements
 * only integer numbers, i.e. no fractions) and BigDecimal (which however only
 * implements precisely rational numbers with denominators that are products of
 * 2 and 5 [and implied 10]). Arbitrary precision means that there is no loss of
 * precision with common arithmetic operations such as addition, subtraction,
 * multiplication, and division (BigDecimal loses precision with division, if
 * factors other than 2 or 5 are involved). [Doubles and floats can overflow and
 * underflow, have a limited precision, and only implement precisely rationals
 * with a denominator that is a power of 2.]
 *
 * BigRational provides most operations needed in rational number space
 * calculations, including addition, subtraction, multiplication, division,
 * integer power, remainder/modulus, comparison, and different roundings.
 *
 * BigRational provides conversion methods to and from the native types long,
 * int, short, and byte (including support for unsigned values), double
 * (binary64), float (binary32), quad (binary128, quadruple), half (binary16),
 * and BigInteger. BigRational can parse and print many string representations:
 * rational, dot notations, with exponent, even mixtures thereof, and supports
 * different radixes/bases (2 to typically 36 [limited by BigInteger parsing
 * implementation]).
 *
 * BigRational uses java.math.BigInteger (JDK 1.1 and later).
 *
 * Binary operations (e.g. add, multiply) calculate their results from a
 * BigRational object ('this') and one [BigRational or long] argument, returning
 * a new immutable BigRational object. Both the original object and the argument
 * are left unchanged (hence immutable). Unary operations (e.g. negate, invert)
 * calculate their result from the BigRational object ('this'), returning a new
 * immutable BigRational object. The original object is left unchanged.
 *
 * Most operations are precise (i.e. without loss of precision); exceptions are
 * the conversion methods to limited precision types (doubleValue, floatValue),
 * rounding (round), truncation (bigIntegerValue, floor, ceiling, truncate), as
 * well as obviously the printing methods that include a precision parameter
 * (toStringDot, toStringDotRelative, toStringExponent).
 *
 * BigRational doesn't provide a notion of "infinity" ([+-]Infinity) and "not a
 * number" (NaN); IEEE 754 floating point Infinity and NaN are rejected
 * (throwing a NumberFormatException). Operations such as 0/0 result in an
 * ArithmeticException.
 *
 * Some commonly used short function names (abs, ceil, div, inv, max, min, mod,
 * mul, neg, pow, rem, sign, sub, trunc) are additionally defined as aliases to
 * to the full function names (absolute, ceiling, divide, invert, maximum,
 * minimum, modulus, multiply, negate, power, remainder, signum, subtract,
 * truncate). [This makes the interface somewhat fatter.]
 *
 * BigRational internally uses private proxy functions for BigInteger
 * functionality, including scanning and multiplying, to enhance speed and to
 * realize fast checks for common values (1, 0, etc.).
 *
 * Constructor samples: normal rational form, abbreviated form, fixed point
 * form, abbreviated fixed point form, [exponential] floating point form,
 * different radixes/bases different from 10, doubles/floats:
 * 
 * 	BigRational("-21/35"): rational -3/5
 * 	BigRational("/3"): rational 1/3
 * 	BigRational("3.4"): rational 17/5
 * 	BigRational(".7"): 0.7, rational 7/10
 * 	BigRational("-65.4E-3"): -327/5000
 * 	BigRational("f/37", 0x10): 3/11
 * 	BigRational("f.37", 0x10): 3895/256
 * 	BigRational("-dcba.efgh", 23): -46112938320/279841
 * 	BigRational("1011101011010110", 2): 47830
 * 	BigRational(StrictMath.E): 6121026514868073/2251799813685248
 * 	BigRational((float)StrictMath.E): 2850325/1048576
 * 
* * Also accepted are denormalized representations such as: *
 * 	BigRational("2.5/-3.5"): -5/7
 * 	BigRational("12.34E-1/-56.78E-1"): -617/2839
 * 
* * Printing: rational form, fixed point (dot) forms with different absolute * precisions (including negative precision), with relative precisions, * exponential form, different radix: *
 * 	BigRational("1234.5678"): "6172839/5000"
 * 	BigRational("1234.5678").toStringDot(6): "1234.567800"
 * 	BigRational("1234.5678").toStringDot(2): "1234.57"
 * 	BigRational("1234.5678").toStringDot(-2): "1200"
 * 	BigRational("1234.5678").toStringDotRelative(6): "1234.57"
 * 	BigRational("0.00012345678").toStringDotRelative(3): "0.000123"
 * 	BigRational("1234.5678").toStringExponent(2): "1.2E3"
 * 	BigRational("1011101011010110", 2).toString(0x10): "bad6"
 * 
* * Usage: BigRational operations can be conveniently chained (sample from * BigRational internal conversion from IEEE 754 bits): *
 * 	BigRational.valueOf(2).power(exponent).multiply(fraction.add(
 * 		BigRational.valueOfUnsigned(mantissa))).multiply(sign);
 * 
* * The BigRational source and documentation can typically be found at the * author's (Eric Laroche) site, at http://www.lrdev.com/lr/java/BigRational.java (source) and http://www.lrdev.com/lr/java/BigRational.html (documentation). * * @author Eric Laroche <[email protected]> * @version @(#)$Id: BigRational.java,v 1.3 2010/03/24 20:11:34 laroche Exp $ */ // @Immutable public class BigRational extends Number implements // note: clone() not so much needed, since the BigRational objects are immutable Cloneable, // note: Comparable implemented (instead of Comparable) // for backward compatibility Comparable // implements Comparable { /** * Numerator. Numerator may be negative. Numerator may be zero, in which * case m_q must be one. [Conditions are put in place by normalize().] */ private BigInteger m_n; /** * Denominator (quotient). Denominator is never negative and never zero. * [Conditions are put in place by normalize().] */ private BigInteger m_q; /** * Default radix, used in string printing and scanning, 10. * * Default radix is decimal [of course]. */ public static final int DEFAULT_RADIX = 10; // note: following constants can't be constructed using bigIntegerValueOf(). // that one _uses_ the constants (avoid circular dependencies). /** * Constant internally used, for convenience and speed. Used as zero * numerator. Used for fast checks. */ private static final BigInteger BIG_INTEGER_ZERO = BigInteger.valueOf( 0 ); /** * Constant internally used, for convenience and speed. Used as neutral * denominator. Used for fast checks. */ private static final BigInteger BIG_INTEGER_ONE = BigInteger.valueOf( 1 ); /** * Constant internally used, for convenience and speed. Used for fast * checks. */ private static final BigInteger BIG_INTEGER_MINUS_ONE = BigInteger.valueOf( -1 ); /** * Constant internally used, for convenience and speed. Used in rounding * zero numerator. _Not_ used for checks. */ private static final BigInteger BIG_INTEGER_TWO = BigInteger.valueOf( 2 ); /** * Constant internally used, for convenience and speed. _Not_ used for * checks. */ private static final BigInteger BIG_INTEGER_MINUS_TWO = BigInteger.valueOf( -2 ); /** * Constant internally used, for convenience and speed. Corresponds to * DEFAULT_RADIX, used in reading, scaling and printing. _Not_ used for * checks. */ private static final BigInteger BIG_INTEGER_TEN = BigInteger.valueOf( 10 ); /** * Constant internally used, for convenience and speed. Used in reading, * scaling and printing. _Not_ used for checks. */ private static final BigInteger BIG_INTEGER_SIXTEEN = BigInteger.valueOf( 16 ); // some more constants /** * The constant two to the power of 64 (18446744073709551616). Used is * slicing larger [than double size] IEEE 754 values. */ private static final BigInteger BIG_INTEGER_TWO_POWER_64 = BigInteger.valueOf( 2 ).pow( 64 ); /** * Construct a BigRational from numerator and denominator. * * Both n and q may be negative. n/q may be denormalized (i.e. have common * factors, or q being negative). */ public BigRational( final BigInteger n, final BigInteger q ) { // note: check for q==null done later if ( q != null && bigIntegerIsZero( q ) ) { throw new NumberFormatException( "quotient zero" ); } normalizeFrom( n, q ); } /** * Construct a BigRational from a big number integer; denominator is 1. */ public BigRational( final BigInteger n ) { this( n, BIG_INTEGER_ONE ); } /** * Construct a BigRational from long fix number integers representing * numerator and denominator. */ public BigRational( final long n, final long q ) { this( bigIntegerValueOf( n ), bigIntegerValueOf( q ) ); } /** * Construct a BigRational from a long fix number integer. */ public BigRational( final long n ) { this( bigIntegerValueOf( n ), BIG_INTEGER_ONE ); } // note: byte/short/int implicitly upgraded to long, // so we don't implement BigRational(int,int) et al. /** * Clone a BigRational. * * [As BigRationals are immutable, this copy-constructor is not that * useful.] */ public BigRational( final BigRational that ) { normalizeFrom( that ); } /** * Construct a BigRational from a string representation. * * The supported string formats are "[+-]n", "[+-]n/[+-]q", "[+-]i.f", * "[+-]i", "[+-]iE[+-]e", "[+-]i.fE[+-]e" (latter two only with * radix<=10, due to possible ambiguities); n and q can be any of the * latter (i.e. mixed representations such as "-1.2E-3/-4.5E-6" are * supported). * * Samples: "-21/35", "3.4", "-65.4E-3", "f/37" (base 16), * "1011101011010110" (base 2). * * [Exponential representation wasn't supported in an earlier version.] */ public BigRational( final String s, final int radix ) { if ( s == null ) { throw new NumberFormatException( "null" ); } // '/': least precedence, and left-to-right associative // (therefore lastIndexOf and not indexOf: last slash has least precedence) final int slash = s.lastIndexOf( '/' ); if ( slash != -1 ) { // "[+-]n/[+-]q" String sn = s.substring( 0, slash ); String sq = s.substring( slash + 1 ); // suppress recursion: make stack-overflow attacks infeasible if ( sn.indexOf( '/' ) != -1 ) { throw new NumberFormatException( "can't nest '/'" ); } // handle "/x" as "1/x" // [note: "1" and "-1" are treated specially and optimized // in bigIntegerValueOf(String,int).] if ( sn.equals( "" ) || sn.equals( "+" ) ) { sn = "1"; } else if ( sn.equals( "-" ) ) { sn = "-1"; } // handle "x/" as "x" // [note: "1" and "-1" are treated special and optimized // in bigIntegerValueOf(String,int).] if ( sq.equals( "" ) || sq.equals( "+" ) ) { sq = "1"; } else if ( sq.equals( "-" ) ) { sq = "-1"; } // [recursion] // [divide()'s outcome is already normalized, // so there would be no need to normalize [again]] normalizeFrom( ( new BigRational( sn, radix ) ).divide( new BigRational( sq, radix ) ) ); return; } checkRadix( radix ); // catch Java's string representations of doubles/floats unsupported by us checkNaNAndInfinity( s, radix ); // [if radix<=10:] 'E': next higher precedence, not associative // or right-to-left associative int exp = -1; // note: a distinction of exponent-'E' from large-base-digit-'e' // would be unintuitive, since case doesn't matter with both uses if ( radix <= 10 ) { // handle both [upper/lower] cases final int exp1 = s.indexOf( 'E' ); final int exp2 = s.indexOf( 'e' ); exp = ( exp1 == -1 || ( exp2 != -1 && exp2 < exp1 ) ? exp2 : exp1 ); } if ( exp != -1 ) { String sm = s.substring( 0, exp ); String se = s.substring( exp + 1 ); // suppress recursion: make stack-overflow attacks infeasible if ( se.indexOf( 'E' ) != -1 || se.indexOf( 'e' ) != -1 ) { throw new NumberFormatException( "can't nest 'E'" ); } // skip '+' if ( se.length() > 0 && se.charAt( 0 ) == '+' ) { se = se.substring( 1 ); } // handle '-' boolean negtexp = false; if ( se.length() > 0 && se.charAt( 0 ) == '-' ) { negtexp = true; se = se.substring( 1 ); } // handle "xE", "xE+", "xE-", as "xE0" aka "x" if ( se.equals( "" ) ) { se = "0"; } // [recursion] BigRational exponent = new BigRational( se, radix ); final int iexponent; // transform possible [overflow/fraction] exception try { iexponent = exponent.intValueExact(); } catch ( ArithmeticException e ) { final NumberFormatException e2 = new NumberFormatException( e.getMessage() ); // make sure this operation doesn't shadow the exception to be thrown try { e2.initCause( e ); } catch ( Throwable e3 ) { // ignored } throw e2; } exponent = valueOf( radix ).power( iexponent ); if ( negtexp ) { exponent = exponent.invert(); } // handle "Ex", "+Ex", "-Ex", as "1Ex" if ( sm.equals( "" ) || sm.equals( "+" ) ) { sm = "1"; } else if ( sm.equals( "-" ) ) { sm = "-1"; } // [multiply()'s outcome is already normalized, // so there would be no need to normalize [again]] normalizeFrom( ( new BigRational( sm, radix ) ).multiply( exponent ) ); return; } // '.': next higher precedence, not associative // (can't have more than one dot) String si; final String sf; final int dot = s.indexOf( '.' ); if ( dot != -1 ) { // "[+-]i.f" si = s.substring( 0, dot ); sf = s.substring( dot + 1 ); } else { // "[+-]i". [not just delegating to BigInteger.] si = s; sf = ""; } // check for multiple signs or embedded signs checkNumberFormat( si ); // skip '+' // skip '+'. [BigInteger [likely] doesn't handle these.] if ( si.length() > 0 && si.charAt( 0 ) == '+' ) { si = si.substring( 1 ); } // handle '-' boolean negt = false; if ( si.length() > 0 && si.charAt( 0 ) == '-' ) { negt = true; si = si.substring( 1 ); } // handle ".x" as "0.x" ("." as "0.0") // handle "" as "0" // note: "0" is treated specially and optimized // in bigIntegerValueOf(String,int). if ( si.equals( "" ) ) { si = "0"; } BigInteger n = bigIntegerValueOf( si, radix ); final BigInteger q; // includes the cases "x." and "." if ( !sf.equals( "" ) ) { // check for signs checkFractionFormat( sf ); final BigInteger f = bigIntegerValueOf( sf, radix ); final int scale = sf.length(); q = bigIntegerPower( bigIntegerValueOf( radix ), scale ); n = bigIntegerMultiply( n, q ).add( f ); } else { q = BIG_INTEGER_ONE; } if ( negt ) { n = n.negate(); } normalizeFrom( n, q ); } // javadoc: in-sync with the radix parameter version /** * Construct a BigRational from a string representation, with default radix * (10). * * The supported string formats are "[+-]n", "[+-]n/[+-]q", "[+-]i.f", * "[+-]i", "[+-]iE[+-]e", "[+-]i.fE[+-]e" (latter two only with * radix<=10, due to possible ambiguities); n and q can be any of the * latter (i.e. mixed representations such as "-1.2E-3/-4.5E-6" are * supported). * * Samples: "-21/35", "3.4", "-65.4E-3", "f/37" (base 16), * "1011101011010110" (base 2). * * [Exponential representation wasn't supported in an earlier version.] */ public BigRational( final String s ) { this( s, DEFAULT_RADIX ); } /** * Construct a BigRational from a decimal. * * @param decimal Decimal value. */ public BigRational( final BigDecimal decimal ) { this( decimal.unscaledValue(), decimal.scale() ); } /** * Construct a BigRational from an unscaled value and a scale value. */ public BigRational( final BigInteger unscaledValue, int scale, final int radix ) { if ( unscaledValue == null ) { throw new NumberFormatException( "null" ); } final boolean negt = ( scale < 0 ); if ( negt ) { scale = -scale; } checkRadix( radix ); final BigInteger scaleValue = bigIntegerPower( bigIntegerValueOf( radix ), scale ); normalizeFrom( ( negt ? bigIntegerMultiply( unscaledValue, scaleValue ) : unscaledValue ), ( negt ? BIG_INTEGER_ONE : scaleValue ) ); } /** * Construct a BigRational from an unscaled value and a scale value, default * radix (10). */ public BigRational( final BigInteger unscaledValue, final int scale ) { this( unscaledValue, scale, DEFAULT_RADIX ); } /** * Construct a BigRational from an unscaled fix number value and a scale * value. */ public BigRational( final long unscaledValue, final int scale, final int radix ) { this( bigIntegerValueOf( unscaledValue ), scale, radix ); } /** * Construct a BigRational from a [IEEE 754] double [size/precision] * floating point number. */ public BigRational( final double x ) { // // [rounding step, possible loss of precision step] // this (String.valueOf(x)); normalizeFrom( valueOfDoubleBits( Double.doubleToLongBits( x ) ) ); } /** * Construct a BigRational from a [IEEE 754] single [size/precision] * floating point number. */ public BigRational( final float x ) { // this ((double)x); normalizeFrom( valueOfFloatBits( Float.floatToIntBits( x ) ) ); } // can't have public BigRational(long unscaledValue, int scale) // as alias for BigRational(unscaledValue, scale, DEFAULT_RADIX); // it's too ambiguous with public BigRational(long n, long q). /** * Normalize BigRational. Denominator will be positive, numerator and * denominator will have no common divisor. BigIntegers -1, 0, 1 will be set * to constants for later comparison speed. */ private void normalize() { // note: don't call anything that depends on a normalized this. // i.e.: don't call most (or all) of the BigRational methods. if ( m_n == null || m_q == null ) { throw new NumberFormatException( "null" ); } // [these are typically cheap.] int ns = m_n.signum(); int qs = m_q.signum(); // note: we don't throw on qs==0. that'll be done elsewhere. // if (qs == 0) { // throw new NumberFormatException("quotient zero"); // } if ( ns == 0 && qs == 0 ) { // [typically not reached, due to earlier tests.] // [both for speed] m_n = BIG_INTEGER_ZERO; m_q = BIG_INTEGER_ZERO; return; } if ( ns == 0 ) { m_q = BIG_INTEGER_ONE; // [for speed] m_n = BIG_INTEGER_ZERO; return; } if ( qs == 0 ) { // [typically not reached, due to earlier tests.] m_n = BIG_INTEGER_ONE; // [for speed] m_q = BIG_INTEGER_ZERO; return; } // optimization // check the frequent case of q==1, for speed. // note: this only covers the normalized-for-speed 1-case. if ( m_q == BIG_INTEGER_ONE ) { // [for [later] speed] m_n = bigIntegerValueOf( m_n ); return; } // optimization // check the symmetric case too, for speed. // note: this only covers the normalized-for-speed 1-case. if ( ( m_n == BIG_INTEGER_ONE || m_n == BIG_INTEGER_MINUS_ONE ) && qs > 0 ) { // [for [later] speed] m_q = bigIntegerValueOf( m_q ); return; } // setup torn apart for speed BigInteger na = m_n; BigInteger qa = m_q; if ( qs < 0 ) { m_n = m_n.negate(); m_q = m_q.negate(); ns = -ns; qs = -qs; qa = m_q; if ( ns > 0 ) { na = m_n; } } else { if ( ns < 0 ) { na = m_n.negate(); } } final BigInteger g = na.gcd( qa ); // test: optimization (body: not) if ( !bigIntegerIsOne( g ) ) { m_n = m_n.divide( g ); m_q = m_q.divide( g ); } // for [later] speed, and normalization generally m_n = bigIntegerValueOf( m_n ); m_q = bigIntegerValueOf( m_q ); } /** * Normalize BigRational. [Convenience method to normalize(void).] */ private void normalizeFrom( final BigInteger n, final BigInteger q ) { m_n = n; m_q = q; normalize(); } /** * Normalize BigRational. [Convenience method to normalize(void).] */ private void normalizeFrom( final BigRational that ) { if ( that == null ) { throw new NumberFormatException( "null" ); } normalizeFrom( that.m_n, that.m_q ); } /** * Check constraints on radixes. Radix may not be negative or less than * two. */ private static void checkRadix( final int radix ) { if ( radix < 0 ) { throw new NumberFormatException( "radix negative" ); } if ( radix < 2 ) { throw new NumberFormatException( "radix too small" ); } // note: we don't check for "radix too large"; // that's left to BigInteger.toString(radix) // [i.e.: we don't really mind whether the underlying // system supports base36, or base62, or even more] } /** * Check some of the integer format constraints. */ private static void checkNumberFormat( final String s ) { // "x", "-x", "+x", "", "-", "+" if ( s == null ) { throw new NumberFormatException( "null" ); } // note: 'embedded sign' catches both-signs cases too. final int p = s.indexOf( '+' ); final int m = s.indexOf( '-' ); final int pp = ( p == -1 ? -1 : s.indexOf( '+', p + 1 ) ); final int mm = ( m == -1 ? -1 : s.indexOf( '-', m + 1 ) ); if ( ( p != -1 && p != 0 ) || ( m != -1 && m != 0 ) || pp != -1 || mm != -1 ) { // embedded sign. this covers the both-signs case. throw new NumberFormatException( "embedded sign" ); } } /** * Check number format for fraction part. */ private static void checkFractionFormat( final String s ) { if ( s == null ) { throw new NumberFormatException( "null" ); } if ( s.indexOf( '+' ) != -1 || s.indexOf( '-' ) != -1 ) { throw new NumberFormatException( "sign in fraction" ); } } /** * Check number input for Java's string representations of doubles/floats * that are unsupported: "NaN" and "Infinity" (with or without sign). */ private static void checkNaNAndInfinity( final String s, final int radix ) { // the strings may actually be valid given a large enough radix // (e.g. base 36), so limit the radix/check if ( radix > 16 ) { return; } // [null and empty string check] final int length = ( s == null ? 0 : s.length() ); if ( length < 1 ) { return; } // optimization (String.equals and even more String.equalsIgnoreCase // are quite expensive, charAt and switch aren't) // test for last character in strings below, both cases switch ( s.charAt( length - 1 ) ) { case 'N': case 'n': case 'y': case 'Y': break; default: return; } if ( s.equalsIgnoreCase( "NaN" ) || s.equalsIgnoreCase( "Infinity" ) || s.equalsIgnoreCase( "+Infinity" ) || s.equalsIgnoreCase( "-Infinity" ) ) { throw new NumberFormatException( s ); } } /** * Check constraints on radixes. [Convenience method to checkRadix(radix).] */ private static void checkRadixArgument( final int radix ) { try { checkRadix( radix ); } catch ( Exception e ) { throw new IllegalArgumentException( e.getMessage() ); } } /** * Proxy to BigInteger.valueOf(). Speeds up comparisons by using constants. */ private static BigInteger bigIntegerValueOf( final long n ) { // return the internal constants used for checks if possible. // optimization // check whether it's outside int range. // actually check a much narrower range, fitting the switch below. if ( n >= -16 && n <= 16 ) { // note: test above needed to make the cast below safe // jump table, for speed switch ( (int)n ) { case 0: return BIG_INTEGER_ZERO; case 1: return BIG_INTEGER_ONE; case -1: return BIG_INTEGER_MINUS_ONE; case 2: return BIG_INTEGER_TWO; case -2: return BIG_INTEGER_MINUS_TWO; case 10: return BIG_INTEGER_TEN; case 16: return BIG_INTEGER_SIXTEEN; } } return BigInteger.valueOf( n ); } /** * Convert BigInteger to its constant if possible. Speeds up later * comparisons by using constants. */ private static BigInteger bigIntegerValueOf( final BigInteger n ) { // note: these tests are quite expensive, // so they should be minimized to a reasonable amount. // priority in the tests: 1, 0, -1; // two phase testing. // cheap tests first. // optimization if ( n == BIG_INTEGER_ONE ) { return n; } // optimization if ( n == BIG_INTEGER_ZERO ) { // [typically not reached, since zero is handled specially.] return n; } // optimization if ( n == BIG_INTEGER_MINUS_ONE ) { return n; } // more expensive tests later. // optimization if ( n.equals( BIG_INTEGER_ONE ) ) { return BIG_INTEGER_ONE; } // optimization if ( n.equals( BIG_INTEGER_ZERO ) ) { // [typically not reached from normalize().] return BIG_INTEGER_ZERO; } // optimization if ( n.equals( BIG_INTEGER_MINUS_ONE ) ) { return BIG_INTEGER_MINUS_ONE; } // note: BIG_INTEGER_TWO et al. _not_ used for checks // and therefore not replaced by constants_here_. // this speeds up tests. // not a known constant return n; } /** * Proxy to (new BigInteger()). Speeds up comparisons by using constants. */ private static BigInteger bigIntegerValueOf( final String s, final int radix ) { // note: mind the radix. // however, 0/1/-1 are not a problem. // _often_ used strings (e.g. 0 for empty fraction and // 1 for empty denominator), for speed. // optimization if ( s.equals( "1" ) ) { return BIG_INTEGER_ONE; } // optimization if ( s.equals( "0" ) ) { return BIG_INTEGER_ZERO; } // optimization if ( s.equals( "-1" ) ) { // typically not reached, due to [private] usage pattern, // i.e. the sign is cut before return BIG_INTEGER_MINUS_ONE; } // note: BIG_INTEGER_TWO et al. _not_ used for checks // and therefore even less valuable. // there's a tradeoff between speeds of these tests // and being consistent in using all constants // (at least with the common radixes). // optimization if ( radix > 2 ) { if ( s.equals( "2" ) ) { return BIG_INTEGER_TWO; } if ( s.equals( "-2" ) ) { // typically not reached, due to [private] usage pattern, // i.e. the sign is cut before return BIG_INTEGER_MINUS_TWO; } } // optimization if ( s.equals( "10" ) ) { switch ( radix ) { case 2: return BIG_INTEGER_TWO; case 10: return BIG_INTEGER_TEN; case 16: return BIG_INTEGER_SIXTEEN; } } // optimization if ( radix == 10 && s.equals( "16" ) ) { return BIG_INTEGER_SIXTEEN; } // note: not directly finding the other [radix'] representations // of 10 and 16 in the code above // use the constants if possible return bigIntegerValueOf( new BigInteger( s, radix ) ); } /** * Proxy to BigInteger.equals(). For speed. */ private static boolean bigIntegerEquals( final BigInteger n, final BigInteger m ) { // optimization // first test is for speed. if ( n == m ) { return true; } return n.equals( m ); } /** * Zero (0) value predicate. [For convenience and speed.] */ private static boolean bigIntegerIsZero( final BigInteger n ) { // optimization // first test is for speed. if ( n == BIG_INTEGER_ZERO ) { return true; } // well, this is also optimized for speed a bit. return ( n.signum() == 0 ); } /** * One (1) value predicate. [For convenience and speed.] */ private static boolean bigIntegerIsOne( final BigInteger n ) { // optimization // first test is for speed. if ( n == BIG_INTEGER_ONE ) { return true; } return bigIntegerEquals( n, BIG_INTEGER_ONE ); } /** * Minus-one (-1) value predicate. [For convenience and speed.] */ private static boolean bigIntegerIsMinusOne( final BigInteger n ) { // optimization // first test is for speed. if ( n == BIG_INTEGER_MINUS_ONE ) { return true; } return bigIntegerEquals( n, BIG_INTEGER_MINUS_ONE ); } /* / ** Positive value predicate. * / private static boolean bigIntegerIsPositive(BigInteger n) { return (n.signum() > 0); } */ /** * Negative value predicate. */ private static boolean bigIntegerIsNegative( final BigInteger n ) { return ( n.signum() < 0 ); } /** * Proxy to BigInteger.multiply(). For speed. The more common cases of * integers (q == 1) are optimized. */ private static BigInteger bigIntegerMultiply( final BigInteger n, final BigInteger m ) { // optimization: one or both operands are zero. if ( bigIntegerIsZero( n ) || bigIntegerIsZero( m ) ) { return BIG_INTEGER_ZERO; } // optimization: second operand is one (i.e. neutral element). if ( bigIntegerIsOne( m ) ) { return n; } // optimization: first operand is one (i.e. neutral element). if ( bigIntegerIsOne( n ) ) { return m; } // optimization if ( bigIntegerIsMinusOne( m ) ) { // optimization if ( bigIntegerIsMinusOne( n ) ) { // typically not reached due to earlier test(s) return BIG_INTEGER_ONE; } return n.negate(); } // optimization if ( bigIntegerIsMinusOne( n ) ) { // [m is not -1, see test above] return m.negate(); } // default case. [this would handle all cases.] return n.multiply( m ); } /** * Proxy to BigInteger.pow(). For speed. */ private static BigInteger bigIntegerPower( final BigInteger n, final int exponent ) { // generally expecting exponent>=0 // (there's nor much use in inverting in the integer domain) // the checks for exponent<0 below are done all the same // optimization // jump table, for speed. switch ( exponent ) { case 0: if ( bigIntegerIsZero( n ) ) { // typically not reached, due to earlier test / [private] usage pattern throw new ArithmeticException( "zero exp zero" ); } return BIG_INTEGER_ONE; case 1: return n; } // optimization if ( bigIntegerIsZero( n ) && exponent > 0 ) { // note: exponent==0 already handled above // typically not reached, due to earlier test return BIG_INTEGER_ZERO; } // optimization if ( bigIntegerIsOne( n ) ) { return BIG_INTEGER_ONE; } // optimization if ( bigIntegerIsMinusOne( n ) ) { return ( exponent % 2 == 0 ? BIG_INTEGER_ONE : BIG_INTEGER_MINUS_ONE ); } return n.pow( exponent ); } /** * Binary logarithm rounded towards floor (towards negative infinity). */ // @PrecisionLoss private static int bigIntegerLogarithm2( final BigInteger n ) { if ( bigIntegerIsZero( n ) ) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException( "logarithm of zero" ); } if ( bigIntegerIsNegative( n ) ) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException( "logarithm of negative number" ); } // take this as a start // (don't wholly rely on bitLength() having the same meaning as log2) int exponent = n.bitLength() - 1; if ( exponent < 0 ) { exponent = 0; } BigInteger p = BIG_INTEGER_TWO.pow( exponent + 1 ); while ( n.compareTo( p ) >= 0 ) { // typically not reached p = p.multiply( BIG_INTEGER_TWO ); exponent++; } p = p.divide( BIG_INTEGER_TWO ); while ( n.compareTo( p ) < 0 ) { // typically not reached p = p.divide( BIG_INTEGER_TWO ); exponent--; } // [possible loss of precision step] return exponent; } /** * Proxy to BigInteger.toString(int radix). */ private static String stringValueOf( final BigInteger n, final int radix ) { return n.toString( radix ); } /** * Proxy to stringValueOf(bigIntegerValueOf(long), radix); take the same * route to format [long/bigint] integer numbers [despite the overhead]. */ private static String stringValueOf( final long n, final int radix ) { return stringValueOf( bigIntegerValueOf( n ), radix ); } /** * Convert a IEEE 754 floating point number (of different sizes, as array of * longs, big endian) to a BigRational. */ private static BigRational fromIeee754( final long[] value0, int fractionSize, final int exponentSize ) { if ( value0 == null ) { throw new NumberFormatException( "null" ); } // note: the long(s) in the input array are considered unsigned, // so expansion operations (to e.g. BigRational) and [right-] shift operations // (unlike assignment, equality-test, narrowing, and/or operations) // must be appropriately chosen BigRational fraction0 = ZERO; // start at the little end of the [bigendian] input int i = value0.length - 1; while ( fractionSize >= 64 ) { if ( i < 0 ) { throw new NumberFormatException( "not enough bits" ); } // mind the long (value0[i]) being unsigned fraction0 = fraction0.add( valueOfUnsigned( value0[ i ] ) ).divide( TWO_POWER_64 ); fractionSize -= 64; i--; } // the rest must now fit into value0[0] (a long), // i.e. we don't support exponentSize > 63 at the moment; // as the power() method accepts ints (not longs), // the restriction is actually even on <= 31 bits if ( i < 0 ) { throw new NumberFormatException( "no bits" ); } if ( i > 0 ) { throw new NumberFormatException( "excess bits" ); } long value = value0[ 0 ]; // [fractionSize [now is] < 64 by loop above] final long fractionMask = ( (long)1 << fractionSize ) - 1; final long rawFraction = value & fractionMask; value >>>= fractionSize; // [exponentSize < 32 by [private] usage pattern; rawExponent < 2**31] final int exponentMask = ( 1 << exponentSize ) - 1; final int exponentBias = ( 1 << ( exponentSize - 1 ) ) - 1; final int rawExponent = (int)value & exponentMask; value >>>= exponentSize; final int signSize = 1; final int signMask = ( 1 << signSize ) - 1; // 1 final int rawSign = (int)value & signMask; value >>>= signSize; if ( value != 0 ) { throw new NumberFormatException( "excess bits" ); } // check for Infinity and NaN (IEEE 754 rawExponent at its maximum) if ( rawExponent == exponentMask ) { // (no fraction bits means one of the Infinities; else NaN) throw new NumberFormatException( rawFraction == 0 && fraction0.isZero() ? ( rawSign == 0 ? "Infinity" : "-Infinity" ) : "NaN" ); } // optimization -- avoids power() calculation below // (isZero and zero multiply) are cheap // check for zero (IEEE 754 rawExponent zero and no fraction bits) if ( rawExponent == 0 && rawFraction == 0 && fraction0.isZero() ) { return ZERO; } // handle subnormal numbers too (with rawExponent==0) // [fractionSize [still is] < 64] final long mantissa1 = rawFraction | ( rawExponent == 0 ? (long)0 : (long)1 << fractionSize ); // mind mantissa1 being unsigned final BigRational mantissa = fraction0.add( valueOfUnsigned( mantissa1 ) ); // (subnormal numbers; exponent is one off) // [rawExponent < 2**31; exponentBias < 2**30] final int exponent = rawExponent - exponentBias + ( rawExponent == 0 ? 1 : 0 ) - fractionSize; final int sign = ( rawSign == 0 ? 1 : -1 ); return valueOf( 2 ).power( exponent ).multiply( mantissa ).multiply( sign ); } /** * Convert a BigRational to a IEEE 754 floating point number (of different * sizes, as array of longs, big endian). * * Possible loss of precision. */ // @PrecisionLoss private static long[] toIeee754( BigRational value, final int fractionSize, final int exponentSize ) { if ( value == null ) { throw new NumberFormatException( "null" ); } // [needed size: fractionSize+exponentSize+1; round up bits to a multiple of 64] final long[] out0 = new long[ ( fractionSize + exponentSize + 1 + ( 64 - 1 ) ) / 64 ]; if ( value.isZero() ) { // 0.0 // note: as we don't keep a sign with our ZERO, // we never return IEEE 754 -0.0 here for ( int j = 0; j < out0.length; j++ ) { out0[ j ] = 0; } return out0; } final boolean negt = value.isNegative(); if ( negt ) { value = value.negate(); } // need to scale to this to get the full mantissa int exponent = fractionSize; final BigRational lower = valueOf( 2 ).power( fractionSize ); final BigRational upper = lower.multiply( 2 ); // optimization, and a good guess (but not exact in all cases) final int scale = lower.divide( value ).logarithm2(); value = value.multiply( valueOf( 2 ).power( scale ) ); exponent -= scale; while ( value.compareTo( lower ) < 0 ) { // [typically done zero or one time] value = value.multiply( 2 ); exponent--; } while ( value.compareTo( upper ) >= 0 ) { // [typically not reached] value = value.divide( 2 ); exponent++; } // [rounding step, possible loss of precision step] BigInteger mantissa = value.bigIntegerValue(); // adjust after [unfortunate] mantissa rounding if ( upper.compareTo( mantissa ) <= 0 ) { mantissa = mantissa.divide( BIG_INTEGER_TWO ); exponent++; } // start [to fill] at the little end of the [bigendian] output int i = out0.length - 1; int fractionSize1 = fractionSize; while ( fractionSize1 >= 64 ) { final BigInteger[] divrem = mantissa.divideAndRemainder( BIG_INTEGER_TWO_POWER_64 ); // [according to BigInteger javadoc] this takes the least significant 64 bits; // i.e. in this case the long is considered unsigned, as we want it out0[ i ] = divrem[ 1 ].longValue(); fractionSize1 -= 64; mantissa = divrem[ 0 ]; i--; } // the rest must now fit into out0[0] if ( i < 0 ) { // not reached throw new NumberFormatException( "too many bits" ); } if ( i > 0 ) { // not reached throw new NumberFormatException( "not enough bits" ); } long fraction = mantissa.longValue(); final int exponentBias = ( 1 << ( exponentSize - 1 ) ) - 1; exponent += exponentBias; final int maximalExponent = ( 1 << exponentSize ) - 1; if ( exponent >= maximalExponent ) { // overflow // throw new NumberFormatException("overflow"); // [positive or negative] infinity exponent = maximalExponent; fraction = 0; for ( int j = 1; j < out0.length; j++ ) { out0[ j ] = 0; } // [keep sign] } else if ( exponent <= 0 ) { // handle subnormal numbers too // [with know loss of precision] // drop one bit, while keeping the exponent int s = 1; // [need not shift more than fractionSize] final int n = ( -exponent > fractionSize ? fractionSize : -exponent ); s += n; exponent += n; // [possible loss of precision step] fraction = shiftrx( fraction, out0, 1, s ); boolean zero = ( fraction == 0 ); for ( int j = 1; zero && j < out0.length; j++ ) { zero = ( out0[ j ] == 0 ); } if ( zero ) { // underflow // throw new NumberFormatException("underflow"); // 0.0 or -0.0; i.e.: keep sign exponent = 0; // [nonzero == 0 implies the rest of the fraction is zero as well] } } // cut implied most significant bit // [unless with subnormal numbers] if ( exponent != 0 ) { fraction &= ~( (long)1 << fractionSize1 ); } long out = 0; out |= ( negt ? 1 : 0 ); out <<= exponentSize; out |= exponent; out <<= fractionSize1; out |= fraction; out0[ 0 ] = out; return out0; } /** * Shift right, while propagating shifted bits (long[] is bigendian). */ private static long shiftrx( long a, final long[] b, final int boff, int n ) { while ( n > 0 ) { final int n2 = ( n < 63 ? n : 63 ); final long m = ( (long)1 << n2 ) - 1; long c = a & m; a >>>= n2; for ( int i = boff; i < b.length; i++ ) { final long t = b[ i ] & m; b[ i ] >>>= n2; b[ i ] |= ( c << ( 64 - n2 ) ); c = t; } n -= n2; } return a; } // note: don't use valueOf() here; valueOf implementations use the constants /** * The constant zero (0). */ // [Constant name: see class BigInteger.] public static final BigRational ZERO = new BigRational( 0 ); /** * The constant one (1). */ // [Name: see class BigInteger.] public static final BigRational ONE = new BigRational( 1 ); /** * The constant minus-one (-1). */ public static final BigRational MINUS_ONE = new BigRational( -1 ); // some more constants, often used as radixes/bases /** * The constant two (2). */ private static final BigRational TWO = new BigRational( 2 ); /** * The constant ten (10). */ private static final BigRational TEN = new BigRational( 10 ); /** * The constant sixteen (16). */ private static final BigRational SIXTEEN = new BigRational( 16 ); // some more constants /** * The constant two to the power of 64 (18446744073709551616). Used is * slicing larger [than double size] IEEE 754 values. */ private static final BigRational TWO_POWER_64 = new BigRational( BIG_INTEGER_TWO_POWER_64 ); /** * Positive predicate. * * Indicates whether this BigRational is larger than zero. Zero is not * positive. * * [For convenience.] */ public boolean isPositive() { return ( signum() > 0 ); } /** * Negative predicate. * * Indicates whether this BigRational is smaller than zero. Zero isn't * negative either. * * [For convenience.] */ public boolean isNegative() { return ( signum() < 0 ); } /** * Zero predicate. * * Indicates whether this BigRational is zero. * * [For convenience and speed.] */ public boolean isZero() { // optimization // first test is for speed. if ( this == ZERO || m_n == BIG_INTEGER_ZERO ) { return true; } // well, this is also optimized for speed a bit. return ( signum() == 0 ); } /** * One predicate. * * Indicates whether this BigRational is 1. * * [For convenience and speed.] */ public boolean isOne() { // optimization // first test is for speed. if ( this == ONE ) { return true; } return equals( ONE ); } /** * Minus-one predicate. * * Indicates whether this BigRational is -1. * * [For convenience and speed.] */ public boolean isMinusOne() { // optimization // first test is for speed. if ( this == MINUS_ONE ) { return true; } return equals( MINUS_ONE ); } /** * Integer predicate. * * Indicates whether this BigRational convertible to a BigInteger without * loss of precision. True iff quotient is one. */ public boolean isInteger() { return bigIntegerIsOne( m_q ); } /** * BigRational string representation, format "[-]n[/q]". * * Sample output: "6172839/5000". */ public String toString( final int radix ) { checkRadixArgument( radix ); final String s = stringValueOf( m_n, radix ); if ( isInteger() ) { return s; } return s + "/" + stringValueOf( m_q, radix ); } /** * BigRational string representation, format "[-]n[/q]", default radix * (10). * * Default string representation, as rational, not using an exponent. * * Sample output: "6172839/5000". * * Overwrites Object.toString(). */ @Override public String toString() { return toString( DEFAULT_RADIX ); } /** * Fixed dot-format "[-]i.f" string representation, with a precision. * * Precision may be negative, in which case the rounding affects digits left * of the dot, i.e. the integer part of the number, as well. * * Sample output: "1234.567800". * * Possible loss of precision. */ // @PrecisionLoss public String toStringDot( final int precision, final int radix ) { return toStringDot( precision, radix, false ); } /** * Fixed dot-format "[-]i.f" string representation, with a precision. * * Precision may be negative, in which case the rounding affects digits left * of the dot, i.e. the integer part of the number, as well. * * The exponentFormat parameter allows for shorter [intermediate] string * representation, an optimization, e.g. used with toStringExponent. * * Possible loss of precision. */ // @PrecisionLoss private String toStringDot( final int precision, final int radix, final boolean exponentFormat ) { checkRadixArgument( radix ); BigRational scaleValue = new BigRational( bigIntegerPower( bigIntegerValueOf( radix ), ( precision < 0 ? -precision : precision ) ) ); if ( precision < 0 ) { scaleValue = scaleValue.invert(); } // default round mode. // [rounding step, possible loss of precision step] BigRational n = multiply( scaleValue ).round(); final boolean negt = n.isNegative(); if ( negt ) { n = n.negate(); } String s = n.toString( radix ); if ( exponentFormat ) { // note that this is _not_ the scientific notation // (one digit left of the dot exactly), // but some intermediate representation suited for post processing // [leaving away the left/right padding steps // is more performant in time and memory space] s = s + "E" + stringValueOf( -precision, radix ); } else { if ( precision >= 0 ) { // left-pad with '0' while ( s.length() <= precision ) { s = "0" + s; } final int dot = s.length() - precision; final String i = s.substring( 0, dot ); final String f = s.substring( dot ); s = i; if ( f.length() > 0 ) { s = s + "." + f; } } else { if ( !s.equals( "0" ) ) { // right-pad with '0' for ( int i = -precision; i > 0; i-- ) { s = s + "0"; } } } } // add sign if ( negt ) { s = "-" + s; } return s; } /** * Dot-format "[-]i.f" string representation, with a precision, default * radix (10). Precision may be negative. * * Sample output: "1234.567800". * * Possible loss of precision. */ // @PrecisionLoss public String toStringDot( final int precision ) { // [possible loss of precision step] return toStringDot( precision, DEFAULT_RADIX, false ); } // note: there is no 'default' precision. /** * Dot-format "[-]i.f" string representation, with a relative precision. * * If the relative precision is zero or negative, "0" will be returned (i.e. * total loss of precision). * * Possible loss of precision. */ // @PrecisionLoss public String toStringDotRelative( final int precision, final int radix ) { // kind of expensive, due to expensive logarithm implementation // (with unusual radixes), and post processing checkRadixArgument( radix ); // zero doesn't have any significant digits if ( isZero() ) { return "0"; } // relative precision zero [or less means]: no significant digits at all, i.e. 0 // [loss of precision step] if ( precision <= 0 ) { return "0"; } // guessed [see below: rounding issues] length: sign doesn't matter; // one digit more than logarithm final int guessedLength = absolute().logarithm( radix ) + 1; // [possible loss of precision step] String s = toStringDot( precision - guessedLength, radix ); // [floor of] logarithm and [arithmetic] rounding [half-up] // need post-processing: // find first significant digit and check for dot boolean dot = false; int i; for ( i = 0; i < s.length(); i++ ) { final char c = s.charAt( i ); if ( c == '.' ) { dot = true; } // expecting nothing than '-', '.', and digits if ( c != '-' && c != '.' && c != '0' ) { break; } } // count digits / [still] check for dot int digits = 0; for ( ; i < s.length(); i++ ) { if ( s.charAt( i ) == '.' ) { dot = true; } else { digits++; } } // cut excess zeros // expecting at most 1 excess zero, e.g. for "0.0099999" final int excess = digits - precision; if ( dot && excess > 0 ) { s = s.substring( 0, s.length() - excess ); } return s; } /** * Dot-format "[-]i.f" string representation, with a relative precision, * default radix (10). * * If the relative precision is zero or negative, "0" will be returned (i.e. * total loss of precision). * * Possible loss of precision. */ // @PrecisionLoss public String toStringDotRelative( final int precision ) { // [possible loss of precision step] return toStringDotRelative( precision, DEFAULT_RADIX ); } /** * Exponent-format string representation, with a relative precision, * "[-]i[.f]E[-]e" (where i is one digit other than 0 exactly; f has no * trailing 0); * * Sample output: "1.2E3". * * Possible loss of precision. */ // @PrecisionLoss public String toStringExponent( final int precision, final int radix ) { checkRadixArgument( radix ); // zero doesn't have any significant digits if ( isZero() ) { return "0"; } // relative precision zero [or less means]: no significant digits at all, i.e. 0 // [loss of precision step] if ( precision <= 0 ) { return "0"; } // guessed [see below: rounding issues] length: sign doesn't matter; // one digit more than logarithm final int guessedLength = absolute().logarithm( radix ) + 1; // [possible loss of precision step] final String s = toStringDot( precision - guessedLength, radix, true ); return toExponentRepresentation( s, radix ); } /** * Exponent-format string representation, with a relative precision, default * radix (10), "[-]i[.f]E[-]e" (where i is one digit other than 0 exactly; f * has no trailing 0); * * Sample output: "1.2E3". * * Possible loss of precision. */ // @PrecisionLoss public String toStringExponent( final int precision ) { // [possible loss of precision step] return toStringExponent( precision, DEFAULT_RADIX ); } /** * Transform a [intermediate] dot representation to an exponent-format * representation. */ private static String toExponentRepresentation( String s, final int radix ) { // skip '+' if ( s.length() > 0 && s.charAt( 0 ) == '+' ) { // typically not reached, due to [private] usage pattern s = s.substring( 1 ); } // handle '-' boolean negt = false; if ( s.length() > 0 && s.charAt( 0 ) == '-' ) { negt = true; s = s.substring( 1 ); } // skip initial zeros while ( s.length() > 0 && s.charAt( 0 ) == '0' ) { s = s.substring( 1 ); } // check for and handle exponent // handle only upper case 'E' (we know we use that in earlier steps); // this allows any base using lower case characters int exponent0 = 0; final int exp = s.indexOf( 'E' ); if ( exp != -1 ) { final String se = s.substring( exp + 1 ); s = s.substring( 0, exp ); exponent0 = ( new BigRational( se, radix ) ).intValueExact(); } final String si; String sf; int exponent; final int dot = s.indexOf( '.' ); if ( dot != -1 ) { if ( dot == 0 ) { // possibly more insignificant digits s = s.substring( 1 ); exponent = -1; while ( s.length() > 0 && s.charAt( 0 ) == '0' ) { s = s.substring( 1 ); exponent--; } if ( s.equals( "" ) ) { // typically not reached, due to [private] usage pattern return "0"; } // first significant digit si = s.substring( 0, 1 ); sf = s.substring( 1 ); } else { // initial [significant] digit si = s.substring( 0, 1 ); sf = s.substring( 1, dot ); exponent = sf.length(); sf = sf + s.substring( dot + 1 ); } } else { // [note that we just cut the zeros above] if ( s.equals( "" ) ) { return "0"; } // initial [significant] digit si = s.substring( 0, 1 ); // rest sf = s.substring( 1 ); exponent = sf.length(); } exponent += exponent0; // drop trailing zeros while ( sf.length() > 0 && sf.charAt( sf.length() - 1 ) == '0' ) { sf = sf.substring( 0, sf.length() - 1 ); } s = si; if ( !sf.equals( "" ) ) { s = s + "." + sf; } if ( exponent != 0 ) { s = s + "E" + stringValueOf( exponent, radix ); } if ( negt ) { s = "-" + s; } return s; } /** * Constant internally used, for speed. */ // calculated via BigRational((float)(StrictMath.log(10)/StrictMath.log(2))) // note: don't use float/double operations in this code though (except for test()) private static final BigRational LOGARITHM_TEN_GUESS = new BigRational( 1741647, 524288 ); /** * Constant internally used, for speed. */ private static final BigRational LOGARITHM_SIXTEEN = new BigRational( 4 ); /** * Return binary logarithm rounded towards floor (towards negative * infinity). * * Possible loss of precision. */ // @PrecisionLoss private int logarithm2() { if ( isZero() ) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException( "logarithm of zero" ); } if ( isNegative() ) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException( "logarithm of negative number" ); } final boolean inverted = ( compareTo( ONE ) < 0 ); final BigRational a = ( inverted ? invert() : this ); // [possible loss of precision step] final int log = bigIntegerLogarithm2( a.bigIntegerValue() ); return ( inverted ? -( log + 1 ) : log ); } /** * Return logarithm rounded towards floor (towards negative infinity). * * Possible loss of precision. */ // @PrecisionLoss private int logarithm( final int base ) { // optimization if ( base == 2 ) { return logarithm2(); } if ( isZero() ) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException( "logarithm of zero" ); } if ( isNegative() ) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException( "logarithm of negative number" ); } // if (base < 2) { // // [typically not reached, due to [private] usage pattern] // throw new ArithmeticException("bad base"); // } if ( base < 0 ) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException( "negative base" ); } if ( base < 2 ) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException( "base too small" ); } final boolean inverted = ( compareTo( ONE ) < 0 ); BigRational a = ( inverted ? invert() : this ); final BigRational bbase = valueOf( base ); // optimization -- we could start from n=0 // initial guess // [base 2 handled earlier] // [unusual bases are handled a bit less performant] final BigRational lbase = ( base == 10 ? LOGARITHM_TEN_GUESS : base == 16 ? LOGARITHM_SIXTEEN : valueOf( ilog2( base ) ) ); int n = valueOf( a.logarithm2() ).divide( lbase ).intValue(); a = a.divide( bbase.power( n ) ); // note that these steps are needed anyway: // LOGARITHM_TEN_GUESS above e.g. is (as the name suggests) // a guess only (since most logarithms usually can't be expressed // as rationals generally); odd bases or off even worse while ( a.compareTo( bbase ) >= 0 ) { a = a.divide( bbase ); n++; } while ( a.compareTo( ONE ) < 0 ) { a = a.multiply( bbase ); n--; } // [possible loss of precision step] return ( inverted ? -( n + 1 ) : n ); } /** * Return binary logarithm of an int. */ private static int ilog2( int n ) { if ( n == 0 ) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException( "logarithm of zero" ); } if ( n < 0 ) { // [typically not reached, due to [private] usage pattern] throw new ArithmeticException( "logarithm of negative number" ); } int i = 0; // as this method is used in the context of [small] bases/radixes, // we expect less than 8 iterations at most, so no need to optimize while ( n > 1 ) { n /= 2; i++; } return i; } /** * Add a BigRational to this BigRational and return a new BigRational. * * If one of the operands is zero, [as an optimization] the other * BigRational is returned. */ // [Name: see class BigInteger.] public BigRational add( final BigRational that ) { // optimization: second operand is zero (i.e. neutral element). if ( that.isZero() ) { return this; } // optimization: first operand is zero (i.e. neutral element). if ( isZero() ) { return that; } // note: not checking for that.equals(negate()), // since that would involve creation of a temporary object // note: the calculated n/q may be denormalized, // implicit normalize() is needed. // optimization: same denominator. if ( bigIntegerEquals( m_q, that.m_q ) ) { return new BigRational( m_n.add( that.m_n ), m_q ); } // optimization: second operand is an integer. if ( that.isInteger() ) { return new BigRational( m_n.add( that.m_n.multiply( m_q ) ), m_q ); } // optimization: first operand is an integer. if ( isInteger() ) { return new BigRational( m_n.multiply( that.m_q ).add( that.m_n ), that.m_q ); } // default case. [this would handle all cases.] return new BigRational( m_n.multiply( that.m_q ).add( that.m_n.multiply( m_q ) ), m_q.multiply( that.m_q ) ); } /** * Add a long fix number integer to this BigRational and return a new * BigRational. */ public BigRational add( final long that ) { return add( valueOf( that ) ); } /** * Subtract a BigRational from this BigRational and return a new * BigRational. * * If the second operand is zero, [as an optimization] this BigRational is * returned. */ // [Name: see class BigInteger.] public BigRational subtract( final BigRational that ) { // optimization: second operand is zero. if ( that.isZero() ) { return this; } // optimization: first operand is zero if ( isZero() ) { return that.negate(); } // optimization: operands are equal if ( equals( that ) ) { return ZERO; } // note: the calculated n/q may be denormalized, // implicit normalize() is needed. // optimization: same denominator. if ( bigIntegerEquals( m_q, that.m_q ) ) { return new BigRational( m_n.subtract( that.m_n ), m_q ); } // optimization: second operand is an integer. if ( that.isInteger() ) { return new BigRational( m_n.subtract( that.m_n.multiply( m_q ) ), m_q ); } // optimization: first operand is an integer. if ( isInteger() ) { return new BigRational( m_n.multiply( that.m_q ).subtract( that.m_n ), that.m_q ); } // default case. [this would handle all cases.] return new BigRational( m_n.multiply( that.m_q ).subtract( that.m_n.multiply( m_q ) ), m_q.multiply( that.m_q ) ); } /** * Subtract a long fix number integer from this BigRational and return a new * BigRational. */ public BigRational subtract( final long that ) { return subtract( valueOf( that ) ); } /** * An alias to subtract(). */ public BigRational sub( final BigRational that ) { return subtract( that ); } /** * An alias to subtract(). */ public BigRational sub( final long that ) { return subtract( that ); } /** * Multiply a BigRational to this BigRational and return a new BigRational. * * If one of the operands is one, [as an optimization] the other BigRational * is returned. */ // [Name: see class BigInteger.] public BigRational multiply( final BigRational that ) { // optimization: one or both operands are zero. if ( that.isZero() || isZero() ) { return ZERO; } // optimization: second operand is 1. if ( that.isOne() ) { return this; } // optimization: first operand is 1. if ( isOne() ) { return that; } // optimization: second operand is -1. if ( that.isMinusOne() ) { return negate(); } // optimization: first operand is -1. if ( isMinusOne() ) { return that.negate(); } // note: the calculated n/q may be denormalized, // implicit normalize() is needed. return new BigRational( bigIntegerMultiply( m_n, that.m_n ), bigIntegerMultiply( m_q, that.m_q ) ); } /** * Multiply a long fix number integer to this BigRational and return a new * BigRational. */ public BigRational multiply( final long that ) { return multiply( valueOf( that ) ); } /** * An alias to multiply(). */ public BigRational mul( final BigRational that ) { return multiply( that ); } /** * An alias to multiply(). */ public BigRational mul( final long that ) { return multiply( that ); } /** * Divide this BigRational through another BigRational and return a new * BigRational. * * If the second operand is one, [as an optimization] this BigRational is * returned. */ public BigRational divide( final BigRational that ) { if ( that.isZero() ) { throw new ArithmeticException( "division by zero" ); } // optimization: first operand is zero. if ( isZero() ) { return ZERO; } // optimization: second operand is 1. if ( that.isOne() ) { return this; } // optimization: first operand is 1. if ( isOne() ) { return that.invert(); } // optimization: second operand is -1. if ( that.isMinusOne() ) { return negate(); } // optimization: first operand is -1. if ( isMinusOne() ) { return that.invert().negate(); } // note: the calculated n/q may be denormalized, // implicit normalize() is needed. return new BigRational( bigIntegerMultiply( m_n, that.m_q ), bigIntegerMultiply( m_q, that.m_n ) ); } /** * Divide this BigRational through a long fix number integer and return a * new BigRational. */ public BigRational divide( final long that ) { return divide( valueOf( that ) ); } /** * An alias to divide(). */ public BigRational div( final BigRational that ) { return divide( that ); } /** * An alias to divide(). */ public BigRational div( final long that ) { return divide( that ); } /** * Calculate this BigRational's integer power and return a new BigRational. * * The integer exponent may be negative. * * If the exponent is one, [as an optimization] this BigRational is * returned. */ public BigRational power( int exponent ) { final boolean zero = isZero(); if ( zero ) { if ( exponent == 0 ) { throw new ArithmeticException( "zero exp zero" ); } if ( exponent < 0 ) { throw new ArithmeticException( "division by zero" ); } } // optimization if ( exponent == 0 ) { return ONE; } // optimization // test for exponent<=0 already done if ( zero ) { return ZERO; } // optimization if ( exponent == 1 ) { return this; } // optimization if ( exponent == -1 ) { return invert(); } final boolean negt = ( exponent < 0 ); if ( negt ) { exponent = -exponent; } final BigInteger n = bigIntegerPower( m_n, exponent ); final BigInteger q = bigIntegerPower( m_q, exponent ); // note: the calculated n/q are not denormalized in the sense // of having common factors, but n might be negative (and become q below) return new BigRational( ( negt ? q : n ), ( negt ? n : q ) ); } /** * An alias to power(). */ // [Name: see classes Math, BigInteger.] public BigRational pow( final int exponent ) { return power( exponent ); } /** * Calculate the remainder of this BigRational and another BigRational and * return a new BigRational. * * The remainder result may be negative. * * The remainder is based on round down (towards zero) / truncate. 5/3 == 1 * + 2/3 (remainder 2), 5/-3 == -1 + 2/-3 (remainder 2), -5/3 == -1 + -2/3 * (remainder -2), -5/-3 == 1 + -2/-3 (remainder -2). */ // [Name: see class BigInteger.] public BigRational remainder( final BigRational that ) { final int s = signum(); final int ts = that.signum(); if ( ts == 0 ) { throw new ArithmeticException( "division by zero" ); } BigRational a = this; if ( s < 0 ) { a = a.negate(); } // divisor's sign doesn't matter, as stated above. // this is also BigInteger's behavior, but don't let us be // dependent of a change in that. BigRational b = that; if ( ts < 0 ) { b = b.negate(); } BigRational r = a.remainderOrModulusOfPositive( b ); if ( s < 0 ) { r = r.negate(); } return r; } /** * Calculate the remainder of this BigRational and a long fix number integer * and return a new BigRational. */ public BigRational remainder( final long that ) { return remainder( valueOf( that ) ); } /** * An alias to remainder(). */ public BigRational rem( final BigRational that ) { return remainder( that ); } /** * An alias to remainder(). */ public BigRational rem( final long that ) { return remainder( that ); } /** * Calculate the modulus of this BigRational and another BigRational and * return a new BigRational. * * The modulus result may be negative. * * Modulus is based on round floor (towards negative). 5/3 == 1 + 2/3 * (modulus 2), 5/-3 == -2 + -1/-3 (modulus -1), -5/3 == -2 + 1/3 (modulus * 1), -5/-3 == 1 + -2/-3 (modulus -2). */ public BigRational modulus( final BigRational that ) { final int s = signum(); final int ts = that.signum(); if ( ts == 0 ) { throw new ArithmeticException( "division by zero" ); } BigRational a = this; if ( s < 0 ) { a = a.negate(); } BigRational b = that; if ( ts < 0 ) { b = b.negate(); } BigRational r = a.remainderOrModulusOfPositive( b ); if ( s < 0 && ts < 0 ) { r = r.negate(); } else if ( ts < 0 ) { r = r.subtract( b ); } else if ( s < 0 ) { r = b.subtract( r ); } return r; } /** * Calculate the modulus of this BigRational and a long fix number integer * and return a new BigRational. */ public BigRational modulus( final long that ) { return modulus( valueOf( that ) ); } /** * An alias to modulus(). */ // [Name: see class BigInteger.] public BigRational mod( final BigRational that ) { return modulus( that ); } /** * An alias to modulus(). */ public BigRational mod( final long that ) { return modulus( that ); } /** * Remainder or modulus of non-negative values. Helper function to * remainder() and modulus(). */ private BigRational remainderOrModulusOfPositive( final BigRational that ) { final int s = signum(); final int ts = that.signum(); if ( s < 0 || ts < 0 ) { // typically not reached, due to [private] usage pattern throw new IllegalArgumentException( "negative values(s)" ); } if ( ts == 0 ) { // typically not reached, due to [private] usage pattern throw new ArithmeticException( "division by zero" ); } // optimization if ( s == 0 ) { return ZERO; } return new BigRational( bigIntegerMultiply( m_n, that.m_q ).remainder( bigIntegerMultiply( m_q, that.m_n ) ), bigIntegerMultiply( m_q, that.m_q ) ); } /** * Signum. -1, 0, or 1. * * If this BigRational is negative, -1 is returned; if it is zero, 0 is * returned; if it is positive, 1 is returned. */ // [Name: see class BigInteger.] public int signum() { // note: m_q is positive. return m_n.signum(); } /** * An alias to signum(). */ public int sign() { return signum(); } /** * Return a new BigRational with the absolute value of this BigRational. * * If this BigRational is zero or positive, [as an optimization] this * BigRational is returned. */ public BigRational absolute() { if ( signum() >= 0 ) { return this; } // optimization if ( isMinusOne() ) { return ONE; } // note: the calculated n/q are not denormalized, // implicit normalize() would not be needed. return new BigRational( m_n.negate(), m_q ); } /** * An alias to absolute(). [Name: see classes Math, BigInteger.] */ public BigRational abs() { return absolute(); } /** * Return a new BigRational with the negative value of this. [Name: see * class BigInteger.] */ public BigRational negate() { // optimization if ( isZero() ) { return ZERO; } // optimization if ( isOne() ) { return MINUS_ONE; } // optimization if ( isMinusOne() ) { return ONE; } // note: the calculated n/q are not denormalized, // implicit normalize() would not be needed. return new BigRational( m_n.negate(), m_q ); } /** * An alias to negate(). */ public BigRational neg() { return negate(); } /** * Return a new BigRational with the inverted (reciprocal) value of this. */ public BigRational invert() { if ( isZero() ) { throw new ArithmeticException( "division by zero" ); } // optimization if ( isOne() || isMinusOne() ) { return this; } // note: the calculated n/q are not denormalized in the sense // of having common factors, but n might be negative (and become q below) return new BigRational( m_q, m_n ); } /** * An alias to invert(). */ public BigRational inv() { return invert(); } /** * Return the minimal value of two BigRationals. */ public BigRational minimum( final BigRational that ) { return ( compareTo( that ) <= 0 ? this : that ); } /** * Return the minimal value of a BigRational and a long fix number integer. */ public BigRational minimum( final long that ) { return minimum( valueOf( that ) ); } /** * An alias to minimum(). [Name: see classes Math, BigInteger.] */ public BigRational min( final BigRational that ) { return minimum( that ); } /** * An alias to minimum(). */ public BigRational min( final long that ) { return minimum( that ); } /** * Return the maximal value of two BigRationals. */ public BigRational maximum( final BigRational that ) { return ( compareTo( that ) >= 0 ? this : that ); } /** * Return the maximum value of a BigRational and a long fix number integer. */ public BigRational maximum( final long that ) { return maximum( valueOf( that ) ); } /** * An alias to maximum(). [Name: see classes Math, BigInteger.] */ public BigRational max( final BigRational that ) { return maximum( that ); } /** * An alias to maximum(). */ public BigRational max( final long that ) { return maximum( that ); } /** * Compare object for equality. Overwrites Object.equals(). Semantic of * equality to non-BigRational changed from earlier version: only * BigRationals can be equal. Never throws. * * Overwrites Object.equals(Object). */ @Override public boolean equals( final Object object ) { // optimization if ( object == this ) { return true; } // test includes null if ( !( object instanceof BigRational ) ) { return false; } final BigRational that = (BigRational)object; // optimization if ( that.m_n == m_n && that.m_q == m_q ) { return true; } return ( bigIntegerEquals( that.m_n, m_n ) && bigIntegerEquals( that.m_q, m_q ) ); // [old version] // // delegate to compareTo(Object) // try { // return (compareTo(object) == 0); // } catch (ClassCastException e) { // return false; // } } /** * Hash code. Overwrites Object.hashCode(). * * Overwrites Object.hashCode(). */ @Override public int hashCode() { return ( ( m_n.hashCode() + 1 ) * ( m_q.hashCode() + 2 ) ); } /** * Compare this BigRational to another BigRational. */ public int compareTo( final BigRational that ) { // optimization if ( that == this ) { return 0; } final int s = signum(); final int t = that.signum(); if ( s != t ) { return ( s < t ? -1 : 1 ); } // optimization: both zero. if ( s == 0 ) { return 0; } // note: both m_q are positive. return bigIntegerMultiply( m_n, that.m_q ).compareTo( bigIntegerMultiply( that.m_n, m_q ) ); } /** * Compare this BigRational to a BigInteger. */ public int compareTo( final BigInteger that ) { return compareTo( valueOf( that ) ); } /** * Compare this BigRational to a long. * * Bytes, shorts, and ints can use this by being promoted to long. */ public int compareTo( final long that ) { return compareTo( valueOf( that ) ); } /** * Compare this BigRational to an Object. * * Object can be BigRational/BigInteger/Long/Integer/Short/Byte. * * Implements Comparable.compareTo(Object) (JDK 1.2 and later). * * A sample use is with a sorted map or set, e.g. TreeSet. * * Only BigRational/BigInteger/Long/Integer objects allowed, method will * throw otherwise. * * For backward compatibility reasons we keep compareTo(Object) additionally * to compareTo(BigRational). Comparable<Object> is declared to be * implemented rather than Comparable<BigRational>. */ public int compareTo( final Object object ) { if ( object instanceof Byte ) { return compareTo( ( (Byte)object ).longValue() ); } if ( object instanceof Short ) { return compareTo( ( (Short)object ).longValue() ); } if ( object instanceof Integer ) { return compareTo( ( (Integer)object ).longValue() ); } if ( object instanceof Long ) { return compareTo( ( (Long)object ).longValue() ); } if ( object instanceof BigInteger ) { return compareTo( (BigInteger)object ); } // now assuming that it's either 'instanceof BigRational' // or it'll throw a ClassCastException. return compareTo( (BigRational)object ); } /** * Number of explicit fraction bits in an IEEE 754 double (binary64) float, * 52. */ private static final int DOUBLE_FLOAT_FRACTION_SIZE = 52; /** * Number of exponent bits in an IEEE 754 double (binary64) float, 11. */ private static final int DOUBLE_FLOAT_EXPONENT_SIZE = 11; /** * Number of explicit fraction bits in an IEEE 754 single (binary32) float, * 23. */ private static final int SINGLE_FLOAT_FRACTION_SIZE = 23; /** * Number of exponent bits in an IEEE 754 single (binary32) float, 8. */ private static final int SINGLE_FLOAT_EXPONENT_SIZE = 8; /** * Number of explicit fraction bits in an IEEE 754 half (binary16) float, * 10. */ private static final int HALF_FLOAT_FRACTION_SIZE = 10; /** * Number of exponent bits in an IEEE 754 half (binary16) float, 5. */ private static final int HALF_FLOAT_EXPONENT_SIZE = 5; /** * Number of explicit fraction bits in an IEEE 754 quad (binary128, * quadruple) float, 112. */ private static final int QUAD_FLOAT_FRACTION_SIZE = 112; /** * Number of exponent bits in an IEEE 754 quad (binary128, quadruple) float, * 15. */ private static final int QUAD_FLOAT_EXPONENT_SIZE = 15; /** * Convert to BigInteger, by rounding. * * Possible loss of precision. */ // @PrecisionLoss public BigInteger bigIntegerValue() { // [rounding step, possible loss of precision step] return round().m_n; } /** * Convert to long, by rounding and delegating to BigInteger. Implements * Number.longValue(). As described with BigInteger.longValue(), this just * returns the low-order [64] bits (losing information about magnitude and * sign). * * Possible loss of precision. * * Overwrites Number.longValue(). */ @Override // @PrecisionLoss public long longValue() { // delegate to BigInteger. // [rounding step, possible loss of precision step] return bigIntegerValue().longValue(); } /** * Convert to int, by rounding and delegating to BigInteger. Implements * Number.intValue(). As described with BigInteger.longValue(), this just * returns the low-order [32] bits (losing information about magnitude and * sign). * * Possible loss of precision. * * Overwrites Number.intValue(). */ @Override // @PrecisionLoss public int intValue() { // delegate to BigInteger. // [rounding step, possible loss of precision step] return bigIntegerValue().intValue(); } /** * Convert to double floating point value. Implements Number.doubleValue(). * * Possible loss of precision. * * Overwrites Number.doubleValue(). */ @Override // @PrecisionLoss public double doubleValue() { // // [rounding step, possible loss of precision step] // return (m_n.doubleValue() / m_q.doubleValue()); // // [rounding step, possible loss of precision step] // return Double.parseDouble(toStringExponent(24)); return Double.longBitsToDouble( // [rounding step, possible loss of precision step] doubleBitsValue() ); } /** * Convert to single floating point value. Implements Number.floatValue(). * * Note that BigRational's [implicit] [default] rounding mode that applies * [too] on indirect double to BigRational to float rounding (round-half-up) * may differ from what's done in a direct cast/coercion from double to * float (e.g. round-half-even). * * Possible loss of precision. * * Overwrites Number.floatValue(). */ @Override // @PrecisionLoss public float floatValue() { // // [rounding step, possible loss of precision step] // return (float)doubleValue(); return Float.intBitsToFloat( // [rounding step, possible loss of precision step] singleBitsValue() ); } /** * Convert to IEEE 754 double float bits. The bits can be converted to a * double by Double.longBitsToDouble(). * * Possible loss of precision. */ // @PrecisionLoss public long doubleBitsValue() { // [rounding step, possible loss of precision step] return ( toIeee754( this, DOUBLE_FLOAT_FRACTION_SIZE, DOUBLE_FLOAT_EXPONENT_SIZE )[ 0 ] ); } /** * Convert to IEEE 754 single float bits. The bits can be converted to a * float by Float.intBitsToFloat(). * * Possible loss of precision. */ // @PrecisionLoss public int floatBitsValue() { // [rounding step, possible loss of precision step] return (int)( toIeee754( this, SINGLE_FLOAT_FRACTION_SIZE, SINGLE_FLOAT_EXPONENT_SIZE )[ 0 ] ); } /** * An alias to floatBitsValue(). * * Possible loss of precision. */ // @PrecisionLoss public int singleBitsValue() { // [rounding step, possible loss of precision step] return floatBitsValue(); } /** * Convert this BigRational to IEEE 754 half float (binary16) bits. * * As a short value is returned rather than a int, care has to be taken no * unwanted sign expansion happens in subsequent operations, e.g. by masking * (x.halfBitsValue()&0xffffl) or similar (x.halfBitsValue()==(short)0xbc00). * * Possible loss of precision. */ // @PrecisionLoss public short halfBitsValue() { // [rounding step, possible loss of precision step] return (short)( toIeee754( this, HALF_FLOAT_FRACTION_SIZE, HALF_FLOAT_EXPONENT_SIZE )[ 0 ] ); } /** * Convert this BigRational to IEEE 754 quad float (binary128, quadruple) * bits. * * The bits are returned in an array of two longs, big endian (higher * significant long first). * * Possible loss of precision. */ // @PrecisionLoss public long[] quadBitsValue() { // [rounding step, possible loss of precision step] return toIeee754( this, QUAD_FLOAT_FRACTION_SIZE, QUAD_FLOAT_EXPONENT_SIZE ); } /** * Convert this BigRational to a long integer, either returning an exact * result (no rounding or truncation needed), or throw an * ArithmeticException. */ public long longValueExact() { final long i = longValue(); // test is kind-of costly if ( !equals( valueOf( i ) ) ) { throw new ArithmeticException( isInteger() ? "overflow" : "rounding necessary" ); } return i; } /** * Convert this BigRational to an int, either returning an exact result (no * rounding or truncation needed), or throw an ArithmeticException. */ public int intValueExact() { final int i = intValue(); // test is kind-of costly if ( !equals( valueOf( i ) ) ) { throw new ArithmeticException( isInteger() ? "overflow" : "rounding necessary" ); } return i; } /** * Convert this BigRational to its constant (ONE, ZERO, MINUS_ONE) if * possible. */ public static BigRational valueOf( final BigRational value ) { if ( value == null ) { throw new NumberFormatException( "null" ); } // note: these tests are quite expensive, // but they are minimized to a reasonable amount. // priority in the tests: 1, 0, -1; // two phase testing. // cheap tests first. // optimization if ( value == ONE ) { return value; } // optimization if ( value == ZERO ) { return value; } // optimization if ( value == MINUS_ONE ) { return value; } // more expensive tests later. // optimization if ( value.equals( ONE ) ) { return ONE; } // optimization if ( value.equals( ZERO ) ) { return ZERO; } // optimization if ( value.equals( MINUS_ONE ) ) { return MINUS_ONE; } // not a known constant return value; } /** * Build a BigRational from a String. * * [Roughly] equivalent to {@code new BigRational(value)}. */ public static BigRational valueOf( final String value ) { if ( value == null ) { throw new NumberFormatException( "null" ); } // optimization if ( "0".equals( value ) ) { return ZERO; } // optimization if ( "1".equals( value ) ) { return ONE; } // optimization if ( "-1".equals( value ) ) { return MINUS_ONE; } return new BigRational( value ); } /** * Build a BigRational from a BigInteger. * * Equivalent to {@code new BigRational(value)}. */ public static BigRational valueOf( final BigInteger value ) { return new BigRational( value ); } /** * Build a BigRational from a long fix number integer. * * [Roughly] equivalent to {@code new BigRational(value)}. * * As an optimization, commonly used numbers are returned as a reused * constant. */ public static BigRational valueOf( final long value ) { // return the internal constants if possible // optimization // check whether it's outside int range. // actually check a much narrower range, fitting the switch below. if ( value >= -16 && value <= 16 ) { // note: test above needed to make the cast below safe // jump table, for speed switch ( (int)value ) { case 0: return ZERO; case 1: return ONE; case -1: return MINUS_ONE; case 2: return TWO; case 10: return TEN; case 16: return SIXTEEN; } } return new BigRational( value ); } // note: byte/short/int implicitly upgraded to long, // so strictly the additional implementations aren't needed; // with unsigned (below) they however are /** * Build a BigRational from an int. */ public static BigRational valueOf( final int value ) { return valueOf( (long)value ); } /** * Build a BigRational from a short. */ public static BigRational valueOf( final short value ) { return valueOf( (long)value ); } /** * Build a BigRational from a byte. */ public static BigRational valueOf( final byte value ) { return valueOf( (long)value ); } /** * Build a BigRational from a [IEEE 754] double [size/precision] floating * point number. */ public static BigRational valueOf( final double value ) { return new BigRational( value ); } /** * Build a BigRational from a [IEEE 754] single [size/precision] floating * point number. */ public static BigRational valueOf( final float value ) { return new BigRational( value ); } /** * Build a BigRational from an unsigned long fix number integer. * * The resulting BigRational is positive, i.e. the negative longs are mapped * to 2**63..2**64 (exclusive). */ public static BigRational valueOfUnsigned( final long value ) { final BigRational b = valueOf( value ); // mind the long being unsigned with highest significant // bit (bit#63) set (interpreted as negative by valueOf(long)) return ( b.isNegative() ? b.add( TWO_POWER_64 ) : b ); } /** * Build a BigRational from an unsigned int. * * The resulting BigRational is positive, i.e. the negative ints are mapped * to 2**31..2**32 (exclusive). */ public static BigRational valueOfUnsigned( final int value ) { // masking: suppress sign expansion return valueOf( value & 0xffffffffl ); } /** * Build a BigRational from an unsigned short. * * The resulting BigRational is positive, i.e. the negative shorts are * mapped to 2**15..2**16 (exclusive). */ public static BigRational valueOfUnsigned( final short value ) { // masking: suppress sign expansion return valueOf( value & 0xffffl ); } /** * Build a BigRational from an unsigned byte. * * The resulting BigRational is positive, i.e. the negative bytes are mapped * to 2**7..2**8 (exclusive). */ public static BigRational valueOfUnsigned( final byte value ) { // masking: suppress sign expansion return valueOf( value & 0xffl ); } /** * Build a BigRational from an IEEE 754 double size (double precision, * binary64) floating point number represented as long. * * An IEEE 754 double size (binary64) number uses 1 bit for the sign, 11 * bits for the exponent, and 52 bits (plus 1 implicit bit) for the * fraction/mantissa. The minimal exponent encodes subnormal nubers; the * maximal exponent encodes Infinities and NaNs. * * Infinities and NaNs are not supported as BigRationals. * * The conversion from the bits to a BigRational is done without loss of * precision. */ public static BigRational valueOfDoubleBits( final long value ) { return fromIeee754( new long[] { value, }, DOUBLE_FLOAT_FRACTION_SIZE, DOUBLE_FLOAT_EXPONENT_SIZE ); } /** * Build a BigRational from an IEEE 754 single size (single precision, * binary32) floating point number represented as int. * * An IEEE 754 single size (binary32) number uses 1 bit for the sign, 8 bits * for the exponent, and 23 bits (plus 1 implicit bit) for the * fraction/mantissa. The minimal exponent encodes subnormal nubers; the * maximal exponent encodes Infinities and NaNs. * * Infinities and NaNs are not supported as BigRationals. * * The conversion from the bits to a BigRational is done without loss of * precision. */ public static BigRational valueOfFloatBits( final int value ) { // [masking: suppress sign expansion, that leads to excess bits, // that's not accepted by fromIeee754()] return fromIeee754( new long[] { value & 0xffffffffl, }, SINGLE_FLOAT_FRACTION_SIZE, SINGLE_FLOAT_EXPONENT_SIZE ); } /** * An alias to valueOfFloatBits(). */ public static BigRational valueOfSingleBits( final int value ) { return valueOfFloatBits( value ); } /** * Build a BigRational from an IEEE 754 half size (half precision, binary16) * floating point number represented as short. * * An IEEE 754 half size (binary16) number uses 1 bit for the sign, 5 bits * for the exponent, and 10 bits (plus 1 implicit bit) for the * fraction/mantissa. The minimal exponent encodes subnormal nubers; the * maximal exponent encodes Infinities and NaNs. * * Infinities and NaNs are not supported as BigRationals. * * The conversion from the bits to a BigRational is done without loss of * precision. */ public static BigRational valueOfHalfBits( final short value ) { // [masking: suppress sign expansion, that leads to excess bits, // that's not accepted by fromIeee754()] return fromIeee754( new long[] { value & 0xffffl, }, HALF_FLOAT_FRACTION_SIZE, HALF_FLOAT_EXPONENT_SIZE ); } /** * Build a BigRational from an IEEE 754 quad size (quadruple precision, * binary128) floating point number represented as an array of two longs * (big endian; higher significant long first). * * An IEEE 754 quad size (binary128, quadruple) number uses 1 bit for the * sign, 15 bits for the exponent, and 112 bits (plus 1 implicit bit) for * the fraction/mantissa. The minimal exponent encodes subnormal nubers; the * maximal exponent encodes Infinities and NaNs. * * Infinities and NaNs are not supported as BigRationals. * * The conversion from the bits to a BigRational is done without loss of * precision. */ public static BigRational valueOfQuadBits( final long[] value ) { return fromIeee754( value, QUAD_FLOAT_FRACTION_SIZE, QUAD_FLOAT_EXPONENT_SIZE ); } /** * Compare two IEEE 754 quad size (quadruple precision, binary128) floating * point numbers (each represented as two longs). NaNs are not considered; * comparison is done by bits. [Convenience method.] */ // note: especially due the NaN issue commented above // (a NaN maps to many bits representations), // we call this method quadBitsEqual rather than quadEqual public static boolean quadBitsEqual( final long[] a, final long[] b ) { if ( a == null || b == null ) { throw new NumberFormatException( "null" ); } if ( a.length != 2 || b.length != 2 ) { throw new NumberFormatException( "not a quad" ); } return ( a[ 1 ] == b[ 1 ] && a[ 0 ] == b[ 0 ] ); } /** * Rounding mode to round away from zero. */ public static final int ROUND_UP = 0; /** * Rounding mode to round towards zero. */ public static final int ROUND_DOWN = 1; /** * Rounding mode to round towards positive infinity. */ public static final int ROUND_CEILING = 2; /** * Rounding mode to round towards negative infinity. */ public static final int ROUND_FLOOR = 3; /** * Rounding mode to round towards nearest neighbor unless both neighbors are * equidistant, in which case to round up. */ public static final int ROUND_HALF_UP = 4; /** * Rounding mode to round towards nearest neighbor unless both neighbors are * equidistant, in which case to round down. */ public static final int ROUND_HALF_DOWN = 5; /** * Rounding mode to round towards the nearest neighbor unless both neighbors * are equidistant, in which case to round towards the even neighbor. */ public static final int ROUND_HALF_EVEN = 6; /** * Rounding mode to assert that the requested operation has an exact result, * hence no rounding is necessary. If this rounding mode is specified on an * operation that yields an inexact result, an ArithmeticException is * thrown. */ public static final int ROUND_UNNECESSARY = 7; /** * Rounding mode to round towards nearest neighbor unless both neighbors are * equidistant, in which case to round ceiling. */ public static final int ROUND_HALF_CEILING = 8; /** * Rounding mode to round towards nearest neighbor unless both neighbors are * equidistant, in which case to round floor. */ public static final int ROUND_HALF_FLOOR = 9; /** * Rounding mode to round towards the nearest neighbor unless both neighbors * are equidistant, in which case to round towards the odd neighbor. */ public static final int ROUND_HALF_ODD = 10; /** * Default round mode, ROUND_HALF_UP. */ public static final int DEFAULT_ROUND_MODE = ROUND_HALF_UP; /** * Round. * * Round mode is one of {{@code ROUND_UP, ROUND_DOWN, ROUND_CEILING, * ROUND_FLOOR, ROUND_HALF_UP, ROUND_HALF_DOWN, ROUND_HALF_EVEN, * ROUND_HALF_CEILING, ROUND_HALF_FLOOR, ROUND_HALF_ODD, ROUND_UNNECESSARY, * DEFAULT_ROUND_MODE (==ROUND_HALF_UP)}}. * * If rounding isn't necessary, i.e. this BigRational is an integer, [as an * optimization] this BigRational is returned. * * Possible loss of precision. */ // @PrecisionLoss public BigRational round( final int roundMode ) { // optimization // return self if we don't need to round, independent of rounding mode if ( isInteger() ) { return this; } return new BigRational( // [rounding step, possible loss of precision step] roundToBigInteger( roundMode ) ); } /** * Round to BigInteger helper function. Internally used. * * Possible loss of precision. */ // @PrecisionLoss private BigInteger roundToBigInteger( final int roundMode ) { // note: remainder and its duplicate are calculated for all cases. BigInteger n = m_n; final BigInteger q = m_q; final int sgn = n.signum(); // optimization if ( sgn == 0 ) { // [typically not reached due to earlier test for integerp] return BIG_INTEGER_ZERO; } // keep info on the sign final boolean pos = ( sgn > 0 ); // operate on positive values if ( !pos ) { n = n.negate(); } final BigInteger[] divrem = n.divideAndRemainder( q ); BigInteger dv = divrem[ 0 ]; final BigInteger r = divrem[ 1 ]; // return if we don't need to round, independent of rounding mode if ( bigIntegerIsZero( r ) ) { // [typically not reached since remainder is not zero // with normalized that are not integerp] if ( !pos ) { dv = dv.negate(); } return dv; } boolean up = false; final int comp = r.multiply( BIG_INTEGER_TWO ).compareTo( q ); switch ( roundMode ) { // Rounding mode to round away from zero. case ROUND_UP: up = true; break; // Rounding mode to round towards zero. case ROUND_DOWN: up = false; break; // Rounding mode to round towards positive infinity. case ROUND_CEILING: up = pos; break; // Rounding mode to round towards negative infinity. case ROUND_FLOOR: up = !pos; break; // Rounding mode to round towards "nearest neighbor" unless both // neighbors are equidistant, in which case round up. case ROUND_HALF_UP: up = ( comp >= 0 ); break; // Rounding mode to round towards "nearest neighbor" unless both // neighbors are equidistant, in which case round down. case ROUND_HALF_DOWN: up = ( comp > 0 ); break; case ROUND_HALF_CEILING: up = ( comp != 0 ? comp > 0 : pos ); break; case ROUND_HALF_FLOOR: up = ( comp != 0 ? comp > 0 : !pos ); break; // Rounding mode to round towards the "nearest neighbor" unless both // neighbors are equidistant, in which case, round towards the even neighbor. case ROUND_HALF_EVEN: up = ( comp != 0 ? comp > 0 : !bigIntegerIsZero( dv.remainder( BIG_INTEGER_TWO ) ) ); break; case ROUND_HALF_ODD: up = ( comp != 0 ? comp > 0 : bigIntegerIsZero( dv.remainder( BIG_INTEGER_TWO ) ) ); break; // Rounding mode to assert that the requested operation has an exact // result, hence no rounding is necessary. If this rounding mode is // specified on an operation that yields an inexact result, an // ArithmeticException is thrown. case ROUND_UNNECESSARY: if ( !bigIntegerIsZero( r ) ) { throw new ArithmeticException( "rounding necessary" ); } // [typically not reached due to earlier test for integerp] up = false; break; default: throw new IllegalArgumentException( "unsupported rounding mode" ); } if ( up ) { dv = dv.add( BIG_INTEGER_ONE ); } if ( !pos ) { dv = dv.negate(); } // [rounding step, possible loss of precision step] return dv; } /** * Round by default mode (ROUND_HALF_UP). * * Possible loss of precision. */ // @PrecisionLoss public BigRational round() { // [rounding step, possible loss of precision step] return round( DEFAULT_ROUND_MODE ); } /** * Floor, round towards negative infinity. * * Possible loss of precision. */ // @PrecisionLoss public BigRational floor() { // [rounding step, possible loss of precision step] return round( ROUND_FLOOR ); } /** * Ceiling, round towards positive infinity. * * Possible loss of precision. */ // @PrecisionLoss public BigRational ceiling() { // [rounding step, possible loss of precision step] return round( ROUND_CEILING ); } /** * An alias to ceiling(). * * Possible loss of precision. */ // [Name: see class Math.] // @PrecisionLoss public BigRational ceil() { // [possible loss of precision step] return ceiling(); } /** * Truncate, round towards zero. * * Possible loss of precision. */ // @PrecisionLoss public BigRational truncate() { // [rounding step, possible loss of precision step] return round( ROUND_DOWN ); } /** * An alias to truncate(). * * Possible loss of precision. */ // @PrecisionLoss public BigRational trunc() { // [possible loss of precision step] return truncate(); } /** * Integer part. * * Possible loss of precision. */ // @PrecisionLoss public BigRational integerPart() { // [rounding step, possible loss of precision step] return round( ROUND_DOWN ); } /** * Fractional part. * * Possible loss of precision. */ // @PrecisionLoss public BigRational fractionalPart() { // this==ip+fp; sign(fp)==sign(this) // [possible loss of precision step] return subtract( integerPart() ); } /** * Return an array of BigRationals with both integer and fractional part. * * Integer part is returned at offset 0; fractional part at offset 1. */ public BigRational[] integerAndFractionalPart() { // note: this duplicates fractionalPart() code, for speed. final BigRational[] pp = new BigRational[ 2 ]; final BigRational ip = integerPart(); pp[ 0 ] = ip; pp[ 1 ] = subtract( ip ); return pp; } /** * Returns the numerator. * * @return Numerator. */ public BigInteger getNumerator() { return m_n; } /** * Returns the denominator. * * @return Denominator. */ public BigInteger getDenominator() { return m_q; } /** * Returns a clone of this object. * * @return Cloned object. * * @throws CloneNotSupportedException if the object doesn't support the * {@code Cloneable} interface. */ public BigRational clone() throws CloneNotSupportedException { return (BigRational)super.clone(); } }