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

org.apfloat.internal.FloatApfloatImpl Maven / Gradle / Ivy

/*
 * Apfloat arbitrary precision arithmetic library
 * Copyright (C) 2002-2017  Mikko Tommila
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library 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.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
 */
package org.apfloat.internal;

import java.io.ObjectInputStream;
import java.io.PushbackReader;
import java.io.Writer;
import java.io.StringWriter;
import java.io.IOException;

import org.apfloat.Apfloat;
import org.apfloat.ApfloatContext;
import org.apfloat.ApfloatRuntimeException;
import org.apfloat.InfiniteExpansionException;
import org.apfloat.OverflowException;
import org.apfloat.spi.ApfloatImpl;
import org.apfloat.spi.DataStorageBuilder;
import org.apfloat.spi.DataStorage;
import org.apfloat.spi.ArrayAccess;
import org.apfloat.spi.AdditionBuilder;
import org.apfloat.spi.AdditionStrategy;
import org.apfloat.spi.ConvolutionBuilder;
import org.apfloat.spi.ConvolutionStrategy;
import org.apfloat.spi.Util;
import static org.apfloat.spi.RadixConstants.*;
import static org.apfloat.internal.FloatRadixConstants.*;

/**
 * Immutable apfloat implementation class for the
 * float data element type.

* * The associated {@link DataStorage} is assumed to be immutable also. * This way performance can be improved by sharing the data storage between * different ApfloatImpl's and by only varying the * ApfloatImpl specific fields, like sign, precision and exponent.

* * This implementation doesn't necessarily store any extra digits for added * precision, so the last digit of any operation may be inaccurate. * * @version 1.8.2 * @author Mikko Tommila */ public class FloatApfloatImpl extends FloatBaseMath implements ApfloatImpl { // Implementation notes: // - The dataStorage must never contain leading zeros or trailing zeros // - If precision is reduced then the dataStorage can contain trailing zeros (physically in the middle) // - The dataStorage should not be unnecessarily subsequenced if precision is reduced e.g. to allow autoconvolution // - Precision is in digits but exponent is in base units private FloatApfloatImpl(int sign, long precision, long exponent, DataStorage dataStorage, int radix) { super(radix); assert (sign == 0 || sign == -1 || sign == 1); assert (precision > 0); assert (sign != 0 || precision == Apfloat.INFINITE); assert (sign != 0 || exponent == 0); assert (sign != 0 || dataStorage == null); assert (sign == 0 || dataStorage != null); assert (exponent <= MAX_EXPONENT[radix] && exponent >= -MAX_EXPONENT[radix]); assert (dataStorage == null || dataStorage.isReadOnly()); this.sign = sign; this.precision = precision; this.exponent = exponent; this.dataStorage = dataStorage; this.radix = radix; } /** * Create a new FloatApfloatImpl instance from a String. * * @param value The string to be parsed to a number. * @param precision The precision of the number (in digits of the radix). * @param radix The radix in which the number is created. * @param isInteger Specifies if the number to be parsed from the string is to be treated as an integer or not. * * @exception java.lang.NumberFormatException If the number is not valid. */ public FloatApfloatImpl(String value, long precision, int radix, boolean isInteger) throws NumberFormatException, ApfloatRuntimeException { super(checkRadix(radix)); assert (precision == Apfloat.DEFAULT || precision > 0); this.radix = radix; // Default sign if not specified this.sign = 1; int startIndex = -1, pointIndex = -1, expIndex = -1, leadingZeros = 0, trailingZeros = 0, digitSize = 0; // Scan through the string looking for various things for (int i = 0; i < value.length(); i++) { char c = value.charAt(i); int digit = Character.digit(c, radix); // Note that checking for a valid digit takes place before checking for e or E in the string if (digit == -1) { if (i == 0 && (c == '-' || c == '+')) { // Get sign this.sign = (c == '-' ? -1 : 1); } else if (!isInteger && c == '.' && pointIndex == -1) { // Mark decimal point location pointIndex = digitSize; } else if (!isInteger && (c == 'e' || c == 'E') && expIndex == -1) { // Mark index after which the exponent is specified expIndex = i; break; } else { throw new NumberFormatException("Invalid character: " + c + " at position " + i); } } else { if (leadingZeros == digitSize && digit == 0) { // Increase number of leading zeros leadingZeros++; } else if (startIndex == -1) { // Mark index where the significant digits start startIndex = i; } // Increase number of digits digitSize++; if (digit == 0) { // Increase number of trailing zeros trailingZeros++; } else { // Reset number of trailing zeros trailingZeros = 0; } } } // Check if no digits were specified if (digitSize == 0) { throw new NumberFormatException("No digits"); } // Check if this number is zero if (startIndex == -1) { this.sign = 0; this.precision = Apfloat.INFINITE; this.exponent = 0; this.dataStorage = null; return; } // Default precision is number of significant digits, if not specified if (precision == Apfloat.DEFAULT) { assert (!isInteger); precision = digitSize - leadingZeros; } this.precision = precision; // Size of integer part int integerSize = (pointIndex >= 0 ? pointIndex : digitSize) - leadingZeros; // Read exponent as specified in string if (expIndex >= 0) { // Thanks to Charles Oliver Nutter for finding this bug String expString = value.substring(expIndex + 1); if (expString.startsWith("+")) { expString = expString.substring(1); } try { this.exponent = Long.parseLong(expString); } catch (NumberFormatException nfe) { throw new NumberFormatException("Invalid exponent: " + expString); } } else { this.exponent = 0; } // Do not allow the exponent to be too close to the limits (MIN_VALUE, MAX_VALUE), leave some slack int slack = BASE_DIGITS[radix]; // Check for overflow in exponent, roughly if (integerSize >= -slack && this.exponent >= Long.MAX_VALUE - integerSize - slack) { throw new NumberFormatException("Exponent overflow"); } else if (integerSize <= slack && this.exponent <= Long.MIN_VALUE - integerSize + slack) { // Underflow this.sign = 0; this.precision = Apfloat.INFINITE; this.exponent = 0; this.dataStorage = null; return; } // Adjust exponent by decimal point location this.exponent += integerSize; // Exponent rounded towards positive infinity to base unit long baseExp = (this.exponent + (this.exponent > 0 ? BASE_DIGITS[radix] - 1 : 0)) / BASE_DIGITS[radix]; // Check for overflow in exponent as represented in base units if (baseExp > MAX_EXPONENT[this.radix]) { throw new OverflowException("Overflow"); } else if (baseExp < -MAX_EXPONENT[this.radix]) { // Underflow this.sign = 0; this.precision = Apfloat.INFINITE; this.exponent = 0; this.dataStorage = null; return; } // Leading zeros in first base unit int digitsInBase = (int) (baseExp * BASE_DIGITS[radix] - this.exponent); // The stored exponent is really the one per base unit this.exponent = baseExp; // Remove leading and trailing zeros from size digitSize -= leadingZeros + trailingZeros; // Limit number of significant digits by specified precision digitSize = (int) Math.min(digitSize, precision); // Needed storage size in floats int size = (int) getBasePrecision(digitSize, BASE_DIGITS[radix] - digitsInBase); this.dataStorage = createDataStorage(size); this.dataStorage.setSize(size); // Base unit that is constructed and stored to an element of the data storage float word = 0; DataStorage.Iterator iterator = this.dataStorage.iterator(DataStorage.WRITE, 0, size); // Set the data for (int i = startIndex; digitSize > 0; i++) { char c = value.charAt(i); if (c == '.') { continue; } int digit = Character.digit(c, radix); word *= (float) radix; word += (float) digit; if (digitSize == 1) { // Last digit while (digitsInBase < BASE_DIGITS[radix] - 1) { // Fill last word with trailing zeros word *= (float) radix; digitsInBase++; } } if (++digitsInBase == BASE_DIGITS[radix]) { // Word is full, write word digitsInBase = 0; iterator.setFloat(word); iterator.next(); word = 0; } digitSize--; } assert (!iterator.hasNext()); this.dataStorage.setReadOnly(); } /** * Create a new FloatApfloatImpl instance from a long. * * @param value The value of the number. * @param precision The precision of the number (in digits of the radix). * @param radix The radix in which the number is created. * * @exception java.lang.NumberFormatException If the number is not valid. */ public FloatApfloatImpl(long value, long precision, int radix) throws NumberFormatException, ApfloatRuntimeException { super(checkRadix(radix)); assert (precision > 0); this.radix = radix; // Faster to set now than calculate later this.isOne = (value == 1 ? 1 : 0); if (value > 0) { this.sign = 1; value = -value; // Calculate here as negative to handle 0x8000000000000000 } else if (value < 0) { this.sign = -1; } else { this.sign = 0; this.precision = Apfloat.INFINITE; this.exponent = 0; this.dataStorage = null; return; } this.precision = precision; int size; float[] data = new float[MAX_LONG_SIZE]; long longBase = (long) BASE[radix]; if (-longBase < value) { size = 1; // Nonzero data[MAX_LONG_SIZE - 1] = (float) -value; } else { for (size = 0; value != 0; size++) { long newValue = value / longBase; data[MAX_LONG_SIZE - 1 - size] = (float) (newValue * longBase - value); // Negated here value = newValue; } } this.exponent = size; this.initialDigits = getDigits(data[MAX_LONG_SIZE - size]); // Check if precision in floats is less than size; truncate size if so long basePrecision = getBasePrecision(precision, this.initialDigits); if (basePrecision < size) { size = (int) basePrecision; } // Remove trailing zeros from data while (data[MAX_LONG_SIZE - 1 - (int) this.exponent + size] == 0) { size--; } this.dataStorage = createDataStorage(size); this.dataStorage.setSize(size); ArrayAccess arrayAccess = this.dataStorage.getArray(DataStorage.WRITE, 0, size); System.arraycopy(data, MAX_LONG_SIZE - (int) this.exponent, arrayAccess.getData(), arrayAccess.getOffset(), size); arrayAccess.close(); this.dataStorage.setReadOnly(); } /** * Create a new FloatApfloatImpl instance from a double. * * @param value The value of the number. * @param precision The precision of the number (in digits of the radix). * @param radix The radix in which the number is created. * * @exception java.lang.NumberFormatException If the number is not valid. */ public FloatApfloatImpl(double value, long precision, int radix) throws NumberFormatException, ApfloatRuntimeException { super(checkRadix(radix)); if (Double.isInfinite(value) || Double.isNaN(value)) { throw new NumberFormatException(value + " is not a valid number"); } this.radix = radix; if (value > 0) { this.sign = 1; } else if (value < 0) { this.sign = -1; value = -value; } else { this.sign = 0; this.precision = Apfloat.INFINITE; this.exponent = 0; this.dataStorage = null; return; } this.precision = precision; int size; float[] data = new float[MAX_DOUBLE_SIZE]; double doubleBase = (double) BASE[radix]; this.exponent = (long) Math.floor(Math.log(value) / Math.log(doubleBase)); // Avoid overflow in intermediate value if (this.exponent > 0) { value *= Math.pow(doubleBase, (double) -this.exponent); } else if (this.exponent < 0) { value *= Math.pow(doubleBase, (double) (-this.exponent - MAX_DOUBLE_SIZE)); value *= Math.pow(doubleBase, (double) MAX_DOUBLE_SIZE); } this.exponent++; if (value < 1.0) { // Round-off error in case the input was very close but just under the base, e.g. 9.999999999999996E-10 value = 1.0; } for (size = 0; size < MAX_DOUBLE_SIZE && value > 0.0; size++) { double tmp = Math.floor(value); assert (tmp <= doubleBase); if (tmp == doubleBase) { // Round-off error e.g. in case of the number being exactly 1/radix tmp -= 1.0; } data[size] = (float) tmp; value -= tmp; value *= doubleBase; } this.initialDigits = getDigits(data[0]); // Check if precision in floats is less than size; truncate size if so long basePrecision = getBasePrecision(precision, this.initialDigits); if (basePrecision < size) { size = (int) basePrecision; } // Remove trailing zeros from data while (data[size - 1] == 0) { size--; } this.dataStorage = createDataStorage(size); this.dataStorage.setSize(size); ArrayAccess arrayAccess = this.dataStorage.getArray(DataStorage.WRITE, 0, size); System.arraycopy(data, 0, arrayAccess.getData(), arrayAccess.getOffset(), size); arrayAccess.close(); this.dataStorage.setReadOnly(); } private static long readExponent(PushbackReader in) throws IOException, NumberFormatException { StringBuilder buffer = new StringBuilder(20); int input; for (long i = 0; (input = in.read()) != -1; i++) { char c = (char) input; int digit = Character.digit(c, 10); // Exponent is always in base 10 if (i == 0 && c == '-' || digit != -1) { buffer.append(c); } else { // Stop at first invalid character and put it back in.unread(input); break; } } return Long.parseLong(buffer.toString()); } /** * Create a new FloatApfloatImpl instance reading from a stream.

* * Implementation note: this constructor calls the in stream's * single-character read() method. If the underlying stream doesn't * explicitly implement this method in some efficient way, but simply inherits it * from the Reader base class, performance will suffer as the default * Reader method creates a new char[1] on every call to * read(). * * @param in The stream to read from. * @param precision The precision of the number (in digits of the radix). * @param radix The radix in which the number is created. * @param isInteger Specifies if the number to be parsed from the stream is to be treated as an integer or not. * * @exception java.io.IOException If an I/O error occurs accessing the stream. * @exception java.lang.NumberFormatException If the number is not valid. */ public FloatApfloatImpl(PushbackReader in, long precision, int radix, boolean isInteger) throws IOException, NumberFormatException, ApfloatRuntimeException { super(checkRadix(radix)); assert (precision == Apfloat.DEFAULT || precision > 0); this.radix = radix; // Default sign if not specified this.sign = 1; // Allocate a maximum memory block, since we don't know how much data to expect ApfloatContext ctx = ApfloatContext.getContext(); long initialSize = ctx.getMemoryThreshold() / 4, previousAllocatedSize = 0, allocatedSize = initialSize; this.dataStorage = createDataStorage(initialSize); this.dataStorage.setSize(initialSize); // Base unit that is constructed and stored to an element of the data storage float word = 0; // Number of digits stored in word int digitsInBase = 0; DataStorage.Iterator iterator = this.dataStorage.iterator(DataStorage.WRITE, previousAllocatedSize, allocatedSize); int input; long actualSize = 0, startIndex = -1, pointIndex = -1, leadingZeros = 0, trailingZeros = 0, digitSize = 0; // Scan through the string looking for various things for (long i = 0; (input = in.read()) != -1; i++) { char c = (char) input; int digit = Character.digit(c, radix); // Note that checking for a valid digit takes place before checking for e or E in the string if (digit == -1) { if (i == 0 && (c == '-' || c == '+')) { // Get sign this.sign = (c == '-' ? -1 : 1); } else if (!isInteger && c == '.' && pointIndex == -1) { // Mark decimal point location pointIndex = digitSize; } else if (!isInteger && digitSize > 0 && (c == 'e' || c == 'E')) { // Read the exponent and stop this.exponent = readExponent(in); break; } else { // Stop at first invalid character and put it back in.unread(input); break; } } else { if (leadingZeros == digitSize && digit == 0) { // Increase number of leading zeros leadingZeros++; } else { if (startIndex == -1) { // Mark index where the significant digits start startIndex = i; } // Set the data word *= (float) radix; word += (float) digit; // Reallocate storage if needed; done here to prepare storing last (partial) word if (actualSize == allocatedSize) { if (actualSize == initialSize) { // Maximum memory block size exceeded; prepare to allocate anything DataStorage dataStorage = createDataStorage(Long.MAX_VALUE / 4); dataStorage.copyFrom(this.dataStorage, actualSize); this.dataStorage = dataStorage; } previousAllocatedSize = allocatedSize; allocatedSize += getBlockSize(); this.dataStorage.setSize(allocatedSize); iterator.close(); iterator = this.dataStorage.iterator(DataStorage.WRITE, previousAllocatedSize, allocatedSize); } if (++digitsInBase == BASE_DIGITS[radix]) { // Word is full, write word digitsInBase = 0; iterator.setFloat(word); iterator.next(); word = 0; actualSize++; } } // Increase number of digits digitSize++; if (digit == 0) { // Increase number of trailing zeros trailingZeros++; } else { // Reset number of trailing zeros trailingZeros = 0; } } } // Check if no digits were specified if (digitSize == 0) { throw new NumberFormatException("No digits"); } // Check if this number is zero if (startIndex == -1) { this.sign = 0; this.precision = Apfloat.INFINITE; this.exponent = 0; this.dataStorage = null; return; } // Handle last word if (digitsInBase > 0 && word != 0) { // Last digit while (digitsInBase < BASE_DIGITS[radix]) { // Fill last word with trailing zeros word *= (float) radix; digitsInBase++; } // Write word iterator.setFloat(word); actualSize++; } iterator.close(); // Default precision is number of significant digits, if not specified if (precision == Apfloat.DEFAULT) { assert (!isInteger); precision = digitSize - leadingZeros; } this.precision = precision; // Size of integer part long integerSize = (pointIndex >= 0 ? pointIndex : digitSize) - leadingZeros; // Do not allow the exponent to be too close to the limits (MIN_VALUE, MAX_VALUE), leave some slack int slack = BASE_DIGITS[radix]; // Check for overflow in exponent, roughly if (integerSize >= -slack && this.exponent >= Long.MAX_VALUE - integerSize - slack) { throw new NumberFormatException("Exponent overflow"); } else if (integerSize <= slack && this.exponent <= Long.MIN_VALUE - integerSize + slack) { // Underflow this.sign = 0; this.precision = Apfloat.INFINITE; this.exponent = 0; this.dataStorage = null; return; } // Adjust exponent by decimal point location this.exponent += integerSize; // Exponent rounded towards negative infinity to base unit long baseExp = (this.exponent - (this.exponent < 0 ? BASE_DIGITS[radix] - 1 : 0)) / BASE_DIGITS[radix]; // Check for overflow in exponent as represented in base units if (baseExp > MAX_EXPONENT[this.radix]) { throw new OverflowException("Overflow"); } else if (baseExp < -MAX_EXPONENT[this.radix]) { // Underflow this.sign = 0; this.precision = Apfloat.INFINITE; this.exponent = 0; this.dataStorage = null; return; } // How much the data needs to be shifted int bias = (int) (this.exponent - baseExp * BASE_DIGITS[radix]); // The stored exponent is really the one per base unit this.exponent = baseExp; // Remove leading and trailing zeros from size digitSize -= leadingZeros + trailingZeros; // Limit number of significant digits by specified precision digitSize = Math.min(digitSize, precision); // Needed storage size in floats actualSize = (digitSize + BASE_DIGITS[radix] - 1) / BASE_DIGITS[radix]; // Truncate allocated space to actually used amount this.dataStorage.setSize(actualSize); this.dataStorage.setReadOnly(); if (bias != 0) { // Shift by bias long factor = 1; for (int i = 0; i < bias; i++) { factor *= radix; } FloatApfloatImpl tmp = (FloatApfloatImpl) multiply(new FloatApfloatImpl(factor, Apfloat.INFINITE, radix)); this.exponent = tmp.exponent; this.dataStorage = tmp.dataStorage; this.initialDigits = UNDEFINED; // Needs to be reset } } // Returns number of trailing zeros before specified index private static long getTrailingZeros(DataStorage dataStorage, long index) throws ApfloatRuntimeException { long count = 0; DataStorage.Iterator iterator = dataStorage.iterator(DataStorage.READ, index, 0); while (iterator.hasNext()) { if (iterator.getFloat() != 0) { iterator.close(); break; } iterator.next(); count++; } return count; } // Returns number of leading zeros starting from specified index private static long getLeadingZeros(DataStorage dataStorage, long index) throws ApfloatRuntimeException { long count = 0; DataStorage.Iterator iterator = dataStorage.iterator(DataStorage.READ, index, dataStorage.getSize()); while (iterator.hasNext()) { if (iterator.getFloat() != 0) { iterator.close(); break; } iterator.next(); count++; } return count; } public ApfloatImpl addOrSubtract(ApfloatImpl x, boolean subtract) throws ApfloatRuntimeException { if (!(x instanceof FloatApfloatImpl)) { throw new ImplementationMismatchException("Wrong operand type: " + x.getClass().getName()); } FloatApfloatImpl that = (FloatApfloatImpl) x; if (this.radix != that.radix) { throw new RadixMismatchException("Cannot use numbers with different radixes: " + this.radix + " and " + that.radix); } assert (this.sign != 0); assert (that.sign != 0); int realThatSign = (subtract ? -that.sign : that.sign); boolean reallySubtract = (this.sign != realThatSign); ApfloatContext ctx = ApfloatContext.getContext(); AdditionBuilder additionBuilder = ctx.getBuilderFactory().getAdditionBuilder(Float.TYPE); AdditionStrategy additionStrategy = additionBuilder.createAddition(this.radix); int sign; long exponent, precision; DataStorage dataStorage; if (this == that) { if (reallySubtract) { // x - x = 0 return zero(); } else { // x + x = 2 * x sign = this.sign; exponent = this.exponent; precision = this.precision; long size = getSize() + 1; dataStorage = createDataStorage(size); dataStorage.setSize(size); DataStorage.Iterator src1 = this.dataStorage.iterator(DataStorage.READ, size - 1, 0), src2 = this.dataStorage.iterator(DataStorage.READ, size - 1, 0), // Sub-optimal: could be the same dst = dataStorage.iterator(DataStorage.WRITE, size, 0); float carry = additionStrategy.add(src1, src2, (float) 0, dst, size - 1); dst.setFloat(carry); dst.close(); size -= getTrailingZeros(dataStorage, size); // Check if carry occurred int carrySize = (int) carry, leadingZeros = 1 - carrySize; dataStorage = dataStorage.subsequence(leadingZeros, size - leadingZeros); exponent += carrySize; if (this.exponent == MAX_EXPONENT[this.radix] && carrySize > 0) { throw new OverflowException("Overflow"); } if (precision != Apfloat.INFINITE && (carrySize > 0 || getInitialDigits(dataStorage) > getInitialDigits())) { // Carry overflow for most significant digit; number of significant digits increases by one precision++; } } } else { // Now this != that int comparison; if (scale() > that.scale()) { comparison = 1; } else if (scale() < that.scale()) { comparison = -1; } else if (reallySubtract) { comparison = compareMantissaTo(that); // Might be sub-optimal, but a more efficient algorithm would be complicated } else { comparison = 1; // Add equally big numbers; arbitrarily choose one } FloatApfloatImpl big, small; if (comparison > 0) { big = this; small = that; sign = this.sign; } else if (comparison < 0) { big = that; small = this; sign = realThatSign; } else { // x - x = 0 return zero(); } long scaleDifference = big.scale() - small.scale(), exponentDifference, size, bigSize, smallSize; if (scaleDifference < 0) { // Small number is completely insignificantly small compared to big precision = big.precision; exponent = big.exponent; bigSize = big.getSize(); smallSize = 0; size = bigSize; exponentDifference = bigSize; } else { precision = Math.min(big.precision, Util.ifFinite(small.precision, scaleDifference + small.precision)); // Detects overflow also long basePrecision = Math.min(MAX_EXPONENT[this.radix], getBasePrecision(precision, big.getInitialDigits())); exponent = big.exponent; exponentDifference = big.exponent - small.exponent; size = Math.min(basePrecision, Math.max(big.getSize(), exponentDifference + small.getSize())); bigSize = Math.min(size, big.getSize()); smallSize = Math.max(0, Math.min(size - exponentDifference, small.getSize())); } long dstSize = size + 1; // One extra word for carry overflow dataStorage = createDataStorage(dstSize); dataStorage.setSize(dstSize); DataStorage.Iterator src1 = big.dataStorage.iterator(DataStorage.READ, bigSize, 0), src2 = small.dataStorage.iterator(DataStorage.READ, smallSize, 0), dst = dataStorage.iterator(DataStorage.WRITE, dstSize, 0); float carry = 0; // big: XXXXXXXX XXXX // small: XXXXXXXX or XXXX // This part: XXXX XXXX if (size > bigSize) { long blockSize = Math.min(size - bigSize, smallSize); if (reallySubtract) { carry = additionStrategy.subtract(null, src2, carry, dst, blockSize); } else { carry = additionStrategy.add(null, src2, carry, dst, blockSize); } } // big: XXXXXXXXXXXX // small: XXXX // This part: XXXX else if (size > exponentDifference + smallSize) { long blockSize = size - exponentDifference - smallSize; if (reallySubtract) { carry = additionStrategy.subtract(src1, null, carry, dst, blockSize); } else { carry = additionStrategy.add(src1, null, carry, dst, blockSize); } } // big: XXXX // small: XXXX // This part: XXXX if (exponentDifference > bigSize) { long blockSize = exponentDifference - bigSize; if (reallySubtract) { carry = additionStrategy.subtract(null, null, carry, dst, blockSize); } else { carry = additionStrategy.add(null, null, carry, dst, blockSize); } } // big: XXXXXXXX XXXXXXXXXXXX // small: XXXXXXXX or XXXX // This part: XXXX XXXX else if (bigSize > exponentDifference) { long blockSize = Math.min(bigSize - exponentDifference, smallSize); if (reallySubtract) { carry = additionStrategy.subtract(src1, src2, carry, dst, blockSize); } else { carry = additionStrategy.add(src1, src2, carry, dst, blockSize); } } // big: XXXXXXXX XXXXXXXXXXXX XXXX // small: XXXXXXXX or XXXX or XXXX // This part: XXXX XXXX XXXX if (exponentDifference > 0) { long blockSize = Math.min(bigSize, exponentDifference); if (reallySubtract) { carry = additionStrategy.subtract(src1, null, carry, dst, blockSize); } else { carry = additionStrategy.add(src1, null, carry, dst, blockSize); } } // Set most significant word dst.setFloat(carry); dst.close(); long leadingZeros; if (reallySubtract) { // Get denormalization leadingZeros = getLeadingZeros(dataStorage, 0); assert (leadingZeros <= size); } else { // Check if carry occurred up to and including most significant word leadingZeros = (carry == 0 ? 1 : 0); if (this.exponent == MAX_EXPONENT[this.radix] && leadingZeros == 0) { throw new OverflowException("Overflow"); } } dstSize -= getTrailingZeros(dataStorage, dstSize); dataStorage = dataStorage.subsequence(leadingZeros, dstSize - leadingZeros); exponent += 1 - leadingZeros; if (exponent < -MAX_EXPONENT[this.radix]) { // Underflow return zero(); } if (precision != Apfloat.INFINITE) { // If scale of number changes, the number of significant digits changes accordingly long scaleChange = (1 - leadingZeros) * BASE_DIGITS[this.radix] + getInitialDigits(dataStorage) - big.getInitialDigits(); if (-scaleChange >= precision) { // All significant digits were lost anyway, due to trailing garbage digits return zero(); } precision += scaleChange; precision = (precision <= 0 ? Apfloat.INFINITE : precision); // Detect overflow } } dataStorage.setReadOnly(); return new FloatApfloatImpl(sign, precision, exponent, dataStorage, this.radix); } public ApfloatImpl multiply(ApfloatImpl x) throws ApfloatRuntimeException { if (!(x instanceof FloatApfloatImpl)) { throw new ImplementationMismatchException("Wrong operand type: " + x.getClass().getName()); } FloatApfloatImpl that = (FloatApfloatImpl) x; if (this.radix != that.radix) { throw new RadixMismatchException("Cannot multiply numbers with different radixes: " + this.radix + " and " + that.radix); } int sign = this.sign * that.sign; if (sign == 0) { return zero(); } long exponent = this.exponent + that.exponent; if (exponent > MAX_EXPONENT[this.radix]) { throw new OverflowException("Overflow"); } else if (exponent < -MAX_EXPONENT[this.radix]) { // Underflow return zero(); } long precision = Math.min(this.precision, that.precision), basePrecision = getBasePrecision(precision, 0), // Round up thisSize = getSize(), thatSize = that.getSize(), size = Math.min(Util.ifFinite(basePrecision, basePrecision + 1), thisSize + thatSize), // Reserve one extra word for carry thisDataSize = Math.min(thisSize, basePrecision), thatDataSize = Math.min(thatSize, basePrecision); DataStorage thisDataStorage = this.dataStorage.subsequence(0, thisDataSize), thatDataStorage = (this.dataStorage == that.dataStorage ? thisDataStorage : // Enable auto-convolution that.dataStorage.subsequence(0, thatDataSize)); ApfloatContext ctx = ApfloatContext.getContext(); ConvolutionBuilder convolutionBuilder = ctx.getBuilderFactory().getConvolutionBuilder(); ConvolutionStrategy convolutionStrategy = convolutionBuilder.createConvolution(this.radix, thisDataSize, thatDataSize, size); // Possibly sub-optimal: could look up trailing zeros of the subsequences DataStorage dataStorage = convolutionStrategy.convolute(thisDataStorage, thatDataStorage, size); // Check if carry occurred up to and including most significant word int leadingZeros = (getMostSignificantWord(dataStorage) == 0 ? 1 : 0); exponent -= leadingZeros; if (exponent < -MAX_EXPONENT[this.radix]) { // Underflow return zero(); } size -= leadingZeros; dataStorage = dataStorage.subsequence(leadingZeros, size); size = Math.min(size, getBasePrecision(precision, getInitialDigits(dataStorage))); size -= getTrailingZeros(dataStorage, size); dataStorage = dataStorage.subsequence(0, size); dataStorage.setReadOnly(); return new FloatApfloatImpl(sign, precision, exponent, dataStorage, this.radix); } public boolean isShort() throws ApfloatRuntimeException { return (this.sign == 0 || getSize() == 1); } public ApfloatImpl divideShort(ApfloatImpl x) throws ApfloatRuntimeException { if (!(x instanceof FloatApfloatImpl)) { throw new ImplementationMismatchException("Wrong operand type: " + x.getClass().getName()); } FloatApfloatImpl that = (FloatApfloatImpl) x; if (this.radix != that.radix) { throw new RadixMismatchException("Cannot divide numbers with different radixes: " + this.radix + " and " + that.radix); } assert (this.sign != 0); assert (that.sign != 0); int sign = this.sign * that.sign; long exponent = this.exponent - that.exponent + 1; if (exponent > MAX_EXPONENT[this.radix]) { throw new OverflowException("Overflow"); } else if (exponent < -MAX_EXPONENT[this.radix]) { // Underflow return zero(); } long precision = Math.min(this.precision, that.precision), basePrecision = getBasePrecision(), thisDataSize = Math.min(getSize(), basePrecision); DataStorage dataStorage; float divisor = getMostSignificantWord(that.dataStorage); if (divisor == (float) 1) { long size = thisDataSize - getTrailingZeros(this.dataStorage, thisDataSize); dataStorage = this.dataStorage.subsequence(0, size); } else { ApfloatContext ctx = ApfloatContext.getContext(); AdditionBuilder additionBuilder = ctx.getBuilderFactory().getAdditionBuilder(Float.TYPE); AdditionStrategy additionStrategy = additionBuilder.createAddition(this.radix); long size; float carry; // Check for finite or infinite result sequence float dividend = divisor; // Check that the factorization of the divisor consists entirely of factors of the base // E.g. if base is 10=2*5 then the divisor should be 2^n*5^m for (int i = 0; i < RADIX_FACTORS[this.radix].length; i++) { float factor = RADIX_FACTORS[this.radix][i], quotient; // Keep dividing by factor as long as dividend % factor == 0 // that is remove factors of the base from the divisor while ((dividend - factor * (quotient = (float) (long) (dividend / factor))) == 0) { dividend = quotient; } } // Check if the divisor was factored all the way to one by just dividing by factors of the base if (dividend != (float) 1) { // Divisor does not contain only factors of the base; infinite nonzero sequence if (basePrecision == Apfloat.INFINITE) { throw new InfiniteExpansionException("Cannot perform inexact division to infinite precision"); } size = basePrecision; } else { // Divisor contains only factors of the base; calculate maximum sequence length carry = (float) 1; DataStorage.Iterator dummy = new DataStorage.Iterator() { @Override public void setFloat(float value) {} @Override public void next() {} private static final long serialVersionUID = 1L; }; long sequenceSize; for (sequenceSize = 0; carry != 0; sequenceSize++) { carry = additionStrategy.divide(null, divisor, carry, dummy, 1); } size = Math.min(basePrecision, thisDataSize + sequenceSize); } // One extra word for result in case the initial word becomes zero; to avoid loss of precision size++; dataStorage = createDataStorage(size); dataStorage.setSize(size); DataStorage.Iterator src = this.dataStorage.iterator(DataStorage.READ, 0, thisDataSize), dst = dataStorage.iterator(DataStorage.WRITE, 0, size); // Perform actual division carry = additionStrategy.divide(src, divisor, (float) 0, dst, thisDataSize); // Produce the trailing sequence of digits due to inexact division carry = additionStrategy.divide(null, divisor, carry, dst, size - thisDataSize); size -= getTrailingZeros(dataStorage, size); // Check if initial word of result is zero int leadingZeros = (getMostSignificantWord() < divisor ? 1 : 0); dataStorage = dataStorage.subsequence(leadingZeros, size - leadingZeros); exponent -= leadingZeros; if (exponent < -MAX_EXPONENT[this.radix]) { // Underflow return zero(); } dataStorage.setReadOnly(); } return new FloatApfloatImpl(sign, precision, exponent, dataStorage, this.radix); } public ApfloatImpl absFloor() throws ApfloatRuntimeException { if (this.sign == 0 || this.exponent >= this.dataStorage.getSize()) // Is integer already, with no extra hidden trailing digits { return precision(Apfloat.INFINITE); } else if (this.exponent <= 0) // Is less than one in absolute value { return zero(); } long size = this.exponent; // Size of integer part, now that this.dataStorage.getSize() > this.exponent size -= getTrailingZeros(this.dataStorage, size); DataStorage dataStorage = this.dataStorage.subsequence(0, size); ApfloatImpl apfloatImpl = new FloatApfloatImpl(this.sign, Apfloat.INFINITE, this.exponent, dataStorage, this.radix); return apfloatImpl; } public ApfloatImpl absCeil() throws ApfloatRuntimeException { if (this.sign == 0) { return this; } long exponent; DataStorage dataStorage; DataStorage.Iterator iterator = null; if (this.exponent <= 0) { // Number is < 1 but > 0; result is one int size = 1; dataStorage = createDataStorage(size); dataStorage.setSize(size); ArrayAccess arrayAccess = dataStorage.getArray(DataStorage.WRITE, 0, size); arrayAccess.getFloatData()[arrayAccess.getOffset()] = (float) 1; arrayAccess.close(); exponent = 1; } else if (getSize() <= this.exponent || // Check if the fractional part is nonzero findMismatch(iterator = getZeroPaddedIterator(this.exponent, getSize()), ZERO_ITERATOR, getSize() - this.exponent) < 0) { // Fractional part is zero; the result is the number itself (to infinite precision) long size = Math.min(this.dataStorage.getSize(), this.exponent); size -= getTrailingZeros(this.dataStorage, size); dataStorage = this.dataStorage.subsequence(0, size); // Ensure truncation exponent = this.exponent; } else { // Fractional part is nonzero; round up ApfloatContext ctx = ApfloatContext.getContext(); AdditionBuilder additionBuilder = ctx.getBuilderFactory().getAdditionBuilder(Float.TYPE); AdditionStrategy additionStrategy = additionBuilder.createAddition(this.radix); long size = this.exponent; // Size of integer part dataStorage = createDataStorage(size + 1); // Reserve room for carry overflow dataStorage.setSize(size + 1); DataStorage.Iterator src = this.dataStorage.iterator(DataStorage.READ, size, 0), dst = dataStorage.iterator(DataStorage.WRITE, size + 1, 0); float carry = additionStrategy.add(src, null, (float) 1, dst, size); // Add carry dst.setFloat(carry); // Set leading float as overflow carry src.close(); dst.close(); int carrySize = (int) carry; // For adjusting size, if carry did overflow or not size -= getTrailingZeros(dataStorage, size + 1); dataStorage = dataStorage.subsequence(1 - carrySize, size + carrySize); exponent = this.exponent + carrySize; } if (iterator != null) { iterator.close(); } dataStorage.setReadOnly(); ApfloatImpl apfloatImpl = new FloatApfloatImpl(this.sign, Apfloat.INFINITE, exponent, dataStorage, this.radix); return apfloatImpl; } public ApfloatImpl frac() throws ApfloatRuntimeException { if (this.sign == 0 || this.exponent <= 0) // Is less than one in absolute value already { return this; } if (this.exponent >= getSize()) // Is an integer, fractional part is zero { return zero(); } long size = this.dataStorage.getSize() - this.exponent; // Size of fractional part, now that getSize() > this.exponent long leadingZeros = getLeadingZeros(this.dataStorage, this.exponent); if (this.exponent + leadingZeros >= getSize()) { // All significant digits were lost, only trailing garbage digits return zero(); } DataStorage dataStorage = this.dataStorage.subsequence(this.exponent + leadingZeros, size - leadingZeros); long precision; if (this.precision != Apfloat.INFINITE) { // Precision is reduced as the integer part is omitted, plus any leading zeros precision = this.precision - getInitialDigits() - (this.exponent + leadingZeros) * BASE_DIGITS[this.radix] + getInitialDigits(dataStorage); if (precision <= 0) { // All significant digits were lost anyway, only trailing garbage digits return zero(); } } else { precision = Apfloat.INFINITE; } long exponent = -leadingZeros; ApfloatImpl apfloatImpl = new FloatApfloatImpl(this.sign, precision, exponent, dataStorage, this.radix); return apfloatImpl; } private ApfloatImpl zero() { return new FloatApfloatImpl(0, Apfloat.INFINITE, 0, null, this.radix); } public int radix() { return this.radix; } public long precision() { return this.precision; } public long size() throws ApfloatRuntimeException { assert (this.dataStorage != null); if (this.size == 0) { // Writes and reads of volatile long values are always atomic so multiple threads can read and write this at the same time this.size = getInitialDigits() + (getSize() - 1) * BASE_DIGITS[this.radix] - getLeastZeros(); } return this.size; } // Get number of trailing zeros private long getLeastZeros() throws ApfloatRuntimeException { if (this.leastZeros == UNDEFINED) { // Cache the value // NOTE: This is not synchronized; it's OK if multiple threads set this at the same time // Writes and reads of volatile long values are always atomic so multiple threads can read and write this at the same time long index = getSize() - 1; float word = getWord(index); word = getLeastSignificantWord(index, word); long leastZeros = 0; if (word == 0) { // Usually the last word is nonzero but in case precision was later changed, it might be zero long trailingZeros = getTrailingZeros(this.dataStorage, index) + 1; index -= trailingZeros; word = getWord(index); word = getLeastSignificantWord(index, word); leastZeros += trailingZeros * BASE_DIGITS[this.radix]; } assert (word != 0); while (word % this.radix == 0) { leastZeros++; word /= this.radix; } this.leastZeros = leastZeros; } return this.leastZeros; } public ApfloatImpl precision(long precision) { if (this.sign == 0 || precision == this.precision) { return this; } else { return new FloatApfloatImpl(this.sign, precision, this.exponent, this.dataStorage, this.radix); } } public long scale() throws ApfloatRuntimeException { assert (this.dataStorage != null); return (this.exponent - 1) * BASE_DIGITS[this.radix] + getInitialDigits(); } public int signum() { return this.sign; } public ApfloatImpl negate() throws ApfloatRuntimeException { return new FloatApfloatImpl(-this.sign, this.precision, this.exponent, this.dataStorage, this.radix); } public double doubleValue() { if (this.sign == 0) { return 0.0; } double value = 0.0, doubleBase = (double) BASE[this.radix]; int size = (int) Math.min(MAX_DOUBLE_SIZE, getSize()); DataStorage.Iterator iterator = this.dataStorage.iterator(DataStorage.READ, size, 0); while (iterator.hasNext()) { value += (double) iterator.getFloat(); value /= doubleBase; iterator.next(); } // If the end result fits in a double, any intermediate calculation must not overflow // Note that 1/BASE <= value < 1 if (this.exponent > 0) { return this.sign * value * Math.pow((double) BASE[this.radix], (double) (this.exponent - 1)) * BASE[this.radix]; } else { return this.sign * value * Math.pow((double) BASE[this.radix], (double) this.exponent); } } public long longValue() { if (this.sign == 0 || this.exponent <= 0) { return 0; } else if (this.exponent > MAX_LONG_SIZE) { // Overflow for sure return (this.sign > 0 ? Long.MAX_VALUE : Long.MIN_VALUE); } long value = 0, longBase = (long) BASE[this.radix], maxPrevious = Long.MIN_VALUE / longBase; // Number of words in integer part of the number int size = (int) Math.min(this.exponent, getSize()); DataStorage.Iterator iterator = this.dataStorage.iterator(DataStorage.READ, 0, size); for (int i = 0; i < (int) this.exponent; i++) { if (value < maxPrevious) { // Overflow value = 0; iterator.close(); break; } value *= longBase; if (i < size) { value -= (long) iterator.getFloat(); // Calculate value negated to handle 0x8000000000000000 iterator.next(); } } if (value == Long.MIN_VALUE || value >= 0) { // Overflow return (this.sign > 0 ? Long.MAX_VALUE : Long.MIN_VALUE); } else { return -this.sign * value; } } // If this ApfloatImpl is equal to 1 public boolean isOne() throws ApfloatRuntimeException { if (this.isOne == UNDEFINED) { // Cache the value // NOTE: This is not synchronized; it's OK if multiple threads set this at the same time this.isOne = (this.sign == 1 && this.exponent == 1 && getSize() == 1 && getMostSignificantWord() == (float) 1 ? 1 : 0); } return (this.isOne == 1); } public long equalDigits(ApfloatImpl x) throws ApfloatRuntimeException { if (!(x instanceof FloatApfloatImpl)) { throw new ImplementationMismatchException("Wrong operand type: " + x.getClass().getName()); } FloatApfloatImpl that = (FloatApfloatImpl) x; if (this.sign == 0 && that.sign == 0) // Both are zero { return Apfloat.INFINITE; } else if (this.sign != that.sign) // No match { return 0; } else if (this.radix != that.radix) { throw new RadixMismatchException("Cannot compare values with different radixes: " + this.radix + " and " + that.radix); } long thisScale = scale(), thatScale = that.scale(), minScale = Math.min(thisScale, thatScale), maxScale = Math.max(thisScale, thatScale); if (maxScale - 1 > minScale) // No match { return 0; } // Need to compare mantissas long thisSize = getSize(), thatSize = that.getSize(), size = Math.max(thisSize, thatSize); DataStorage.Iterator thisIterator = getZeroPaddedIterator(0, thisSize), thatIterator = that.getZeroPaddedIterator(0, thatSize); long index, result = Math.min(this.precision, that.precision); // If mantissas are identical int lastMatchingDigits = -1; // Will be used for deferred comparison hanging in last word, e.g. this = 1.000000000, that = 0.999999999 float carry, base = BASE[this.radix]; if (this.exponent > that.exponent) { // Possible case this = 1.0000000, that = 0.9999999 float value = thisIterator.getFloat(); // Check first word if (value != (float) 1) { // No match thisIterator.close(); thatIterator.close(); return 0; } carry = base; thisIterator.next(); } else if (this.exponent < that.exponent) { // Possible case this = 0.9999999, that = 1.0000000 float value = thatIterator.getFloat(); // Check first word if (value != (float) 1) { // No match thisIterator.close(); thatIterator.close(); return 0; } carry = -base; thatIterator.next(); } else { // Trivial case, e.g. this = 111234, that = 111567 carry = 0; } // Calculate this - that, stopping at first difference for (index = 0; index < size; index++) { float value = thisIterator.getFloat() - thatIterator.getFloat() + carry; if (value == 0) { // Trivial case; words are equal carry = 0; } else if (Math.abs(value) > (float) 1) { // Mismatch found if (Math.abs(value) >= base) { // Deferred comparison, e.g. this = 1.0000000002, that = 0.9999999991 lastMatchingDigits = -1; } else { // Any trivial cases and e.g. this = 1.0000000001, that = 0.9999999992 lastMatchingDigits = BASE_DIGITS[this.radix] - getDigits(Math.abs(value)); } break; } else if (value == (float) 1) { // Case this = 1.0000000..., that = 0.9999999... carry = base; } else if (value == (float) -1) { // Case this = 0.9999999..., that = 1.0000000... carry = -base; } thisIterator.next(); thatIterator.next(); } if (index < size || carry != 0) // Mismatch found { long initialMatchingDigits = (this.exponent == that.exponent ? Math.min(getInitialDigits(), that.getInitialDigits()) : // Normal case, e.g. this = 10, that = 5 BASE_DIGITS[this.radix]); // Special case, e.g. this = 1.0, that = 0.9 // Note that this works even if index == 0 long middleMatchingDigits = (index - 1) * BASE_DIGITS[this.radix]; // This is correct even if exponents are different // Limit by available precision result = Math.min(result, initialMatchingDigits + middleMatchingDigits + lastMatchingDigits); // Handle some cases e.g. 0.15 vs. 0.04 result = Math.max(result, 0); } thisIterator.close(); thatIterator.close(); return result; } public int compareTo(ApfloatImpl x) throws ApfloatRuntimeException { if (!(x instanceof FloatApfloatImpl)) { throw new ImplementationMismatchException("Wrong operand type: " + x.getClass().getName()); } FloatApfloatImpl that = (FloatApfloatImpl) x; if (this.sign == 0 && that.sign == 0) { return 0; } else if (this.sign < that.sign) // Now we know that not both are zero { return -1; } else if (this.sign > that.sign) { return 1; } else if (this.radix != that.radix) { throw new RadixMismatchException("Cannot compare values with different radixes: " + this.radix + " and " + that.radix); } else if (scale() < that.scale()) // Now we know that both have same sign (which is not zero) { return -this.sign; } else if (scale() > that.scale()) { return this.sign; } // Need to compare mantissas return this.sign * compareMantissaTo(that); } // Returns an iterator for this number's data storage from start to end, // least significant word is correctly truncated with getLeastSignificantWord(), // after that the iterator returns zeros only private DataStorage.Iterator getZeroPaddedIterator(final long start, final long end) throws ApfloatRuntimeException { final DataStorage.Iterator iterator = this.dataStorage.iterator(DataStorage.READ, start, end); return new DataStorage.Iterator() { @Override public float getFloat() throws ApfloatRuntimeException { float value; if (this.index < end) { value = iterator.getFloat(); if (this.index == end - 1) { value = getLeastSignificantWord(this.index, value); } } else { value = 0; } return value; } @Override public void next() throws ApfloatRuntimeException { if (this.index < end) { iterator.next(); this.index++; } } @Override public void close() throws ApfloatRuntimeException { iterator.close(); } private static final long serialVersionUID = 1L; private long index = start; }; } // Compare absolute values of mantissas private int compareMantissaTo(FloatApfloatImpl that) throws ApfloatRuntimeException { long thisSize = getSize(), thatSize = that.getSize(), size = Math.max(thisSize, thatSize); DataStorage.Iterator thisIterator = getZeroPaddedIterator(0, thisSize), thatIterator = that.getZeroPaddedIterator(0, thatSize); int result = 0; long index = findMismatch(thisIterator, thatIterator, size); if (index >= 0) // Mismatch found { float thisValue = thisIterator.getFloat(), thatValue = thatIterator.getFloat(); if (thisValue < thatValue) { result = -1; } else if (thisValue > thatValue) { result = 1; } } thisIterator.close(); thatIterator.close(); return result; } // Returns index of first mismatching float, or -1 if mantissas are equal // Iterators are left to point to the mismatching words private long findMismatch(DataStorage.Iterator thisIterator, DataStorage.Iterator thatIterator, long size) throws ApfloatRuntimeException { for (long index = 0; index < size; index++) { float thisValue = thisIterator.getFloat(), thatValue = thatIterator.getFloat(); if (thisValue != thatValue) { return index; } thisIterator.next(); thatIterator.next(); } // All searched words matched exactly return -1; } // Truncate insignificant digits from the last float of the number private float getLeastSignificantWord(long index, float word) throws ApfloatRuntimeException { if (this.precision == Apfloat.INFINITE) { return word; } // Total digits including the specified index long digits = getInitialDigits() + index * BASE_DIGITS[this.radix]; if (this.precision >= digits) { return word; } // Assert that the second array access will not be out of bounds float divisor = MINIMUM_FOR_DIGITS[this.radix][(int) (digits - this.precision)]; return (float) (long) (word / divisor) * divisor; } /** * Compares this object to the specified object. * * @param obj The object to compare with. * * @return true if the objects are equal; false otherwise. */ @Override public boolean equals(Object obj) { if (!(obj instanceof ApfloatImpl)) { return false; } ApfloatImpl thatImpl = (ApfloatImpl) obj; // Special comparisons against Apfloat.ZERO and Apfloat.ONE work regardless of radix or implementation class if (signum() == 0 && thatImpl.signum() == 0) { return true; } else if (isOne() && thatImpl.isOne()) { return true; } if (!(obj instanceof FloatApfloatImpl)) { return false; } FloatApfloatImpl that = (FloatApfloatImpl) obj; if (this.radix != that.radix) { // Limitation: cannot compare values with different radixes return false; } else if (this.sign != that.sign || this.exponent != that.exponent) { return false; } else { // Need to compare mantissas return compareMantissaTo(that) == 0; } } @Override public int hashCode() { if (this.hashCode == 0) { // Cache the value // NOTE: This is not synchronized; it's OK if multiple threads set this at the same time int hashCode = 1 + this.sign + (int) this.exponent + (int) (this.exponent >>> 32); if (this.dataStorage != null) { long size = getSize(); // Scan through log(size) scattered words in the mantissa for (long i = 0; i < size; i = i + i + 1) { float word = getWord(i); if (i == size - 1) { word = getLeastSignificantWord(i, word); } long element = (long) word; hashCode += (int) element + (int) (element >>> 32); } } this.hashCode = hashCode; } return this.hashCode; } public String toString(boolean pretty) throws ApfloatRuntimeException { if (this.sign == 0) { return "0"; } long size = getSize() * BASE_DIGITS[this.radix], // This is a rounded up value length; if (pretty) { long scale = scale(); if (scale <= 0) { length = 2 - scale + size; // Format is 0.xxxx or 0.0000xxx } else if (size > scale) { length = 1 + size; // Format is x.xxx } else { length = scale; // Format is xxxx or xxxx0000 } length += (this.sign < 0 ? 1 : 0); // Room for minus sign } else { length = size + 24; // Sign, "0.", "e", exponent sign and 19 digits of exponent } if (length > Integer.MAX_VALUE || length < 0) // Detect overflow { throw new ApfloatInternalException("Number is too large to fit in a String"); } StringWriter writer = new StringWriter((int) length); try { writeTo(writer, pretty); } catch (IOException ioe) { throw new ApfloatInternalException("Unexpected I/O error writing to StringWriter", ioe); } String value = writer.toString(); assert (value.length() <= length); // Postcondition to ensure performance return value; } private static void writeZeros(Writer out, long count) throws IOException { for (long i = 0; i < count; i++) { out.write('0'); } } public void writeTo(Writer out, boolean pretty) throws IOException, ApfloatRuntimeException { if (this.sign == 0) { out.write('0'); return; } if (this.sign < 0) { out.write('-'); } long integerDigits, // Number of digits to write before the decimal point exponent; // Exponent to print if (pretty) { if (this.exponent <= 0) { out.write("0."); // Output is 0.xxxx writeZeros(out, -scale()); // Print leading zeros after decimal point before first nonzero digit integerDigits = -1; // Decimal point is already written } else { integerDigits = scale(); // Decimal point location } exponent = 0; // Do not print exponent } else { integerDigits = 1; // Always write as x.xxxey exponent = scale() - 1; // Print exponent } boolean leftPadZeros = false; // If the written base unit should be left-padded with zeros long size = getSize(), digitsToWrite = Math.min(this.precision, getInitialDigits() + (size - 1) * BASE_DIGITS[this.radix]), digitsWritten = 0, trailingZeros = 0; DataStorage.Iterator iterator = this.dataStorage.iterator(DataStorage.READ, 0, size); char[] buffer = new char[BASE_DIGITS[this.radix]]; while (size > 0) { int start = (leftPadZeros ? 0 : BASE_DIGITS[this.radix] - getInitialDigits()), digits = (int) Math.min(digitsToWrite, BASE_DIGITS[this.radix] - start); formatWord(buffer, iterator.getFloat()); for (int i = 0; i < digits; i++) { int c = buffer[start + i]; if (c == '0') { trailingZeros++; digitsToWrite--; } else { while (trailingZeros > 0) { if (digitsWritten == integerDigits) { out.write('.'); } out.write('0'); digitsWritten++; trailingZeros--; } if (digitsWritten == integerDigits) { out.write('.'); } out.write(c); digitsWritten++; digitsToWrite--; } } leftPadZeros = true; // Always pad with zeros after first word iterator.next(); size--; } if (!pretty && exponent != 0) { out.write("e" + exponent); } writeZeros(out, integerDigits - digitsWritten); // If format is xxxx0000 } private void formatWord(char[] buffer, float word) { int position = BASE_DIGITS[this.radix]; while (position > 0 && word > 0) { float newWord = (float) (long) (word / this.radix); int digit = (int) (word - newWord * this.radix); word = newWord; position--; buffer[position] = Character.forDigit(digit, this.radix); } // Left pad zeros while (position > 0) { position--; buffer[position] = '0'; } } // Effective size, in floats private long getSize() throws ApfloatRuntimeException { assert (this.dataStorage != null); return Math.min(getBasePrecision(), this.dataStorage.getSize()); } private static int checkRadix(int radix) throws NumberFormatException { if (radix < Character.MIN_RADIX || radix > Character.MAX_RADIX) { throw new NumberFormatException("Invalid radix " + radix + "; radix must be between " + Character.MIN_RADIX + " and " + Character.MAX_RADIX); } return radix; } // Get the most significant word of this number private float getMostSignificantWord() throws ApfloatRuntimeException { return getMostSignificantWord(this.dataStorage); } // Get the most significant word of the specified data storage private static float getMostSignificantWord(DataStorage dataStorage) throws ApfloatRuntimeException { float msw; ArrayAccess arrayAccess = dataStorage.getArray(DataStorage.READ, 0, 1); msw = arrayAccess.getFloatData()[arrayAccess.getOffset()]; arrayAccess.close(); return msw; } // Get number of digits in the most significant word private int getInitialDigits() throws ApfloatRuntimeException { if (this.initialDigits == UNDEFINED) { // Cache the value // NOTE: This is not synchronized; it's OK if multiple threads set this at the same time this.initialDigits = getDigits(getMostSignificantWord()); } return this.initialDigits; } // Get number of digits in the most significant word of specified data private int getInitialDigits(DataStorage dataStorage) throws ApfloatRuntimeException { return getDigits(getMostSignificantWord(dataStorage)); } // Gets the number of digits in the specified float and this number's radix private int getDigits(float x) { assert (x > 0); float[] minimums = MINIMUM_FOR_DIGITS[this.radix]; int i = minimums.length; while (x < minimums[--i]) { } return i + 1; } // Gets the precision in floats private long getBasePrecision() throws ApfloatRuntimeException { return getBasePrecision(this.precision, getInitialDigits()); } // Gets the precision in floats, based on specified precision (in digits), // number of digits in most significant word and this number's radix private long getBasePrecision(long precision, int mswDigits) { if (precision == Apfloat.INFINITE) { return Apfloat.INFINITE; } else { return (precision + BASE_DIGITS[this.radix] - mswDigits - 1) / BASE_DIGITS[this.radix] + 1; } } private float getWord(long index) { ArrayAccess arrayAccess = this.dataStorage.getArray(DataStorage.READ, index, 1); float word = arrayAccess.getFloatData()[arrayAccess.getOffset()]; arrayAccess.close(); return word; } private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException { this.leastZeros = UNDEFINED; this.isOne = UNDEFINED; in.defaultReadObject(); } // Gets a new data storage for specified size private static DataStorage createDataStorage(long size) throws ApfloatRuntimeException { ApfloatContext ctx = ApfloatContext.getContext(); DataStorageBuilder dataStorageBuilder = ctx.getBuilderFactory().getDataStorageBuilder(); return dataStorageBuilder.createDataStorage(size * 4); } // Gets I/O block size in floats private static int getBlockSize() { ApfloatContext ctx = ApfloatContext.getContext(); return ctx.getBlockSize() / 4; } private static final DataStorage.Iterator ZERO_ITERATOR = new DataStorage.Iterator() { @Override public float getFloat() { return 0; } @Override public void next() { } private static final long serialVersionUID = 1L; }; private static final long serialVersionUID = 4198839366471670758L; private static final int UNDEFINED = 0x80000000; private static final int MAX_LONG_SIZE = 4; private static final int MAX_DOUBLE_SIZE = 4; private int sign; private long precision; private long exponent; private DataStorage dataStorage; private int radix; private int hashCode = 0; private int initialDigits = UNDEFINED; private int isOne = UNDEFINED; private volatile long leastZeros = UNDEFINED; private volatile long size = 0; }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy