org.apache.poi.ss.util.MutableFPNumber Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of apache-poi Show documentation
Show all versions of apache-poi Show documentation
The Apache Commons Codec package contains simple encoder and decoders for
various formats such as Base64 and Hexadecimal. In addition to these
widely used encoders and decoders, the codec package also maintains a
collection of phonetic encoding utilities.
/* ====================================================================
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==================================================================== */
package org.apache.poi.ss.util;
import java.math.BigInteger;
final class MutableFPNumber {
// TODO - what about values between (1014-0.5) and (1014-0.05) ?
/**
* The minimum value in 'Base-10 normalised form'.
* When {@link #_binaryExponent} == 46 this is the the minimum {@link #_frac} value
* (1014-0.05) * 2^17
*
* Values between (1014-0.05) and 1014 will be represented as '1'
* followed by 14 zeros.
* Values less than (1014-0.05) will get shifted by one more power of 10
*
* This frac value rounds to '1' followed by fourteen zeros with an incremented decimal exponent
*/
private static final BigInteger BI_MIN_BASE = new BigInteger("0B5E620F47FFFE666", 16);
/**
* For 'Base-10 normalised form'
* The maximum {@link #_frac} value when {@link #_binaryExponent} == 49
* (10^15-0.5) * 2^14
*/
private static final BigInteger BI_MAX_BASE = new BigInteger("0E35FA9319FFFE000", 16);
/**
* Width of a long
*/
private static final int C_64 = 64;
/**
* Minimum precision after discarding whole 32-bit words from the significand
*/
private static final int MIN_PRECISION = 72;
private BigInteger _significand;
private int _binaryExponent;
public MutableFPNumber(BigInteger frac, int binaryExponent) {
_significand = frac;
_binaryExponent = binaryExponent;
}
public MutableFPNumber copy() {
return new MutableFPNumber(_significand, _binaryExponent);
}
public void normalise64bit() {
int oldBitLen = _significand.bitLength();
int sc = oldBitLen - C_64;
if (sc == 0) {
return;
}
if (sc < 0) {
throw new IllegalStateException("Not enough precision");
}
_binaryExponent += sc;
if (sc > 32) {
int highShift = (sc-1) & 0xFFFFE0;
_significand = _significand.shiftRight(highShift);
sc -= highShift;
oldBitLen -= highShift;
}
if (sc < 1) {
throw new IllegalStateException();
}
_significand = Rounder.round(_significand, sc);
if (_significand.bitLength() > oldBitLen) {
sc++;
_binaryExponent++;
}
_significand = _significand.shiftRight(sc);
}
public int get64BitNormalisedExponent() {
return _binaryExponent + _significand.bitLength() - C_64;
}
public boolean isBelowMaxRep() {
int sc = _significand.bitLength() - C_64;
return _significand.compareTo(BI_MAX_BASE.shiftLeft(sc)) < 0;
}
public boolean isAboveMinRep() {
int sc = _significand.bitLength() - C_64;
return _significand.compareTo(BI_MIN_BASE.shiftLeft(sc)) > 0;
}
public NormalisedDecimal createNormalisedDecimal(int pow10) {
// missingUnderBits is (0..3)
int missingUnderBits = _binaryExponent-39;
int fracPart = (_significand.intValue() << missingUnderBits) & 0xFFFF80;
long wholePart = _significand.shiftRight(C_64-_binaryExponent-1).longValue();
return new NormalisedDecimal(wholePart, fracPart, pow10);
}
public void multiplyByPowerOfTen(int pow10) {
TenPower tp = TenPower.getInstance(Math.abs(pow10));
if (pow10 < 0) {
mulShift(tp._divisor, tp._divisorShift);
} else {
mulShift(tp._multiplicand, tp._multiplierShift);
}
}
private void mulShift(BigInteger multiplicand, int multiplierShift) {
_significand = _significand.multiply(multiplicand);
_binaryExponent += multiplierShift;
// check for too much precision
int sc = (_significand.bitLength() - MIN_PRECISION) & 0xFFFFFFE0;
// mask makes multiples of 32 which optimises BigInteger.shiftRight
if (sc > 0) {
// no need to round because we have at least 8 bits of extra precision
_significand = _significand.shiftRight(sc);
_binaryExponent += sc;
}
}
private static final class Rounder {
private static final BigInteger[] HALF_BITS;
static {
BigInteger[] bis = new BigInteger[33];
long acc=1;
for (int i = 1; i < bis.length; i++) {
bis[i] = BigInteger.valueOf(acc);
acc <<=1;
}
HALF_BITS = bis;
}
/**
* @param nBits number of bits to shift right
*/
public static BigInteger round(BigInteger bi, int nBits) {
if (nBits < 1) {
return bi;
}
return bi.add(HALF_BITS[nBits]);
}
}
/**
* Holds values for quick multiplication and division by 10
*/
private static final class TenPower {
private static final BigInteger FIVE = BigInteger.valueOf(5);
private static final TenPower[] _cache = new TenPower[350];
public final BigInteger _multiplicand;
public final BigInteger _divisor;
public final int _divisorShift;
public final int _multiplierShift;
private TenPower(int index) {
BigInteger fivePowIndex = FIVE.pow(index);
int bitsDueToFiveFactors = fivePowIndex.bitLength();
int px = 80 + bitsDueToFiveFactors;
BigInteger fx = BigInteger.ONE.shiftLeft(px).divide(fivePowIndex);
int adj = fx.bitLength() - 80;
_divisor = fx.shiftRight(adj);
bitsDueToFiveFactors -= adj;
_divisorShift = -(bitsDueToFiveFactors+index+80);
int sc = fivePowIndex.bitLength() - 68;
if (sc > 0) {
_multiplierShift = index + sc;
_multiplicand = fivePowIndex.shiftRight(sc);
} else {
_multiplierShift = index;
_multiplicand = fivePowIndex;
}
}
static TenPower getInstance(int index) {
TenPower result = _cache[index];
if (result == null) {
result = new TenPower(index);
_cache[index] = result;
}
return result;
}
}
public ExpandedDouble createExpandedDouble() {
return new ExpandedDouble(_significand, _binaryExponent);
}
}