/*
* JScience - Java(TM) Tools and Libraries for the Advancement of Sciences.
* Copyright (C) 2007 - 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.context.LocalContext;
import javolution.context.ObjectFactory;
import javolution.lang.MathLib;
import javolution.text.Text;
import javolution.text.TextBuilder;
import javolution.xml.XMLFormat;
import javolution.xml.stream.XMLStreamException;
/**
* This class represents a floating point number of arbitrary precision.
* A floating point number consists of a {@link #getSignificand significand}
* and a decimal {@link #getExponent exponent}:
* (significand · 10exponent).
*
* Unlike {@link Real} numbers, no calculation error is performed on
* floating point instances but the number of digits used during
* calculations can be specified (see {@link #setDigits(int)}). The
* largest the number of digits, the smallest the numeric error.
* For example:[code]
* FloatingPoint two = FloatingPoint.valueOf(2);
* FloatingPoint.setDigits(30); // 30 digits calculations.
* System.out.println(two.sqrt());
* > 0.141421356237309504880168872420E1
* [/code]
*
* Instances of this class are immutable and can be used to find
* accurate solutions to linear equations with the {@link
* org.jscience.mathematics.vector.Matrix Matrix} class.
*
* @author Jean-Marie Dautelle
* @version 4.1, June 8, 2007
* @see
* Wikipedia: Floating point
*/
public final class FloatingPoint extends Number implements
Field {
/**
* Holds the default XML representation for floating point numbers.
* This representation consists of a simple value attribute
* holding the {@link #toText() textual} representation.
*/
static final XMLFormat XML = new XMLFormat(
FloatingPoint.class) {
@Override
public FloatingPoint newInstance(Class cls,
InputElement xml) throws XMLStreamException {
return FloatingPoint.valueOf(xml.getAttribute("value"));
}
public void write(FloatingPoint FloatingPoint, OutputElement xml)
throws XMLStreamException {
xml.setAttribute("value", FloatingPoint.toText());
}
public void read(InputElement xml, FloatingPoint FloatingPoint) {
// Nothing to do, immutable.
}
};
/**
* Holds the factory constructing floating point instances.
*/
private static final ObjectFactory FACTORY = new ObjectFactory() {
protected FloatingPoint create() {
return new FloatingPoint();
}
};
/**
* The floating point instance representing the additive identity.
*/
public static final FloatingPoint ZERO = new FloatingPoint(
LargeInteger.ZERO, 0);
/**
* The floating point instance representing the multiplicative identity.
*/
public static final FloatingPoint ONE = new FloatingPoint(LargeInteger.ONE,
0);
/**
* The Not-a-Number instance (unique).
*/
public static final FloatingPoint NaN = new FloatingPoint(
LargeInteger.ZERO, Integer.MAX_VALUE);
/**
* Holds the number of digits to be used (default 20 digits).
*/
private static final LocalContext.Reference DIGITS = new LocalContext.Reference(
20);
/**
* Holds the significand value.
*/
private LargeInteger _significand;
/**
* Holds the power of 10 exponent.
*/
private int _exponent;
/**
* Default constructor.
*/
private FloatingPoint() {
}
/**
* Creates a floating point number for the specified significand and
* exponent.
*
* @param significand the significand.
* @param exponent the power of two exponent.
*/
private FloatingPoint(LargeInteger significand, int exponent) {
_significand = significand;
_exponent = exponent;
}
/**
* Returns the floating point number for the specified {@link
* LargeInteger} significand and power of two exponent.
*
* @param significand the significand value.
* @param exponent the power of two exponent.
* @return (significand · 2exponent
*/
public static FloatingPoint valueOf(LargeInteger significand, int exponent) {
FloatingPoint fp = FACTORY.object();
fp._significand = significand;
fp._exponent = exponent;
return fp;
}
/**
* Returns the floating point number for the specified long
* significand and power of two exponent (convenience method).
*
* @param significand the significand value.
* @param exponent the power of two exponent.
* @return (significand · 2exponent
*/
public static FloatingPoint valueOf(long significand, int exponent) {
FloatingPoint fp = FACTORY.object();
fp._significand = LargeInteger.valueOf(significand);
fp._exponent = exponent;
return fp;
}
/**
* Returns the floating point number for the specified long
* value (convenience method).
*
* @param longValue the long value.
* @return FloatingPoint.valueOf(longValue, 0)
*/
public static FloatingPoint valueOf(long longValue) {
return FloatingPoint.valueOf(longValue, 0);
}
/**
* Returns the floating point number for the specified double
* value (convenience method).
*
* @param doubleValue the double value.
* @return FloatingPoint.valueOf(longValue, 0)
*/
public static FloatingPoint valueOf(double doubleValue) {
if (doubleValue == 0.0)
return FloatingPoint.ZERO;
if (doubleValue == 1.0)
return FloatingPoint.ONE;
if (Double.isNaN(doubleValue) || Double.isInfinite(doubleValue))
return FloatingPoint.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);
return FloatingPoint.valueOf(significand, e);
}
/**
* Returns the floating point number for the specified character sequence.
* The number of digits
*
* @param chars the character sequence.
* @return the corresponding FloatingPoint number.
*/
public static FloatingPoint valueOf(CharSequence chars) {
// Use same format as Real.
Real real = Real.valueOf(chars);
if (real.getError() != 0)
throw new IllegalArgumentException("No error allowed");
return FloatingPoint.valueOf(real.getSignificand(), real.getExponent());
}
/**
* Returns the {@link javolution.context.LocalContext local} number of
* digits used during calculations (default 20 digits).
*
* @return the number of digits.
*/
public static int getDigits() {
return DIGITS.get();
}
/**
* Sets the {@link javolution.context.LocalContext local} number of digits
* to be used during calculations.
*
* @param digits the number of digits.
* @throws IllegalArgumentException if digits <= 0
*/
public static void setDigits(int digits) {
if (digits <= 0)
throw new IllegalArgumentException("digits: " + digits
+ " has to be greater than 0");
DIGITS.set(digits);
}
/**
* Returns the significand value.
*
* @return this floating point significand.
*/
public LargeInteger getSignificand() {
return _significand;
}
/**
* Returns the decimal exponent.
*
* @return this floating point decimal exponent.
*/
public int getExponent() {
return _exponent;
}
/**
* Returns the closest integer to this floating point number.
*
* @return this floating point rounded to the nearest integer.
*/
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 opposite of this floating point number.
*
* @return -this.
*/
public FloatingPoint opposite() {
return FloatingPoint.valueOf(_significand.opposite(), _exponent);
}
/**
* Returns the sum of this floating point number with the one specified.
*
* @param that the floating point number to be added.
* @return this + that.
*/
public FloatingPoint plus(FloatingPoint that) {
if (this._exponent > that._exponent)
return that.plus(this);
int pow10Scaling = that._exponent - this._exponent;
LargeInteger thatScaled = that._significand.times10pow(pow10Scaling);
return FloatingPoint.valueOf(_significand.plus(thatScaled), _exponent)
.normalize();
}
/**
* Returns the difference between this FloatingPoint number and the one
* specified.
*
* @param that the floating point number to be subtracted.
* @return this - that.
*/
public FloatingPoint minus(FloatingPoint that) {
if (this._exponent > that._exponent)
return that.opposite().plus(this);
int pow10Scaling = that._exponent - this._exponent;
LargeInteger thatScaled = that._significand.times10pow(pow10Scaling);
return FloatingPoint.valueOf(_significand.minus(thatScaled), _exponent)
.normalize();
}
/**
* Returns the product of this floating point number with the specified
* long multiplier.
*
* @param multiplier the long multiplier.
* @return this · multiplier.
*/
public FloatingPoint times(long multiplier) {
return this.times(FloatingPoint.valueOf(multiplier));
}
/**
* Returns the product of this floating point number with the one specified.
*
* @param that the floating point number multiplier.
* @return this · that.
*/
public FloatingPoint times(FloatingPoint that) {
return FloatingPoint.valueOf(
this._significand.times(that._significand),
this._exponent + that._exponent).normalize();
}
/**
* Returns the inverse of this floating point number.
*
* @return 1 / this.
* @throws ArithmeticException if dividend.isZero()
*/
public FloatingPoint inverse() {
if (_significand.isZero())
return NaN;
int pow10 = DIGITS.get() + _significand.digitLength();
LargeInteger dividend = LargeInteger.ONE.times10pow(pow10);
return FloatingPoint.valueOf(dividend.divide(_significand),
-pow10 - _exponent).normalize();
}
/**
* Returns this floating point number divided by the one specified.
*
* @param that the FloatingPoint number divisor.
* @return this / that.
* @throws ArithmeticException if that.equals(ZERO)
*/
public FloatingPoint divide(FloatingPoint that) {
if (that._significand.isZero())
return NaN;
int pow10 = DIGITS.get() + that._significand.digitLength();
LargeInteger dividend = _significand.times10pow(pow10);
return FloatingPoint.valueOf(dividend.divide(that._significand),
this._exponent - pow10 - that._exponent).normalize();
}
/**
* Returns the absolute value of this floating point number.
*
* @return |this|.
*/
public FloatingPoint abs() {
return FloatingPoint.valueOf(_significand.abs(), _exponent);
}
/**
* Returns the square root of this floating point number.
*
* @return the positive square root of this floating point number.
*/
public FloatingPoint sqrt() {
if (this == NaN)
return NaN;
if (isZero()) return ZERO;
int pow10 = DIGITS.get() * 2 - _significand.digitLength();
int exp = _exponent - pow10;
if ((exp & 1) == 1) { // Ensures that exp is even.
pow10++;
exp--;
}
LargeInteger scaledValue = _significand.times10pow(pow10);
return FloatingPoint.valueOf(scaledValue.sqrt(), exp >> 1)
.normalize();
}
/**
* Indicates if this floating point number is equal to zero.
*
* @return this == 0
*/
public boolean isZero() {
return _significand.isZero() && (this != NaN);
}
/**
* Indicates if this floating point number is greater than zero.
*
* @return this > 0
*/
public boolean isPositive() {
return _significand.isPositive();
}
/**
* Indicates if this rational number is less than zero.
*
* @return this < 0
*/
public boolean isNegative() {
return _significand.isNegative();
}
/**
* Indicates if this floating point is Not-a-Number.
*
* @return true if this number has unbounded value;
* false otherwise.
*/
public boolean isNaN() {
return this == NaN;
}
/**
* Compares the absolute value of two FloatingPoint numbers.
*
* @param that the FloatingPoint number to be compared with.
* @return |this| > |that|
*/
public boolean isLargerThan(FloatingPoint that) {
return this.abs().compareTo(that.abs()) > 0;
}
/**
* 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 (this._significand.isZero())
return Text.valueOf("0.0");
TextBuilder tb = TextBuilder.newInstance();
LargeInteger m = _significand;
if (isNegative()) {
tb.append('-');
m = m.opposite();
}
tb.append("0.");
LargeInteger.DECIMAL_FORMAT.format(m, tb);
int exp = _exponent + m.digitLength();
if (exp != 0) {
tb.append("E");
tb.append(_exponent + m.digitLength());
}
Text txt = tb.toText();
TextBuilder.recycle(tb);
return txt;
}
/**
* Compares this floating point number against the specified object.
*
* @param that the object to compare with.
* @return true if the objects are the same;
* false otherwise.
*/
public boolean equals(Object that) {
if (that instanceof FloatingPoint) {
return this.minus((FloatingPoint) that).isZero();
} else {
return false;
}
}
/**
* Returns the hash code for this floating point number.
*
* @return the hash code value.
*/
public int hashCode() {
if (isZero()) return 0;
if (isNaN()) return 483929293; // some random number
// This is a random prime - the same as in LargeInteger.hashCode()
// We return _significand.mod(p).times(10.pow(-exp) mod p)
final long p = 1327144033;
long code = _significand.hashCode();
int exp = _exponent;
long mult;
if (0 > exp) {
mult = 398143210; // modInverse of 10 mod p
exp = -exp;
} else {
mult = 10;
}
while (0 != exp) {
if (1 == exp % 2) {
code = (code * mult) % p;
}
mult = (mult * mult) % p;
exp = exp / 2;
}
return (int) code;
}
/**
* Returns the value of this floating point number as a long.
*
* @return the numeric value represented by this floating point
* after conversion to type long.
*/
public long longValue() {
Real real = Real.valueOf(_significand, 0, _exponent);
return real.longValue();
}
/**
* Returns the value of this floating point number as a double.
*
* @return the numeric value represented by this FloatingPoint after conversion
* to type double.
*/
public double doubleValue() {
Real real = Real.valueOf(_significand, 0, _exponent);
return real.doubleValue();
}
/**
* Compares two floating point number numerically.
*
* @param that the floating point number to compare with.
* @return -1, 0 or 1 as this FloatingPoint number is numerically less than,
* equal to, or greater than that.
*/
public int compareTo(FloatingPoint that) {
FloatingPoint diff = this.minus(that);
if (diff.isPositive()) {
return 1;
} else if (diff.isNegative()) {
return -1;
} else {
return 0;
}
}
/**
* Returns this floating point number after normalization based upon
* the number of digits.
*
* @return this
*/
private FloatingPoint normalize() {
int digits = FloatingPoint.getDigits();
int thisDigits = this._significand.digitLength();
if (thisDigits > digits) { // Scale down.
int pow10 = digits - thisDigits; // Negative.
_significand = _significand.times10pow(pow10);
long exponent = ((long) _exponent) - pow10;
if (exponent > Integer.MAX_VALUE)
return NaN;
if (exponent < Integer.MIN_VALUE)
return ZERO;
_exponent = (int) exponent;
}
return this;
}
@Override
public FloatingPoint copy() {
if (this == NaN)
return NaN; // Maintains unicity.
FloatingPoint r = FACTORY.object();
r._significand = _significand.copy();
r._exponent = _exponent;
return r;
}
private static final long serialVersionUID = 1L;
}
Ads help maintain this website.