// $Id: MicroFloat.java,v 1.2 2004/08/03 04:57:42 Dave Exp $ /* * Float.java * Copyright (C) 2003, 2004 David Clausen * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program 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 General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. * */ package net.dclausen.microfloat; /** * A software implementation of IEEE-754 single precision math which does not * rely on the <code>float</code> data type. * This class overloads the <code>int</code> data type by storing * <code>float</code> data in it. * See the * <a href="package-summary.html#package_description">package description</a> * for more information. * <p> * @author David Clausen * @see <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Float.html">Float</a> * @see MicroDouble * @version $Revision: 1.2 $ */ public final class MicroFloat { ///////////////////////////////////////////////////////////////////////////// // General-purpose constants ///////////////////////////////////////////////////////////////////////////// /** * A constant representing the same value as * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Float.html#POSITIVE_INFINITY">Float.POSITIVE_INFINITY</a> */ public static final int POSITIVE_INFINITY = 0x7f800000; /** * A constant holding the same value as * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Float.html#NEGATIVE_INFINITY">Float.NEGATIVE_INFINITY</a> */ public static final int NEGATIVE_INFINITY = 0xff800000; /** * A constant holding the same value as * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Float.html#NaN">Float.NaN</a> */ public static final int NaN = 0x7fc00000; /** * A constant holding the same value as * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Float.html#MAX_VALUE">Float.MAX_VALUE</a> */ public static final int MAX_VALUE = 0x7f7fffff; /** * A constant holding the same value as * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Float.html#MIN_VALUE">Float.MIN_VALUE</a> */ public static final int MIN_VALUE = 0x00000001; /** * A single-precision version of {@link MicroDouble#E} */ public static final int E = 0x402df854; /** * A single-precision version of {@link MicroDouble#PI} */ public static final int PI = 0x40490fdb; // Other constants needed internally, and exposed as a convenience. /** A constant holding the value of 0.0f */ public static final int ZERO = 0x00000000; /** A constant holding the value of -0.0f */ public static final int NEGATIVE_ZERO = 0x80000000; /** A constant holding the value of 1.0f */ public static final int ONE = 0x3f800000; /** A constant holding the value of 2.0f */ public static final int TWO = 0x40000000; /** A constant holding the value of 0.5f */ public static final int ONE_HALF = 0x3f000000; ///////////////////////////////////////////////////////////////////////////// // Packing and unpacking the IEEE-754 single precision format ///////////////////////////////////////////////////////////////////////////// private static final int ABS_MASK = 0x7fffffff; private static final int SIGN_MASK = 0x80000000; // 1 bit private static final int EXPONENT_MASK = 0x7f800000; // 8 bits private static final int FRACTION_MASK = 0x007fffff; // 23 bits private static final int IMPLIED_ONE = 0x00800000; // 24th bit /** @return true iff d is negative */ static boolean unpackSign(int f) { return (f < 0); } /** @return an integer in the range [-150, 105] */ static int unpackExponent(int f) { return ((f >> 23) & 0xff) - 150; } /** @return an integer in the range [0, 0x00ffffff] */ static int unpackMantissa(int f) { if ((f & EXPONENT_MASK) == 0) { return ((f & FRACTION_MASK) << 1); } else { return ((f & FRACTION_MASK) | IMPLIED_ONE); } } /** * @return the float which most closely represents the given base-2 mantissa * and exponent */ static int pack(boolean negative, int exponent, int mantissa) { // left align mantissa int shift = BitUtils.countLeadingZeros(mantissa); mantissa <<= shift; exponent -= shift; return pack2(negative, exponent, mantissa); } /** * @return the float which most closely represents the given base-2 mantissa * and exponent */ static int pack(boolean negative, int exponent, long mantissa) { // shift mantissa so that it is left-aligned when cast to an int int shift = 32 - BitUtils.countLeadingZeros(mantissa); exponent += shift; if (shift > 0) { mantissa = BitUtils.stickyRightShift(mantissa, shift); } else if (shift < 0) { mantissa <<= -shift; } return pack2(negative, exponent, (int) mantissa); } /** * @param mantissa must be left aligned (or zero) */ private static int pack2(boolean negative, int exponent, int mantissa) { // reduce precision of mantissa, rounding if necessary if (mantissa != 0) { if (exponent < -157) { // subnormal mantissa = BitUtils.roundingRightShift(mantissa, -149 - exponent); } else { // normal mantissa = BitUtils.roundingRightShift(mantissa, 8); if (mantissa == 0x1000000) { // oops, the rounding carried into the 25th bit mantissa = 0x800000; exponent++; } // pack the exponent if (exponent > 96) { mantissa = POSITIVE_INFINITY; } else { mantissa ^= IMPLIED_ONE; mantissa |= (exponent + 158) << 23; } } } // pack the sign bit if (negative) { mantissa |= SIGN_MASK; } return mantissa; } ///////////////////////////////////////////////////////////////////////////// // Simple tests ///////////////////////////////////////////////////////////////////////////// /** * Mimics * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Float.html#isNaN(float)">Float.isNaN(float)</a> */ public static boolean isNaN(int f) { return ((f & ABS_MASK) > POSITIVE_INFINITY); } /** * Mimics * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Float.html#isInfinite(float)">Float.isInfinite(float)</a> */ public static boolean isInfinite(int f) { return ((f & ABS_MASK) == POSITIVE_INFINITY); } /** * Returns <code>true</code> if the specified number has zero * magnitude, <code>false</code> otherwise. * * @param f the <code>float</code> value to be tested. * @return <code>true</code> if the value of the argument is positive * zero or negative zero; <code>false</code> otherwise. */ public static boolean isZero(int f) { return ((f & ABS_MASK) == 0); } ///////////////////////////////////////////////////////////////////////////// // Sign changes ///////////////////////////////////////////////////////////////////////////// /** * Mimics * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Math.html#abs(float)">Math.abs(float)</a> */ public static int abs(int f) { //if (isNaN(f)) { // return NaN; //} return (f & ABS_MASK); } /** * Returns the negation of a <code>float</code> value. * Special cases: * <ul> * <li>If the argument is negative zero, the result is positive zero. * <li>If the argument is positive zero, the result is negative zero. * <li>If the argument is negative infinity, the result is positive infinity. * <li>If the argument is positive infinity, the result is negative infinity. * <li>If the argument is NaN, the result is NaN.</ul> * <p> * This method takes the place of the unary <code>-</code> operator. * * @param f the <code>float</code> value whose negated value is to be * determined * @return the negation of the argument. */ public static int negate(int f) { if (isNaN(f)) { return NaN; } return (f ^ SIGN_MASK); } ///////////////////////////////////////////////////////////////////////////// // Comparison ///////////////////////////////////////////////////////////////////////////// /** * Returns <code>true</code> if the specified numbers are considered equal * according to * <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#5198">section 15.21.1 * of the JLS</a>. Special cases: * <ul> * <li>If either operand is NaN, then the result is false * <li>Positive zero and negative zero are considered equal * </ul> * <p> * This method takes the place of the <code>==</code> operator. * * @param f1 the first <code>float</code> value to be compared. * @param f2 the second <code>float</code> value to be compared. * @return <code>true</code> if the two values are considered equal; * <code>false</code> otherwise. */ public static boolean eq(int f1, int f2) { return (((f1 == f2) && (! isNaN(f1))) || (isZero(f1) && isZero(f2))); } /** * Returns <code>true</code> if the specified numbers are considered unequal * according to * <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#5198">section * 15.21.1 of the JLS</a>. Special cases: * <ul> * <li>If either operand is NaN, then the result is true * <li>Positive zero and negative zero are considered equal * </ul> * The value returned by <code>ne</code> is always the opposite of the value * returned by <code>eq</code> for the same arguments. * <p> * This method takes the place of the <code>!=</code> operator. * * @param f1 the first <code>float</code> value to be compared. * @param f2 the second <code>float</code> value to be compared. * @return <code>true</code> if the two values are considered equal; * <code>false</code> otherwise. */ public static boolean ne(int f1, int f2) { return (! eq(f1, f2)); } /** * Returns <code>true</code> if the first argument is considered less than * the second argument according to * <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#153654">section * 15.20.1 of the JLS</a>. Special cases: * <ul> * <li>If either operand is NaN, then the result is false * <li>Positive zero and negative zero are considered equal * <li>Negative infinity is conisdered less than all other values except NaN * <li>Positive infinity is conisdered greater than all other values except NaN * </ul> * <p> * This method takes the place of the <code><</code> operator. * * @param f1 the first <code>float</code> value to be compared. * @param f2 the second <code>float</code> value to be compared. * @return <code>true</code> if the first value is less than the second value; * <code>false</code> otherwise. */ public static boolean lt(int f1, int f2) { if (isNaN(f1) || isNaN(f2)) { return false; } else if (f2 == ZERO) { f2 = NEGATIVE_ZERO; } return (cmp(f1, f2) < 0); } /** * Returns <code>true</code> if the first argument is considered less than * or equal to the second argument according to * <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#153654">section * 15.20.1 of the JLS</a>. Special cases: * <ul> * <li>If either operand is NaN, then the result is false * <li>Positive zero and negative zero are considered equal * <li>Negative infinity is conisdered less than all other values except NaN * <li>Positive infinity is conisdered greater than all other values except NaN * </ul> * <p> * This method takes the place of the <code><=</code> operator. * * @param f1 the first <code>float</code> value to be compared. * @param f2 the second <code>float</code> value to be compared. * @return <code>true</code> if the first value is less than or equal to * the second value; <code>false</code> otherwise. */ public static boolean le(int f1, int f2) { if (isNaN(f1) || isNaN(f2)) { return false; } else if (f2 == NEGATIVE_ZERO) { f2 = ZERO; } return (cmp(f1, f2) <= 0); } /** * Returns <code>true</code> if the first argument is considered greater than * the second argument according to * <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#153654">section * 15.20.1 of the JLS</a>. Special cases: * <ul> * <li>If either operand is NaN, then the result is false * <li>Positive zero and negative zero are considered equal * <li>Negative infinity is conisdered less than all other values except NaN * <li>Positive infinity is conisdered greater than all other values except NaN * </ul> * <p> * This method takes the place of the <code>></code> operator. * * @param f1 the first <code>float</code> value to be compared. * @param f2 the second <code>float</code> value to be compared. * @return <code>true</code> if the first value is greater than the second value; * <code>false</code> otherwise. */ public static boolean gt(int f1, int f2) { if (isNaN(f1) || isNaN(f2)) { return false; } else if (f1 == ZERO) { f1 = NEGATIVE_ZERO; } return (cmp(f1, f2) > 0); } /** * Returns <code>true</code> if the first argument is considered greater than * or equal to the second argument according to * <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#153654">section * 15.20.1 of the JLS</a>. Special cases: * <ul> * <li>If either operand is NaN, then the result is false * <li>Positive zero and negative zero are considered equal * <li>Negative infinity is conisdered less than all other values except NaN * <li>Positive infinity is conisdered greater than all other values except NaN * </ul> * <p> * This method takes the place of the <code>>=</code> operator. * * @param f1 the first <code>float</code> value to be compared. * @param f2 the second <code>float</code> value to be compared. * @return <code>true</code> if the first value is greater than or equal to * the second value; <code>false</code> otherwise. */ public static boolean ge(int f1, int f2) { if (isNaN(f1) || isNaN(f2)) { return false; } else if (f1 == NEGATIVE_ZERO) { f1 = ZERO; } return (cmp(f1, f2) >= 0); } /** * Mimics * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Float.html#compare(float, float)">Float.compare(float, float)</a>. * <p> * Note that when using this method (as well as <code>Float.compare</code>), * the following rules apply: * <ul><li> * <code>NaN</code> is considered * to be equal to itself and greater than all other * <code>float</code> values (including * <code>POSITIVE_INFINITY</code>). * <li> * <code>0.0</code> is considered to be greater * than <code>-0.0</code>. * </ul> */ public static int compare(int f1, int f2) { boolean n1 = isNaN(f1); boolean n2 = isNaN(f2); if (n1 || n2) { if (n1 && n2) { return 0; } return (n1 ? 1 : -1); } return cmp(f1, f2); } /** * Mimics * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Math.html#max(float, float)">Math.max(float, float)</a>. */ public static int max(int f1, int f2) { if (isNaN(f1) || isNaN(f2)) { return NaN; } return ((cmp(f1, f2) < 0) ? f2 : f1); } /** * Mimics * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Math.html#min(float, float)">Math.min(float, float)</a>. */ public static int min(int f1, int f2) { if (isNaN(f1) || isNaN(f2)) { return NaN; } return ((cmp(f1, f2) > 0) ? f2 : f1); } private static int cmp(int f1, int f2) { if (f1 < 0) { if (f2 < 0) { return f2 - f1; } else { return -1; } } else if (f2 < 0) { return 1; } else { return f1 - f2; } } ///////////////////////////////////////////////////////////////////////////// // Type conversion ///////////////////////////////////////////////////////////////////////////// /** * Convert the given <code>int</code> to a <code>float</code> as would happen * in a casting operation specified by * <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25214">section * 5.1.2 of the JLS</a>. This is a widening primitive conversion which * will not result in a loss of magnitude, but might result in a loss of * precision. * * @param x the <code>int</code> to be converted * @return the <code>float</code> representation of the argument */ public static int intToFloat(int x) { if (x < 0) { return pack(true, 0, -x); } return pack(false, 0, x); } /** * Convert the given <code>long</code> to a <code>float</code> as would happen * in a casting operation specified by * <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25214">section * 5.1.2 of the JLS</a>. This is a widening primitive conversion which * will not result in a loss of magnitude, but might result in a loss of * precision. * * @param x the <code>long</code> to be converted * @return the <code>float</code> representation of the argument */ public static int longToFloat(long x) { if (x < 0) { return pack(true, 0, -x); } return pack(false, 0, x); } /** * Convert the given <code>double</code> to a <code>float</code> as would happen * in a casting operation specified by * <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25363">section * 5.1.3 of the JLS</a>. This is a narrowing primitive conversion which * may result in a loss of magnitude and/or precision. * * @param d the <code>double</code> to be converted * @return the <code>float</code> representation of the argument */ public static int doubleToFloat(long d) { if (MicroDouble.isNaN(d)) { return NaN; } boolean n = MicroDouble.unpackSign(d); if (MicroDouble.isZero(d)) { return (n ? NEGATIVE_ZERO : ZERO); } else if (MicroDouble.isInfinite(d)) { return (n ? NEGATIVE_INFINITY : POSITIVE_INFINITY); } int x = MicroDouble.unpackExponent(d); long m = MicroDouble.unpackMantissa(d); return pack(n, x, m); } /** * Convert the given <code>float</code> to a <code>byte</code> as would happen * in a casting operation specified by * <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25363">section * 5.1.3 of the JLS</a>. This is a narrowing primitive conversion which * may result in a loss of magnitude and/or precision. * <p> * Note that this is a non-intuitive conversion. If the argument is outside * of the range of the byte type, the result is basically meaningless. * * @param f the <code>float</code> to be converted * @return the <code>byte</code> representation of the argument */ public static byte byteValue(int f) { long x = intValue(f); return (byte) x; } /** * Convert the given <code>float</code> to a <code>short</code> as would happen * in a casting operation specified by * <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25363">section * 5.1.3 of the JLS</a>. This is a narrowing primitive conversion which * may result in a loss of magnitude and/or precision. * <p> * Note that this is a non-intuitive conversion. If the argument is outside * of the range of the short type, the result is basically meaningless. * * @param f the <code>float</code> to be converted * @return the <code>short</code> representation of the argument */ public static short shortValue(int f) { long x = intValue(f); return (short) x; } /** * Convert the given <code>float</code> to an <code>int</code> as would happen * in a casting operation specified by * <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25363">section * 5.1.3 of the JLS</a>. This is a narrowing primitive conversion which * may result in a loss of magnitude and/or precision. * * @param f the <code>float</code> to be converted * @return the <code>int</code> representation of the argument */ public static int intValue(int f) { long x = longValue(f); if (x >= Integer.MAX_VALUE) { return Integer.MAX_VALUE; } else if (x <= Integer.MIN_VALUE) { return Integer.MIN_VALUE; } return (int) x; } /** * Convert the given <code>float</code> to a <code>long</code> as would happen * in a casting operation specified by * <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25363">section * 5.1.3 of the JLS</a>. This is a narrowing primitive conversion which * may result in a loss of magnitude and/or precision. * * @param f the <code>float</code> to be converted * @return the <code>long</code> representation of the argument */ public static long longValue(int f) { if (isNaN(f)) { return 0; } boolean n = unpackSign(f); int x = unpackExponent(f); long m = unpackMantissa(f); if (x > 0) { if ((x >= 63) || ((m >> (63 - x)) != 0)) { return (n ? Long.MIN_VALUE : Long.MAX_VALUE); } m <<= x; } else if (x <= -24) { return 0; } else { m >>>= -x; } return (n ? -m : m); } /** * Convert the given <code>float</code> to a <code>double</code> as would happen * in a casting operation specified by * <a href="http://java.sun.com/docs/books/jls/second_edition/html/conversions.doc.html#25214">section * 5.1.2 of the JLS</a>. This is a widening primitive conversion which * will result in neither a loss of magnitude nor precision. * * @param f the <code>float</code> to be converted * @return the <code>double</code> representation of the argument */ public static long doubleValue(int f) { return MicroDouble.floatToDouble(f); } ///////////////////////////////////////////////////////////////////////////// // Basic arithmetic ///////////////////////////////////////////////////////////////////////////// /** * Returns the sum of the two <code>float</code> arguments according to * <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#13510">section * 15.18.2 of the JLS</a>. * <p> * This method takes the place of the binary <code>+</code> operator. * * @param f1 the first <code>float</code> value to be summed. * @param f2 the second <code>float</code> value to be summed. * @return the sum of the two arguments */ public static int add(int f1, int f2) { if (isNaN(f1) || isNaN(f2)) { return NaN; } boolean n1 = unpackSign(f1); boolean n2 = unpackSign(f2); // special handling of infinity boolean i1 = isInfinite(f1); boolean i2 = isInfinite(f2); if (i1 || i2) { if (i1 && i2) { if (n1 != n2) { // infinites of opposite sign -> NaN return NaN; } else { // infinites of same sign -> infinity the same sign return f1; } } else if (i1) { return f1; // infinite + finite = infinite } else { return f2; // finite + infinite = infinite } } // special handling of zero boolean z1 = isZero(f1); boolean z2 = isZero(f2); if (z1 || z2) { if (z1 && z2) { if (n1 != n2) { // zeros of opposite sign -> positive zero return ZERO; } else { return f1; // zeros of same sign -> zero of the same sign } } else if (z1) { return f2; // zero + nonzero = nonzero } else { return f1; // nonzero + zero = nonzero } } // unpack, and add 3 guard digits int m1 = unpackMantissa(f1) << 3; int x1 = unpackExponent(f1) - 3; int m2 = unpackMantissa(f2) << 3; int x2 = unpackExponent(f2) - 3; // make exponents equal int dx = x1 - x2; if (dx > 0) { m2 = BitUtils.stickyRightShift(m2, dx); x2 = x1; } else if (dx < 0) { m1 = BitUtils.stickyRightShift(m1, -dx); x1 = x2; } // if the signs are different, negate the smaller mantissa and choose // the sign of the larger if (n1 ^ n2) { if (m1 > m2) { m2 = -m2; } else { m1 = -m1; n1 = n2; } } // add (or subtract) mantissas m1 += m2; // pack result, and handle special case of zero (which always returns +0.0) int f = pack(n1, x1, m1); if (f == NEGATIVE_ZERO) { return ZERO; } return f; } /** * Returns the difference of the two <code>float</code> arguments according to * <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#13510">section * 15.18.2 of the JLS</a>. * <p> * This method takes the place of the binary <code>-</code> operator. * * @param f1 the first <code>float</code> value * @param f2 the second <code>float</code> value * @return the difference of the two arguments */ public static int sub(int f1, int f2) { return add(f1, negate(f2)); } /** * Returns the product of the two <code>float</code> arguments according to * <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#5036">section * 15.17.1 of the JLS</a>. * <p> * This method takes the place of the <code>*</code> operator. * * @param f1 the first <code>float</code> value * @param f2 the second <code>float</code> value * @return the product of the two arguments */ public static int mul(int f1, int f2) { if (isNaN(f1) || isNaN(f2)) { return NaN; } boolean negative = unpackSign(f1) ^ unpackSign(f2); // special handling of infinity if (isInfinite(f1) || isInfinite(f2)) { if (isZero(f1) || isZero(f2)) { return NaN; } else { return (negative ? NEGATIVE_INFINITY : POSITIVE_INFINITY); } } // unpack int m1 = unpackMantissa(f1); int x1 = unpackExponent(f1); int m2 = unpackMantissa(f2); int x2 = unpackExponent(f2); // compute the resultant exponent x1 += x2; // compute the resultant mantissa using integer multiplication long m = ((long) m1) * ((long) m2); // round and pack the result return pack(negative, x1, m); } /** * Returns the quotient of the two <code>float</code> arguments according to * <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#5047">section * 15.17.2 of the JLS</a>. * <p> * This method takes the place of the <code>/</code> operator. * * @param f1 the <code>float</code> dividend * @param f2 the <code>float</code> divisor * @return the quotient of the two arguments */ public static int div(int f1, int f2) { if (isNaN(f1) || isNaN(f2)) { return NaN; } boolean negative = unpackSign(f1) ^ unpackSign(f2); // special handling of infinity boolean n1 = isInfinite(f1); boolean n2 = isInfinite(f2); if (n1 || n2) { if (n1 && n2) { return NaN; } else if (n1) { return (negative ? NEGATIVE_INFINITY : POSITIVE_INFINITY); } else { return (negative ? NEGATIVE_ZERO : ZERO); } } // neither value is infinite // special handling of zero n1 = isZero(f1); n2 = isZero(f2); if (n1 || n2) { if (n1 && n2) { return NaN; } else if (n1) { return (negative ? NEGATIVE_ZERO : ZERO); } else { return (negative ? NEGATIVE_INFINITY : POSITIVE_INFINITY); } } // neither value is zero // unpack int m1 = unpackMantissa(f1); int x1 = unpackExponent(f1); int m2 = unpackMantissa(f2); int x2 = unpackExponent(f2); // shift the dividend to the left to increase precision, then do an integer // divide int s = BitUtils.countLeadingZeros(m1) + 22; long m3 = ((long) m1) << s; int x = x1 - x2 - s; long m = m3 / m2; boolean r = ((m * m2) != m3); // put a non-zero fraction into the sticky bit if (r) { m |= 1; } return pack(negative, x, m); } /** * Returns the remainder of the two <code>float</code> arguments according to * <a href="http://java.sun.com/docs/books/jls/second_edition/html/expressions.doc.html#24956">section * 15.17.3 of the JLS</a>. * <p> * This method takes the place of the <code>%</code> operator. * * @param f1 the <code>float</code> dividend * @param f2 the <code>float</code> divisor * @return the remainder of the two arguments */ public static int mod(int f1, int f2) { if (isNaN(f1) || isNaN(f2) || isInfinite(f1) || isZero(f2)) { return NaN; } else if (isZero(f1) || isInfinite(f2)) { return f1; } // unpack int x1 = unpackExponent(f1); int x2 = unpackExponent(f2); if (x1 < x2) { return f1; } boolean n = unpackSign(f1); int m1 = unpackMantissa(f1); int m2 = unpackMantissa(f2); if (x1 == x2) { m1 %= m2; } else { // reduce m1 by left shifting and modding until the exponents x1 and x2 are // equal while (x1 != x2) { int s = Math.min(39, x1 - x2); x1 -= s; m1 = (int) ((((long) m1) << s) % m2); } } return pack(n, x1, m1); } ///////////////////////////////////////////////////////////////////////////// // Rounding ///////////////////////////////////////////////////////////////////////////// /** * Returns the <code>float</code> of greatest magnitude (furthest from zero) * that is equal to a mathematical integer and which has a mignitude not * greater than the argument's magnitude. Special cases: * <ul><li>If the argument value is already equal to a mathematical * integer, then the result is the same as the argument. * <li>If the argument is NaN or an infinity or positive zero or * negative zero, then the result is the same as the argument.</ul> * * @param f a <code>float</code> value. * @return the <code>float</code> of greatest magnitude (furthest from zero) * whose magnitude is not greater than the argument's and which * is equal to a mathematical integer. */ public static int truncate(int f) { return round(f, false, unpackSign(f)); } /** * Mimics * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Math.html#rint(double)">Math.rint(double)</a>, * using single precision. */ public static int rint(int f) { return round(f, true, false); } /** * Mimics * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Math.html#floor(double)">Math.floor(double)</a>, * using single precision. */ public static int floor(int f) { return round(f, false, false); } /** * Mimics * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Math.html#ceil(double)">Math.ceil(double)</a>, * using single precision. */ public static int ceil(int f) { return round(f, false, true); } /** * Mimics * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Math.html#round(double)">Math.round(double)</a>, * using single precision. */ public static int round(int f) { return intValue(floor(add(f, ONE_HALF))); } private static int round(int f, boolean round, boolean ceil) { if (isNaN(f)) { return NaN; } else if (isZero(f) || isInfinite(f)) { return f; } int x = unpackExponent(f); if (x >= 0) { return f; } boolean n = unpackSign(f); int m = unpackMantissa(f); if (round) { m = BitUtils.roundingRightShift(m, -x); } else { int r; if (x <= -32) { r = m; m = 0; } else { r = m << (32 + x); m >>>= -x; } if ((n ^ ceil) && (r != 0)) { m++; } } return pack(n, 0, m); } ///////////////////////////////////////////////////////////////////////////// // String conversion ///////////////////////////////////////////////////////////////////////////// // decimal -> binary // base 2 mantissas for 10**-54 through 10**38, at intervals of 100 private static final int[] pow10m = { 0xc428d05b, 0x993fe2c7, 0xef73d257, 0xbb127c54, 0x92267121, 0xe45c10c4, 0xb267ed19, 0x8b61313c, 0xd9c7dced, 0xaa242499, 0x84ec3c98, 0xcfb11ead, 0xa2425ff7, 0xfd87b5f3, 0xc6120625, 0x9abe14cd, 0xf1c90081, 0xbce50865, 0x9392ee8f, 0xe69594bf, 0xb424dc35, 0x8cbccc09, 0xdbe6fecf, 0xabcc7712, 0x8637bd06, 0xd1b71759, 0xa3d70a3d, 0x80000000, 0xc8000000, 0x9c400000, 0xf4240000, 0xbebc2000, 0x9502f900, 0xe8d4a510, 0xb5e620f5, 0x8e1bc9bf, 0xde0b6b3a, 0xad78ebc6, 0x87867832, 0xd3c21bcf, 0xa56fa5ba, 0x813f3979, 0xc9f2c9cd, 0x9dc5ada8, 0xf684df57, 0xc097ce7c, 0x96769951, }; // base 2 exponents for 10**-54 through 10**38, at intervals of 100 private static final short[] pow10x = { -211, -204, -198, -191, -184, -178, -171, -164, -158, -151, -144, -138, -131, -125, -118, -111, -105, -98, -91, -85, -78, -71, -65, -58, -51, -45, -38, -31, -25, -18, -12, -5, 2, 8, 15, 22, 28, 35, 42, 48, 55, 62, 68, 75, 81, 88, 95, }; private static int decToFloat(boolean negative, int base10x, int base10m) { if (base10m == 0) { return (negative ? NEGATIVE_ZERO : ZERO); } // maximize base10m to ensure consistency between toString and parseFloat while ((base10m > 0) && (base10m <= 0x19999999)) { // (Integer.MAX_VALUE / 5))) { base10m = (base10m << 3) + (base10m << 1); base10x--; } // base10x needs to be a multiple of 2, because the tables are // spaced at intervals of 100 (not 10). base10x += 54; boolean mod = ((base10x & 1) != 0); base10x >>= 1; if (base10x < 0) { // -54 return (negative ? NEGATIVE_ZERO : ZERO); } else if (base10x > 46) { // 38 return (negative ? NEGATIVE_INFINITY : POSITIVE_INFINITY); } int base2x = pow10x[base10x]; long base2m = (base10m & 0xffffffffL) * (pow10m[base10x] & 0xffffffffL); if (mod) { if (base2m < 0) { base2m >>>= 1; base2x++; } base2m += base2m >>> 2; base2x += 3; } return pack(negative, base2x, base2m); } /** * Mimics * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Float.html#parseFloat(String)">Float.parseFloat(String)</a>. * <p> * <b>This implementation is known to be inaccurate, and * does not always return the same value as * <code>Float.parseFloat</code>.</b> However the difference should be no * greater than 1 ulp. * * @exception NumberFormatException if the string does not contain a * parsable number. */ public static int parseFloat(String s) { // remove leading & trailing whitespace s = s.trim().toUpperCase(); // check length int len = s.length(); if (len == 0) { throw new NumberFormatException(s); } // check for NaN if ("NAN".equals(s)) { return NaN; } // begin parsing, one character at a time int idx = 0; // read sign boolean negative = false; char c = s.charAt(0); negative = (c == '-'); if (negative || (c == '+')) { idx = 1; } // check for "Infinity" if (idx < len) { c = s.charAt(idx); if ((c == 'I') || (c == 'i')) { if ("INFINITY".equals(s.substring(idx))) { return (negative ? NEGATIVE_INFINITY : POSITIVE_INFINITY); } } } // read Digits.Digits int mantissa = 0; int exponent = 0; int fractionChars = 0; boolean sticky = false; boolean readingFraction = false; while (idx < len) { c = s.charAt(idx); if (c == '.') { if (readingFraction) { throw new NumberFormatException(s); } readingFraction = true; } else if ((c < '0') || (c > '9')) { break; } else { fractionChars++; if (mantissa <= 0x19999998) { // ((Integer.MAX_VALUE / 5) - 1)) { mantissa = (mantissa << 3) + (mantissa << 1) + (c - '0'); if (readingFraction) { exponent--; } } else { if (! readingFraction) { exponent++; } sticky |= (c != '0'); } } idx++; } if (fractionChars == 0) { throw new NumberFormatException(s); } // read exponent if (((idx + 1) < len) && ((s.charAt(idx) == 'E') || (s.charAt(idx) == 'e'))) { try { exponent += Integer.parseInt(s.substring(idx + 1)); } catch (NumberFormatException e) { throw new NumberFormatException(s); } idx = len; } else if (idx != len) { // check that we parsed the entire string throw new NumberFormatException(s); } // convert the decimal to a float return decToFloat(negative, exponent, mantissa); } // binary -> decimal // base 10 mantissas for 2**-150 through 2**98, at intervals of 2**8 private static final int[] pow2m = { 0xb35dbf82, 0x2deaf18a, 0x758ca7c7, 0x1e17b843, 0x4d0985cb, 0xc5371912, 0x327cb273, 0x813f3979, 0x21165458, 0x54b40b20, 0xd8d726b7, 0x3782dacf, 0x8e1bc9bf, 0x246139cb, 0x5d21dba0, 0xee6b2800, 0x3d090000, 0x9c400000, 0x28000000, 0x66666666, 0x1a36e2eb, 0x431bde83, 0xabcc7712, 0x2bfaffc3, 0x709709a1, 0x1cd2b298, 0x49c97747, 0xbce50865, 0x305b6680, 0x7bcb43d7, 0x1fb0f6be, 0x51212ffc, }; // base 10 exponents for 2 ^ -150 through 2 ^ 98, at intervals of 2 ^ 8 private static final byte[] pow2x = { -45, -42, -40, -37, -35, -33, -30, -28, -25, -23, -21, -18, -16, -13, -11, -9, -6, -4, -1, 1, 4, 6, 8, 11, 13, 16, 18, 20, 23, 25, 28, 30, }; /** * Mimics * <a href="http://java.sun.com/j2se/1.4.2/docs/api/java/lang/Float.html#toString(float)">Float.toString(float)</a>. * <p> * <b>This implementation is known to be inaccurate, and * does not always return the same value as * <code>Float.toString</code>.</b> However the difference should be no * greater than 1 ulp. */ public static String toString(int f) { if (isNaN(f)) { return "NaN"; } boolean n = unpackSign(f); StringBuffer sb = new StringBuffer(15); if (n) { sb.append('-'); } if (isZero(f)) { sb.append("0.0"); return sb.toString(); } else if (isInfinite(f)) { sb.append("Infinity"); return sb.toString(); } // convert from base 2 to base 10 int base2x = unpackExponent(f); int base2m = unpackMantissa(f); int idx = base2x + 150; int dx = idx & 7; base2m <<= dx; idx >>= 3; int base10x = pow2x[idx]; while (base2m <= 0xccccccc) { base2m = (base2m << 3) + (base2m << 1); // base2m *= 10; base10x--; } long base10ml = base2m * (pow2m[idx] & 0xffffffffL); int base10m = (int) (base10ml >>> 32); if ((base10ml << 32) < 0) { base10m++; } // reduce the number of digits in m10 boolean roundedUp = false; while (true) { int r = base10m % 10; int mt = base10m / 10; int xt = base10x + 1; if (r != 0) { if ((r > 5) || ((r == 5) && (! roundedUp))) { roundedUp = true; mt++; } else { roundedUp = false; } int ft = decToFloat(n, xt, mt); if (ft != f) { if (roundedUp) { mt--; } else { mt++; } roundedUp ^= true; ft = decToFloat(n, xt, mt); if (ft != f) { break; } } } base10m = mt; base10x = xt; } // convert to string String s = Integer.toString(base10m); base10x += s.length() - 1; boolean scientific = ((base10x < -3) || (base10x >= 7)); int dp; // index of decimal point in final string if (scientific) { dp = 1; } else { dp = base10x + 1; if (dp < 1) { sb.append('0'); } } for (int i=0; i<dp; i++) { if (i < s.length()) { sb.append(s.charAt(i)); } else { sb.append('0'); } } sb.append('.'); if (dp >= s.length()) { sb.append('0'); } else { for (int i=dp; i<s.length(); i++) { if (i < 0) { sb.append('0'); } else { sb.append(s.charAt(i)); } } } if (scientific) { sb.append('E'); sb.append(Integer.toString(base10x)); } return sb.toString(); } ///////////////////////////////////////////////////////////////////////////// // Instance members ///////////////////////////////////////////////////////////////////////////// private final int value; ///////////////////////////////////////////////////////////////////////////// // Constructors ///////////////////////////////////////////////////////////////////////////// /** * Constructs a newly-allocated <code>MicroFloat</code> object that represents * the argument. * * @param f the <code>float</code> value to be represented by the <code>MicroFloat</code>. */ public MicroFloat(int f) { // canonicalize NaN values so that hashCode() and equals() can be simpler if (isNaN(f)) { f = NaN; } value = f; } /** * Constructs a newly-allocated <code>MicroFloat</code> object that represents * the argument. * * @param s a <code>String</code> to be converted to a <code>MicroFloat</code>. * @throws NumberFormatException if the <code>String</code> does not contain a * parsable number. * @see #parseFloat(String) */ public MicroFloat(String s) { this(parseFloat(s)); } ///////////////////////////////////////////////////////////////////////////// // Instance methods ///////////////////////////////////////////////////////////////////////////// /** * Returns the <code>float</code> value of this <code>MicroFloat</code> * object. */ public int floatValue() { return value; } /** * Returns a String object representing this MicroFloat's value. * Equivalent to <code>toString(floatValue())</code>. * * @see #toString(int) */ public String toString() { return toString(value); } /** * Returns a hash code for this <code>MicroFloat</code> object. * Equivalent to floatValue(). */ public int hashCode() { return value; } /** * Compares this object against the specified object. * Equivalent to <code>((obj instanceof MicroFloat) && (compare(((MicroFloat) obj).floatValue(), floatValue()) == 0))</code> * * @see #compare(int, int) */ public boolean equals(Object obj) { return ((obj instanceof MicroFloat) && (((MicroFloat) obj).value == value)); } }