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