📄 microfloat.java
字号:
// $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);
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -