You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@commons.apache.org by er...@apache.org on 2019/08/08 17:30:28 UTC
[commons-numbers] 05/18: NUMBERS-120: Extract functionality of
doubleValue() to helper method for reuse in floatValue()
This is an automated email from the ASF dual-hosted git repository.
erans pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/commons-numbers.git
commit 1712ee65ed09d1813aebde55b2d96c6aa05a2c9f
Author: Schamschi <he...@gmx.at>
AuthorDate: Wed Jun 26 02:35:38 2019 +0200
NUMBERS-120: Extract functionality of doubleValue() to helper method for reuse in floatValue()
---
.../commons/numbers/fraction/BigFraction.java | 141 +++++++++++++--------
1 file changed, 86 insertions(+), 55 deletions(-)
diff --git a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java
index d9efcaf..6de8f37 100644
--- a/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java
+++ b/commons-numbers-fraction/src/main/java/org/apache/commons/numbers/fraction/BigFraction.java
@@ -661,63 +661,75 @@ public class BigFraction extends Number implements Comparable<BigFraction>, Seri
}
/**
- * <p>
- * Gets the fraction as a {@code double}. This calculates the fraction as
- * the numerator divided by denominator.
- * </p>
- *
- * @return the fraction as a {@code double}
- * @see java.lang.Number#doubleValue()
+ * Calculates the sign bit, the biased exponent and the significand for a
+ * binary floating-point representation of this {@code BigFraction}
+ * according to the IEEE 754 standard, and encodes these values into a {@code long}
+ * variable. The representative bits are arranged adjacent to each other and
+ * placed at the low-order end of the returned {@code long} value, with the
+ * least significant bits used for the significand, the next more
+ * significant bits for the exponent, and next more significant bit for the
+ * sign.
+ * @param exponentLength the number of bits allowed for the exponent; must be
+ * between 1 and 32 (inclusive), and must not be greater
+ * than 63 - significandLength
+ * @param significandLength the number of bits allowed for the significand
+ * (excluding the implicit leading 1-bit in
+ * normalized numbers, e.g. 52 for a double-precision
+ * floating-point number); must be between 1 and
+ * (63 - exponentLength) (inclusive)
+ * @return the bits of an IEEE 754 binary floating-point representation of
+ * this fraction encoded in a {@code long}, as described above.
*/
- @Override
- public double doubleValue() {
+ private long toFloatingPointBits(int exponentLength, int significandLength) {
+ if (exponentLength < 1 ||significandLength < 1 || exponentLength > Math.min(32, 63 - significandLength)) {
+ throw new IllegalArgumentException();
+ }
if (numerator.signum() == 0) {
- return 0;
+ return 0L;
}
- /*
- * We will manually construct a long from which to create the double to
- * ensure maximum precision. First, we set the sign:
- */
long sign = numerator.signum() == -1 ? 1L : 0L;
BigInteger positiveNumerator = numerator.abs();
/*
- * The significand of a double value consists of 52 bits for the
- * fractional part, plus the implicit leading 1 bit for the
- * non-fractional part, which makes 53 bits in total. So we need to
- * calculate the most significant 54 bits of this fraction's quotient,
- * and then, based on the 54th most significant bit, find out whether
- * we need to round up or down.
+ * The most significant 1-bit of a non-zero number is not explicitly
+ * stored in the significand of an IEEE 754 normalized binary
+ * floating-point number, so we need to round the value of this fraction
+ * to (significandLength + 1) bits. In order to do this, we calculate
+ * the most significant (significandLength + 2) bits, and then, based on
+ * the least significant of those bits, find out whether we need to
+ * round up or down.
*
* First, we'll remove all powers of 2 from the denominator because they
- * are not relevant for the significand of the prospective double value.
+ * are not relevant for the significand of the prospective binary
+ * floating-point value.
*/
int denRightShift = denominator.getLowestSetBit();
BigInteger divisor = denominator.shiftRight(denRightShift);
/*
- * Now, we're going to calculate the 54 most significant bits of the
- * quotient using integer division. To guarantee that the quotient has
- * at least 54 bits, the bit length of the dividend must exceed that
- * of the divisor by at least 54. If the numerator has powers of 2
- * beyond this limit, they can be removed as well.
+ * Now, we're going to calculate the (significandLength + 2) most
+ * significant bits of the fraction's value using integer division. To
+ * guarantee that the quotient of the division has at least
+ * (significandLength + 2) bits, the bit length of the dividend must
+ * exceed that of the divisor by at least that amount. If the numerator
+ * has powers of 2 beyond this limit, they can be removed as well.
*/
- int numRightShift = positiveNumerator.bitLength() - divisor.bitLength() - 54;
+ int numRightShift = positiveNumerator.bitLength() - divisor.bitLength() - (significandLength + 2);
if (numRightShift > 0) {
numRightShift = Math.min(numRightShift, positiveNumerator.getLowestSetBit());
}
BigInteger dividend = positiveNumerator.shiftRight(numRightShift);
BigInteger quotient = dividend.divide(divisor);
- int quotRightShift = quotient.bitLength() - 53;
/*
* If the denominator was a power of two, then the divisor was reduced
- * 1, meaning the quotient was calculated exactly. Otherwise, the
+ * to 1, meaning the quotient was calculated exactly. Otherwise, the
* fractional part of the precise quotient's binary representation does
* not terminate.
*/
+ int quotRightShift = quotient.bitLength() - (significandLength + 1);
long significand = roundAndRightShift(
quotient,
quotRightShift,
@@ -725,60 +737,79 @@ public class BigFraction extends Number implements Comparable<BigFraction>, Seri
).longValue();
/*
- * If the significand had to be rounded up, this could have caused an
- * overflow into the 54th least significant bit.
+ * If the significand had to be rounded up, this could have caused the
+ * bit length of the significand to increase by one.
*/
- if ((significand & (1L << 53)) != 0) {
+ if ((significand & (1L << (significandLength + 1))) != 0) {
significand >>= 1;
quotRightShift++;
}
/*
- * Now comes the exponent. The absolute value of this fraction based
- * on the current local variables is:
+ * Now comes the exponent. The absolute value of this fraction based on
+ * the current local variables is:
*
* significand * 2^(numRightShift - denRightShift + quotRightShift)
*
- * To get the unbiased exponent for the double value, we need to add 52
- * to the above exponent, because the 52 least significant bits of
- * significant will be treated as a fractional part. To convert the
- * unbiased exponent to a biased exponent, we need to add 1023.
+ * To get the unbiased exponent for the floating-point value, we need to
+ * add (significandLength) to the above exponent, because all but the
+ * most significant bit of the significand will be treated as a
+ * fractional part. To convert the unbiased exponent to a biased
+ * exponent, we also need to add the exponent bias.
*/
- long exponent = numRightShift - denRightShift + quotRightShift + 52 + 1023;
+ int exponentBias = (1 << (exponentLength - 1)) - 1;
+ long exponent = numRightShift - denRightShift + quotRightShift + significandLength + exponentBias;
+ long maxExponent = (1L << exponentLength) - 1L; //special exponent for infinities and NaN
- if (exponent > 2046) { //infinity
- exponent = 2047;
- significand = 0;
+ if (exponent >= maxExponent) { //infinity
+ exponent = maxExponent;
+ significand = 0L;
} else if (exponent > 0) { //normalized number
- significand &= 0x000FFFFFFFFFFFFFL; //remove implicit leading 1-bit
+ significand &= -1L >>> (64 - significandLength); //remove implicit leading 1-bit
} else { //smaller than the smallest normalized number
/*
- * We need to round the quotient to fewer than 53 bits. This must
- * be done with the original quotient and not with the current
- * significand, because the loss of precision in the rounding to 53
- * bits might cause a rounding of the current significand's value
- * to produce a different result than a rounding of the
- * original quotient.
+ * We need to round the quotient to fewer than
+ * (significandLength + 1) bits. This must be done with the original
+ * quotient and not with the current significand, because the loss
+ * of precision in the previous rounding might cause a rounding of
+ * the current significand's value to produce a different result
+ * than a rounding of the original quotient.
*
* So we find out how many high-order bits from the quotient we can
- * squeeze into the significand. The absolute value of the fraction
+ * transfer into the significand. The absolute value of the fraction
* is:
*
* quotient * 2^(numRightShift - denRightShift)
*
* To get the significand, we need to right shift the quotient so
- * that the above exponent becomes (- 1022 - 52) = -1074 (-1022 is
- * the unbiased exponent of a subnormal double value, and 52 because
- * the significand will be treated as the fractional part)
+ * that the above exponent becomes (1 - exponentBias - significandLength)
+ * (the unbiased exponent of a subnormal floating-point number is
+ * defined as equivalent to the minimum unbiased exponent of a
+ * normalized floating-point number, and (- significandLength)
+ * because the significand will be treated as the fractional part).
*/
significand = roundAndRightShift(
quotient,
- - 1074 - (numRightShift - denRightShift),
+ (1 - exponentBias - significandLength) - (numRightShift - denRightShift),
!divisor.equals(BigInteger.ONE)
).longValue();
exponent = 0L;
}
- return Double.longBitsToDouble((sign << 63) | (exponent << 52) | significand);
+ return (sign << (significandLength + exponentLength)) | (exponent << significandLength) | significand;
+ }
+
+ /**
+ * <p>
+ * Gets the fraction as a {@code double}. This calculates the fraction as
+ * the numerator divided by denominator.
+ * </p>
+ *
+ * @return the fraction as a {@code double}
+ * @see java.lang.Number#doubleValue()
+ */
+ @Override
+ public double doubleValue() {
+ return Double.longBitsToDouble(toFloatingPointBits(11, 52));
}
/**