From ca6e325b74522ea4dce4d5feb4e45547c724a029 Mon Sep 17 00:00:00 2001 From: konsoletyper Date: Sat, 8 Mar 2014 00:17:25 +0400 Subject: [PATCH] Completes java.lang.Float implementation --- .../org/teavm/classlib/java/lang/TDouble.java | 6 +- .../org/teavm/classlib/java/lang/TFloat.java | 145 ++++++++++++++++++ .../org/teavm/classlib/java/lang/TMath.java | 37 ++++- .../teavm/classlib/java/lang/FloatTest.java | 33 ++++ 4 files changed, 217 insertions(+), 4 deletions(-) diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java index dd0bf82ad..feeb56d86 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TDouble.java @@ -260,7 +260,7 @@ public class TDouble extends TNumber implements TComparable { } else { doubleMantissa = abs * 0x1p1022 * binaryExponent(negExp - 1022); } - long mantissa = (long)(doubleMantissa) & 0xFFFFFFFFFFFFFL; + long mantissa = (long)(doubleMantissa + 0.5) & 0xFFFFFFFFFFFFFL; return mantissa | ((exp + 1023L) << 52) | (value < 0 ? (1L << 63) : 0); } @@ -275,7 +275,7 @@ public class TDouble extends TNumber implements TComparable { } } boolean negative = (bits & (1 << 63)) != 0; - int rawExp = (int)((bits >> 52) & 0x7FFL) - 1023; + int rawExp = (int)((bits >> 52) & 0x7FFL); long mantissa = bits & 0xFFFFFFFFFFFFFL; if (rawExp == 0) { mantissa <<= 1; @@ -350,7 +350,7 @@ public class TDouble extends TNumber implements TComparable { return new TString(buffer, 0, sz); } - public static double binaryExponent(int n) { + private static double binaryExponent(int n) { double result = 1; if (n >= 0) { double d = 2; diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java index 511dfe5c8..86aea46e4 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TFloat.java @@ -89,6 +89,11 @@ public class TFloat extends TNumber implements TComparable { return other instanceof TFloat && ((TFloat)other).value == value; } + @Override + public int hashCode() { + return floatToIntBits(value); + } + @GeneratedBy(FloatNativeGenerator.class) public static native boolean isNaN(float v); @@ -231,4 +236,144 @@ public class TFloat extends TNumber implements TComparable { public int compareTo(TFloat other) { return compare(value, other.value); } + + public static int floatToRawIntBits(float value) { + return floatToIntBits(value); + } + + public static int floatToIntBits(float value) { + if (value == POSITIVE_INFINITY) { + return 0x7F800000; + } else if (value == NEGATIVE_INFINITY) { + return 0xFF800000; + } else if (isNaN(value)) { + return 0x7FC00000; + } + float abs = TMath.abs(value); + int exp = TMath.getExponent(abs); + int negExp = -exp + 23; + if (exp < -126) { + exp = -127; + negExp = 126 + 23; + } + float doubleMantissa; + if (negExp <= 126) { + doubleMantissa = abs * binaryExponent(negExp); + } else { + doubleMantissa = abs * 0x1p126f * binaryExponent(negExp - 126); + } + int mantissa = (int)(doubleMantissa + 0.5f) & 0x7FFFFF; + return mantissa | ((exp + 127) << 23) | (value < 0 ? (1 << 31) : 0); + } + + public static float intBitsToFloat(int bits) { + if ((bits & 0x7F800000) == 0x7F800000) { + if (bits == 0x7F800000) { + return POSITIVE_INFINITY; + } else if (bits == 0xFF800000) { + return NEGATIVE_INFINITY; + } else { + return NaN; + } + } + boolean negative = (bits & (1 << 31)) != 0; + int rawExp = ((bits >> 23) & 0x7F8) - 127; + int mantissa = bits & 0x7FFFFF; + if (rawExp == 0) { + mantissa <<= 1; + } else { + mantissa |= (1L << 23); + } + float value = mantissa * binaryExponent(rawExp - 127 - 23); + return !negative ? value : -value; + } + + private static float binaryExponent(int n) { + float result = 1; + if (n >= 0) { + float d = 2; + while (n != 0) { + if (n % 2 != 0) { + result *= d; + } + n /= 2; + d *= d; + } + } else { + n = -n; + float d = 0.5f; + while (n != 0) { + if (n % 2 != 0) { + result *= d; + } + n /= 2; + d *= d; + } + } + return result; + } + + public static TString toHexString(float f) { + if (isNaN(f)) { + return TString.wrap("NaN"); + } else if (isInfinite(f)) { + return f > 0 ? TString.wrap("Infinity") : TString.wrap("-Infinity"); + } + char[] buffer = new char[18]; + int sz = 0; + int bits = floatToIntBits(f); + boolean subNormal = false; + int exp = ((bits >>> 23) & 0xFF) - 127; + int mantissa = (bits & 0x7FFFFF) << 1; + if (exp == -127) { + ++exp; + subNormal = true; + } + for (int i = 0; i < 6; ++i) { + int digit = mantissa & 0xF; + if (digit > 0 || sz > 0) { + buffer[sz++] = TCharacter.forDigit(digit, 16); + } + mantissa >>>= 4; + } + if (sz == 0) { + buffer[sz++] = '0'; + } + buffer[sz++] = '.'; + + buffer[sz++] = subNormal ? '0' : '1'; + buffer[sz++] = 'x'; + buffer[sz++] = '0'; + if ((bits & (1L << 31)) != 0) { + buffer[sz++] = '-'; + } + int half = sz / 2; + for (int i = 0; i < half; ++i) { + char tmp = buffer[i]; + buffer[i] = buffer[sz - i - 1]; + buffer[sz - i - 1] = tmp; + } + + buffer[sz++] = 'p'; + if (exp < 0) { + exp = -exp; + buffer[sz++] = '-'; + } + int pos = 100; + boolean first = true; + for (int i = 0; i < 3; ++i) { + int digit = exp / pos; + if (digit > 0 || !first) { + buffer[sz++] = TCharacter.forDigit(digit, 10); + first = false; + } + exp %= pos; + pos /= 10; + } + if (first) { + buffer[sz++] = '0'; + } + + return new TString(buffer, 0, sz); + } } diff --git a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java index fa15c57d2..c765ace22 100644 --- a/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java +++ b/teavm-classlib/src/main/java/org/teavm/classlib/java/lang/TMath.java @@ -242,7 +242,36 @@ public final class TMath extends TObject { } public static int getExponent(float f) { - return getExponent(f); + f = abs(f); + int exp = 0; + float[] exponents = FloatExponents.exponents; + float[] negativeExponents = FloatExponents.negativeExponents; + if (f > 1) { + int expBit = 1 << (exponents.length - 1); + for (int i = exponents.length - 1; i >= 0; --i) { + if (f >= exponents[i]) { + f *= negativeExponents[i]; + exp |= expBit; + } + expBit >>>= 1; + } + } else if (f < 1) { + int expBit = 1 << (negativeExponents.length - 1); + int offset = 0; + if (f <= 0x1p-127) { + f *= 0x1p23f; + offset = 23; + } + for (int i = negativeExponents.length - 1; i >= 0; --i) { + if (f <= negativeExponents[i]) { + f *= exponents[i]; + exp |= expBit; + } + expBit >>>= 1; + } + exp = -(exp + offset); + } + return exp; } public static double nextAfter(double start, double direction) { @@ -273,4 +302,10 @@ public final class TMath extends TObject { public static double[] negativeExponents = { 0x1p-1, 0x1p-2, 0x1p-4, 0x1p-8, 0x1p-16, 0x1p-32, 0x1p-64, 0x1p-128, 0x1p-256, 0x1p-512 }; } + + private static class FloatExponents { + public static float[] exponents = { 0x1p1f, 0x1p2f, 0x1p4f, 0x1p8f, 0x1p16f, 0x1p32f, 0x1p64f }; + public static float[] negativeExponents = { 0x1p-1f, 0x1p-2f, 0x1p-4f, 0x1p-8f, 0x1p-16f, 0x1p-32f, + 0x1p-64f }; + } } diff --git a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/FloatTest.java b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/FloatTest.java index a6954b36c..a37ddbc1b 100644 --- a/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/FloatTest.java +++ b/teavm-classlib/src/test/java/org/teavm/classlib/java/lang/FloatTest.java @@ -48,4 +48,37 @@ public class FloatTest { assertEquals(0, Double.parseDouble("00000"), 1E-12); assertEquals(0, Double.parseDouble("00000.0000"), 1E-12); } + + @Test + public void floatBitsExtracted() { + assertEquals(0x4591A2B4, Float.floatToIntBits(0x1.234567p+12f)); + } + + @Test + public void subNormalFloatBitsExtracted() { + assertEquals(0x000092, Float.floatToIntBits(0x0.000123p-126f)); + } + + @Test + public void floatBitsPacked() { + assertEquals(0x1.234567p+12f, Float.intBitsToFloat(0x4591A2B4), 1e7); + } + + @Test + public void subNormalFloatBitsPacked() { + assertEquals(0x0.000123p-126f, Float.intBitsToFloat(0x000092), 0x000008p-126); + } + + @Test + public void hexStringBuilt() { + assertEquals("0x1.23456p17", Float.toHexString(0x1.23456p17f)); + assertEquals("0x1.0p0", Float.toHexString(1)); + assertEquals("-0x1.0p0", Float.toHexString(-1)); + assertEquals("0x1.0p1", Float.toHexString(2)); + assertEquals("0x1.8p1", Float.toHexString(3)); + assertEquals("0x1.0p-1", Float.toHexString(0.5f)); + assertEquals("0x1.0p-2", Float.toHexString(0.25f)); + assertEquals("0x1.0p-126", Float.toHexString(0x1.0p-126f)); + assertEquals("0x0.001p-126", Float.toHexString(0x0.001p-126f)); + } }