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

org.jscience.mathematics.number.Real Maven / Gradle / Ivy

The newest version!
/*
 * JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
 * Copyright (C) 2006 - JScience (http://jscience.org/)
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software is
 * freely granted, provided that this notice is preserved.
 */
package org.jscience.mathematics.number;

import org.jscience.mathematics.structure.Field;

import javolution.lang.MathLib;
import javolution.context.HeapContext;
import javolution.context.LocalContext;
import javolution.context.ObjectFactory;
import javolution.text.Text;
import javolution.text.TypeFormat;
import javolution.xml.XMLFormat;
import javolution.xml.stream.XMLStreamException;

/**
 * 

This class represents a real number of arbitrary precision with * known/guaranteed uncertainty. A real number consists of a * {@link #getSignificand significand}, a maximum {@link #getError error} * (on the significand value) and a decimal {@link #getExponent exponent}: * ((significand ± error) · 10exponent).

* *

Reals number can be {@link #isExact exact} (e.g. integer values * scaled by a power of ten). Exactness is maintained for * {@link org.jscience.mathematics.structure.Ring Ring} operations * (e.g. addition, multiplication), but typically lost when a * multiplicative {@link #inverse() inverse} is calculated. The minimum * precision used for exact numbers is set by * {@link #setExactPrecision(int)} ({@link * javolution.context.LocalContext context local} setting, default * 19 digits).

* *

The actual {@link #getPrecision precision} and {@link #getAccuracy * accuracy} of any real number is available and guaranteed * (the true/exact value is always within the precision/accuracy range).

* *

Operations on instances of this class are quite fast * as information substantially below the precision level (aka noise) * is not processed/stored. There is no limit on a real precision * but precision degenerates (due to numeric errors) and calculations * accelerate as more and more operations are performed.

* *

Instances of this class can be utilized to find approximate * solutions to linear equations using the * {@link org.jscience.mathematics.vector.Matrix Matrix} class for which * high-precision reals is often required, the primitive type * double being not accurate enough to resolve equations * when the matrix's size exceeds 100x100. Furthermore, even for small * matrices the "qualified" result is indicative of possible system * singularities.

* * @author Jean-Marie Dautelle * @version 3.3, January 8, 2006 * @see * Wikipedia: Real number */ public final class Real extends Number implements Field { /** * Holds the default XML representation for real numbers. * This representation consists of a simple value attribute * holding the {@link #toText() textual} representation. */ static final XMLFormat XML = new XMLFormat(Real.class) { @Override public Real newInstance(Class cls, InputElement xml) throws XMLStreamException { return Real.valueOf(xml.getAttribute("value")); } public void write(Real real, OutputElement xml) throws XMLStreamException { xml.setAttribute("value", real.toText()); } public void read(InputElement xml, Real real) { // Nothing to do, immutable. } }; /** * Holds a Not-a-Number instance (infinite error). */ public static final Real NaN = new Real(); // Unique (0 ± 1E2147483647) static { NaN._significand = LargeInteger.ZERO; NaN._error = LargeInteger.ONE; NaN._exponent = Integer.MAX_VALUE; } /** * Holds the exact ZERO instance. */ public static final Real ZERO; /** * Holds the exact ONE instance. */ public static final Real ONE; /** * Holds local precision for exact number. */ private static final LocalContext.Reference EXACT_PRECISION = new LocalContext.Reference( new Integer(19)); /** * The significand value. */ private LargeInteger _significand; /** * The significand error (0 for exact number). */ private LargeInteger _error; /** * The decimal exponent. */ private int _exponent; /** * Default constructor. */ private Real() { } /** * Returns the {@link javolution.context.LocalContext local} minimum * precision (number of exact digits) when exact numbers have to be * approximated. * * @return the minimum number of digits assumed exact for {@link #isExact * exact} real numbers. */ public static int getExactPrecision() { return EXACT_PRECISION.get(); } /** * Sets the {@link javolution.context.LocalContext local} minimum precision * (number of exact digits) when exact numbers have to be approximated. * * @param precision the minimum number of digits assumed exact for * {@link #isExact exact} numbers. */ public static void setExactPrecision(int precision) { EXACT_PRECISION.set(precision); } /** * Returns a real having the specified significand, error and exponent values. * If the error is 0, the real is assumed exact. * For example:[code] * * // x = 0.0 ± 0.01 * Real x = Real.valueOf(LargeInteger.ZERO, 1, -2); * * // y = -12.3 exact * Real y = Real.valueOf(LargeInteger.valueOf(-123), 0, -1); * * [/code] * * @param significand this real significand. * @param error the maximum error on the significand. * @param exponent the decimal exponent. * @return (significand ± error)·10exponent) * @throws IllegalArgumentException if error < 0 */ public static Real valueOf(LargeInteger significand, int error, int exponent) { if (error < 0) throw new IllegalArgumentException("Error cannot be negative"); Real real = FACTORY.object(); real._significand = significand; real._error = LargeInteger.valueOf(error); real._exponent = exponent; return real; } /** * Returns the real number (inexact except for 0.0) * corresponding to the specified double value. * The error is derived from the inexact representation of * double values intrinsic to the 64 bits IEEE 754 format. * * @param doubleValue the double value to convert. * @return the corresponding real number. */ public static Real valueOf(double doubleValue) { if (doubleValue == 0.0) return Real.ZERO; if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue)) return Real.NaN; // Find the exponent e such as: value == x.xxx * 10^e int e = MathLib.floorLog10(MathLib.abs(doubleValue)) - 18 + 1; // 18 digits significand. long significand = MathLib.toLongPow10(doubleValue, -e); int error = (int) MathLib.toLongPow10(Math.ulp(doubleValue), -e) + 1; return Real.valueOf(LargeInteger.valueOf(significand), error, e); } /** * Returns the exact real number corresponding to the specified * long value (convenience method). * * @param longValue the exact long value. * @return Real.valueOf(LargeInteger.valueOf(longValue), 0, 0) */ public static Real valueOf(long longValue) { return Real.valueOf(LargeInteger.valueOf(longValue), 0, 0); } /** * Returns the real for the specified character sequence. * If the precision is not specified (using the ± symbol), * the real is supposed exact. Example of valid character sequences: *
  • "1.2E3" (1200 exact)
  • *
  • "1.2E3±1E-2" (1200 ± 0.01)
  • * * @param chars the character sequence. * @return the corresponding real number. * @throws NumberFormatException if the character sequence does not contain * a parsable real. */ public static Real valueOf(CharSequence chars) throws NumberFormatException { if ('-' == chars.charAt(0)) { return valueOf(chars.subSequence(1, chars.length())).opposite(); } Text txt = Text.valueOf(chars); // TODO Use TextFormat... if ((txt.length() == 3) && (txt.indexOf("NaN", 0) == 0)) return NaN; if (txt.equals("0")) return ZERO; int exponentIndex = txt.indexOf("E", 0); if (exponentIndex >= 0) { int exponent = TypeFormat.parseInt(txt.subtext(exponentIndex + 1, txt.length())); Real r = valueOf(txt.subtext(0, exponentIndex)); if (r == ZERO) return valueOf(LargeInteger.ZERO, 1, exponent); r._exponent += exponent; return r; } Real real = FACTORY.object(); int errorIndex = txt.indexOf("±", 0); if (errorIndex >= 0) { real._significand = LargeInteger.valueOf(txt.subtext(0, errorIndex)); real._error = LargeInteger.valueOf(txt.subtext(errorIndex + 1, txt .length())); if (real._error.isNegative()) throw new NumberFormatException(chars + " not parsable (error cannot be negative)"); real._exponent = 0; return real; } int decimalPointIndex = txt.indexOf(".", 0); if (decimalPointIndex >= 0) { LargeInteger integer = LargeInteger.valueOf(txt.subtext(0, decimalPointIndex)); LargeInteger fraction = LargeInteger.valueOf(txt.subtext( decimalPointIndex + 1, txt.length())); int fractionDigits = chars.length() - decimalPointIndex - 1; real._significand = integer.isNegative() ? integer.times10pow( fractionDigits).minus(fraction) : integer.times10pow( fractionDigits).plus(fraction); real._error = LargeInteger.ZERO; real._exponent = -fractionDigits; return real; } else { real._significand = LargeInteger.valueOf(chars); real._error = LargeInteger.ZERO; real._exponent = 0; return real; } } /** * Returns this real * significand value. * * @return the significand. */ public LargeInteger getSignificand() { return _significand; } /** * Returns the maximum error (positive) on this real significand. * * @return the maximum error on the significand. */ public int getError() { return _error.intValue(); } /** * Returns the exponent of the power of 10 multiplier. * * @return the decimal exponent. */ public int getExponent() { return _exponent; } /** * Indicates if this real number is exact ({@link #getError() error} * == 0). * * @return getError() == 0 */ public boolean isExact() { return _error.isZero(); } /** * Returns the number of decimal digits guaranteed exact which appear to * the right of the decimal point (absolute error). * * @return a measure of the absolute error of this real number. */ public int getAccuracy() { if (_error.isZero()) return Integer.MAX_VALUE; if (this == NaN) return Integer.MIN_VALUE; return -_exponent - _error.digitLength(); } /** * Returns the total number of decimal digits guaranteed exact * (relative error). * * @return a measure of the relative error of this real number. */ public final int getPrecision() { if (_error.isZero()) return Integer.MAX_VALUE; if (this == NaN) return Integer.MIN_VALUE; return _significand.digitLength() - _error.digitLength(); } /** * Indicates if this real is greater than zero. * * @return this > 0 */ public boolean isPositive() { return _significand.isPositive(); } /** * Indicates if this real is less than zero. * * @return this < 0 */ public boolean isNegative() { return _significand.isNegative(); } /** * Indicates if this real is Not-a-Number (unbounded value interval). * * @return true if this number has unbounded value interval; * false otherwise. */ public boolean isNaN() { return this == NaN; } /** * Indicates if this real approximates the one specified. * This method takes into account possible errors (e.g. numeric * errors) to make this determination. * *

    Note: This method returns true if this or * that {@link #isNaN} (basically Not-A-Number * approximates anything).

    * * @param that the real to compare with. * @return this ≈ that */ public boolean approximates(Real that) { Real diff = this.minus(that); if (diff == NaN) return false; return diff._error.isLargerThan(diff._significand); } /** * Returns the closest integer value to this rational number. * * @return this real rounded to the nearest integer. * @throws ArithmeticException if this.isNaN() */ public LargeInteger round() { if (this == NaN) throw new ArithmeticException("Cannot convert NaN to integer value"); LargeInteger half = LargeInteger.FIVE.times10pow(-_exponent - 1); return isNegative() ? _significand.minus(half).times10pow(_exponent) : _significand.plus(half).times10pow(_exponent); } /** * Returns the negation of this real number. * * @return -this. */ public Real opposite() { if (this == NaN) return NaN; Real real = FACTORY.object(); real._significand = _significand.opposite(); real._exponent = _exponent; real._error = _error; return real; } /** * Returns the sum of this real number with the one specified. * * @param that the real to be added. * @return this + that. */ public Real plus(Real that) { if ((this == NaN) || (that == NaN)) return NaN; if (this._exponent > that._exponent) return that.plus(this); // Adds to the real with smallest exponent. int scale = that._exponent - this._exponent; // >= 0 Real real = FACTORY.object(); real._exponent = _exponent; real._significand = this._significand.plus(that._significand.times10pow(scale)); real._error = this._error.plus(that._error.times10pow(scale)); return real.normalize(); } /** * Returns the difference between this real number and the one * specified. * * @param that the real to be subtracted. * @return this - that. */ public Real minus(Real that) { return this.plus(that.opposite()); } /** * Returns the product of this real number with the specified * long multiplier. * * @param multiplier the long multiplier. * @return this · multiplier. */ public Real times(long multiplier) { if (this == NaN) return NaN; Real real = FACTORY.object(); real._exponent = this._exponent; real._significand = this._significand.times(multiplier); real._error = this._error.times(multiplier); return real.normalize(); } /** * Returns the product of this real number with the one specified. * * @param that the real multiplier. * @return this · that. */ public Real times(Real that) { if ((this == NaN) || (that == NaN)) return NaN; long exp = ((long) this._exponent) + that._exponent; if (exp > Integer.MAX_VALUE || (exp < Integer.MIN_VALUE)) return NaN; // Exponent overflow. LargeInteger thisMin = this._significand.minus(this._error); LargeInteger thisMax = this._significand.plus(this._error); LargeInteger thatMin = that._significand.minus(that._error); LargeInteger thatMax = that._significand.plus(that._error); LargeInteger min, max; if (thisMin.compareTo(thisMax.opposite()) > 0) { if (thatMin.compareTo(thatMax.opposite()) > 0) { min = thisMin.times(thatMin); max = thisMax.times(thatMax); } else { min = thisMax.times(thatMin); max = thisMin.times(thatMax); } } else { if (thatMin.compareTo(thatMax.opposite()) > 0) { min = thisMin.times(thatMax); max = thisMax.times(thatMin); } else { min = thisMax.times(thatMax); max = thisMin.times(thatMin); } } Real real = FACTORY.object(); real._exponent = (int) exp; real._significand = min.plus(max).shiftRight(1); real._error = max.minus(min); return real.normalize(); } /** * Returns this real number divided by the specified int * divisor. * * @param divisor the int divisor. * @return this / divisor */ public Real divide(long divisor) { return this.divide(Real.valueOf(divisor)); } /** * Returns this real number divided by the one specified. * * @param that the real divisor. * @return this / that. * @throws ArithmeticException if that.equals(ZERO) */ public Real divide(Real that) { return this.times(that.inverse()); } /** * Returns the reciprocal (or inverse) of this real number. * * @return 1 / this. */ public Real inverse() { if ((this == NaN) || (this == ZERO)) return NaN; if (this.isExact()) return this.toInexact().inverse(); LargeInteger thisMin = this._significand.minus(this._error); LargeInteger thisMax = this._significand.plus(this._error); if (thisMin.isNegative() && thisMax.isPositive()) // Encompasses 0 return NaN; int digits = MathLib.max(thisMin.digitLength(), thisMax.digitLength()); long exp = ((long) -this._exponent) - digits - digits; if ((exp > Integer.MAX_VALUE || (exp < Integer.MIN_VALUE))) return NaN; // Exponent overflow. LargeInteger min = div(2 * digits, thisMax); LargeInteger max = div(2 * digits, thisMin); Real real = FACTORY.object(); real._exponent = (int) exp; real._significand = min.plus(max).shiftRight(1); real._error = max.minus(min).plus(LargeInteger.ONE); return real.normalize(); } private static LargeInteger div(int exp, LargeInteger significand) { int expBitLength = (int) (exp * DIGITS_TO_BITS); int precision = expBitLength - significand.bitLength() + 1; LargeInteger reciprocal = significand.inverseScaled(precision); LargeInteger result = reciprocal.times10pow(exp); return result.shiftRight(expBitLength + 1); } private static final double DIGITS_TO_BITS = MathLib.LOG10 / MathLib.LOG2; private Real toInexact() { int digits = _significand.digitLength(); int scale = Real.getExactPrecision() - digits + 1; Real z = FACTORY.object(); z._significand = _significand.times10pow(scale); z._error = LargeInteger.ONE; z._exponent = _exponent - scale; return z; } /** * Returns the absolute value of this real number. * * @return |this|. */ public Real abs() { return _significand.isNegative() ? this.opposite() : this; } /** * Compares the absolute value of two real numbers. * * @param that the real number to be compared with. * @return |this| > |that| */ public boolean isLargerThan(Real that) { return this.abs().compareTo(that.abs()) > 0; } /** * Returns the square root of this real number, the more accurate is this * real number, the more accurate the square root. * * @return the positive square root of this real number. */ public Real sqrt() { if (this == NaN) return NaN; if (this == ZERO) return ZERO; if (this.isExact()) return this.toInexact().sqrt(); LargeInteger thisMin = this._significand.minus(this._error); LargeInteger thisMax = this._significand.plus(this._error); if (thisMin.isNegative()) return NaN; int exponent = _exponent >> 1; if ((_exponent & 1) == 1) { // Odd exponent. thisMin = thisMin.times10pow(1); thisMax = thisMax.times10pow(1); } LargeInteger minSqrt = thisMin.sqrt(); LargeInteger maxSqrt = thisMax.sqrt().plus(LargeInteger.ONE); LargeInteger sqrt = minSqrt.plus(maxSqrt).shiftRight(1); Real z = FACTORY.object(); z._significand = sqrt; z._error = maxSqrt.minus(sqrt); z._exponent = exponent; return z.normalize(); } /** * Returns the decimal text representation of this number. * * @return the text representation of this number. */ public Text toText() { if (this == NaN) return Text.valueOf("NaN"); if (isExact()) { return (_exponent == 0) ? _significand.toText() : _significand.toText().plus("E").plus(Text.valueOf(_exponent)); } int errorDigits = _error.digitLength(); LargeInteger m = (_significand.isPositive()) ? _significand.plus(FIVE .times10pow(errorDigits - 1)) : _significand.plus(MINUS_FIVE .times10pow(errorDigits - 1)); m = m.times10pow(-errorDigits); int exp = _exponent + errorDigits; Text txt = m.toText(); int digits = (m.isNegative()) ? txt.length() - 1 : txt.length(); if (digits > 1) { if ((exp < 0) && (-exp < digits)) { txt = txt.insert(txt.length() + exp, Text.valueOf('.')); } else { // Scientific notation. txt = txt.insert(txt.length() - digits + 1, Text.valueOf('.')); txt = txt.concat(Text.valueOf('E')).concat( Text.valueOf(exp + digits - 1)); } } else { txt = txt.concat(Text.valueOf('E')).concat(Text.valueOf(exp)); } return txt; } /** * Compares this real number against the specified object. * *

    Note: This method returns true if this or * that {@link #isNaN is Not-A-Number}, even though * Double.NaN == Double.NaN has the value * false.

    * * @param that the object to compare with. * @return true if the objects are two reals with same * significand, error and exponent;false otherwise. */ public boolean equals(Object that) { if (this == that) return true; if (!(that instanceof Real)) return false; Real thatReal = (Real) that; return this._significand.equals(thatReal._significand) && this._error.equals(thatReal._error) && (this._exponent == thatReal._exponent); } /** * Returns the hash code for this real number. * * @return the hash code value. */ public int hashCode() { return _significand.hashCode() + _error.hashCode() + _exponent * 31; } /** * Returns the value of this real number as a long. * * @return the numeric value represented by this real after conversion * to type long. */ public long longValue() { return (long) doubleValue(); } /** * Returns the value of this real number as a double. * * @return the numeric value represented by this real after conversion * to type double. */ public double doubleValue() { if (this == NaN) return Double.NaN; if (this == ZERO) return 0.0; // Shift the significand to a >18 digits integer (long compatible). int nbrDigits = _significand.digitLength(); int digitShift = nbrDigits - 18; long reducedSignificand = _significand.times10pow(-digitShift).longValue(); int exponent = _exponent + digitShift; return MathLib.toDoublePow10(reducedSignificand, exponent); } /** * Compares two real numbers numerically. * * @param that the real to compare with. * @return -1, 0 or 1 as this real is numerically less than, equal to, * or greater than that. * @throws ClassCastException that is not a {@link Real}. */ public int compareTo(Real that) { Real diff = this.minus(that); if (diff.isPositive()) { return 1; } else if (diff.isNegative()) { return -1; } else { return 0; } } /** * Normalizes this real (maintains error less than 31 bits). * * @return the normalized real. */ private Real normalize() { int digitError = this._error.digitLength(); int scale = 9 - digitError; if (scale >= 0) return this; // Small error. Real z = FACTORY.object(); z._significand = _significand.times10pow(scale); z._error = _error.times10pow(scale).plus(LargeInteger.ONE); z._exponent = _exponent - scale; return z; } @Override public Real copy() { if (this == NaN) return NaN; // Maintains unicity. return Real.valueOf(_significand.copy(), getError(), _exponent); } /** * Holds the factory constructing real instances. */ private static final ObjectFactory FACTORY = new ObjectFactory() { protected Real create() { return new Real(); } }; private static final LargeInteger FIVE; private static final LargeInteger MINUS_FIVE; static { // Immortal memory allocation. HeapContext.enter(); try { ZERO = Real.valueOf(0); ONE = Real.valueOf(1); FIVE = LargeInteger.valueOf(5); MINUS_FIVE = LargeInteger.valueOf(-5); } finally { HeapContext.exit(); } } private static final long serialVersionUID = 1L; }




    © 2015 - 2024 Weber Informatics LLC | Privacy Policy