killbill-aplcache

usage: initial import of HalfFloat Half-precision floating-point

7/28/2012 4:10:57 PM

Details

diff --git a/usage/src/main/java/com/ning/billing/usage/timeline/samples/HalfFloat.java b/usage/src/main/java/com/ning/billing/usage/timeline/samples/HalfFloat.java
new file mode 100644
index 0000000..90f9e77
--- /dev/null
+++ b/usage/src/main/java/com/ning/billing/usage/timeline/samples/HalfFloat.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright 2010-2012 Ning, Inc.
+ *
+ * Ning licenses this file to you under the Apache License, version 2.0
+ * (the "License"); you may not use this file except in compliance with the
+ * License.  You may obtain a copy of the License at:
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
+ * License for the specific language governing permissions and limitations
+ * under the License.
+ */
+
+package com.ning.billing.usage.timeline.samples;
+
+public class HalfFloat {
+
+    private final short halfFloat;
+
+    public HalfFloat(final float input) {
+        halfFloat = (short)fromFloat(input);
+    }
+
+    public float getFloat() {
+        return toFloat((int)halfFloat);
+    }
+
+    // These two static methods were pinched from http://stackoverflow.com/questions/6162651/half-precision-floating-point-in-java/6162687#6162687
+    // The last comment on that page is the author saying "I hereby commit these to the public domain"
+
+    // Ignores the higher 16 bits
+    public static float toFloat(final int hbits) {
+        int mant = hbits & 0x03ff;                                   // 10 bits mantissa
+        int exp =  hbits & 0x7c00;                                   // 5 bits exponent
+        if (exp == 0x7c00) {                                         // NaN/Inf
+            exp = 0x3fc00;                                           // -> NaN/Inf
+        }
+        else if (exp != 0) {                                         // normalized value
+            exp += 0x1c000;                                          // exp - 15 + 127
+            if(mant == 0 && exp > 0x1c400) {                         // smooth transition
+                return Float.intBitsToFloat((hbits & 0x8000) << 16 | exp << 13 | 0x3ff);
+            }
+        }
+        else if (mant != 0) {                                        // && exp==0 -> subnormal
+            exp = 0x1c400;                                           // make it normal
+            do {
+                mant <<= 1;                                          // mantissa * 2
+                exp -= 0x400;                                        // decrease exp by 1
+            } while ((mant & 0x400) == 0);                           // while not normal
+            mant &= 0x3ff;                                           // discard subnormal bit
+        }                                                            // else +/-0 -> +/-0
+        return Float.intBitsToFloat(                                 // combine all parts
+            (hbits & 0x8000) << 16                                   // sign  << (31 - 15)
+            | (exp | mant) << 13);                                   // value << (23 - 10)
+    }
+
+    // returns all higher 16 bits as 0 for all results
+    public static int fromFloat(final float fval) {
+        final int fbits = Float.floatToIntBits(fval);
+        final int sign = fbits >>> 16 & 0x8000;                      // sign only
+        int val = (fbits & 0x7fffffff) + 0x1000;                     // rounded value
+
+        if (val >= 0x47800000) {                                     // might be or become NaN/Inf
+            if ((fbits & 0x7fffffff) >= 0x47800000) {                // is or must become NaN/Inf
+
+                if (val < 0x7f800000) {                              // was value but too large
+                    return sign | 0x7c00;                            // make it +/-Inf
+                }
+                return sign | 0x7c00 |                               // remains +/-Inf or NaN
+                    (fbits & 0x007fffff) >>> 13;                     // keep NaN (and Inf) bits
+            }
+            return sign | 0x7bff;                                    // unrounded not quite Inf
+        }
+        if(val >= 0x38800000)                                        // remains normalized value
+            return sign | val - 0x38000000 >>> 13;                   // exp - 127 + 15
+        if(val < 0x33000000)                                         // too small for subnormal
+            return sign;                                             // becomes +/-0
+        val = (fbits & 0x7fffffff) >>> 23;                           // tmp exp for subnormal calc
+        return sign | ((fbits & 0x7fffff | 0x800000)                 // add subnormal bit
+             + (0x800000 >>> val - 102) >>> 126 - val);              // round depending on cut off; div by 2^(1-(exp-127+15)) and >> 13 | exp=0
+    }
+
+    private static String describe(final int halfFloat) {
+        final int sign = (halfFloat >> 15) & 1;
+        final int exponent = (halfFloat >> 10) & 0x1f;
+        final double fraction = (double)(0x400 + (halfFloat & 0x3ff)) / (1.0 * 0x400);
+        final double product = fraction * Math.pow(2.0, exponent - 15) * (sign == 0 ? 1.0 : -1.0);
+        return String.format("HalfFloat %f, representation %x, sign %d, exponent 0x%x == 2**%d, fraction %f, product %f",
+                toFloat(halfFloat),
+                halfFloat,
+                sign,
+                exponent,
+                exponent - 15,
+                fraction,
+                product);
+    }
+
+    public static void main(String[] args) {
+        System.out.printf("%f double-converted = %f\n", 200.0, toFloat(fromFloat((float)200.0)));
+        System.out.printf("%s\n", describe(fromFloat(200.0f)));
+    }
+}