org.apache.lucene.util.SmallFloat.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.lucene.util.SmallFloat.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF 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 org.apache.lucene.util;

/** Floating point numbers smaller than 32 bits.
 *
 * @lucene.internal
 */
public class SmallFloat {

    /** No instance */
    private SmallFloat() {
    }

    /** Converts a 32 bit float to an 8 bit float.
     * <br>Values less than zero are all mapped to zero.
     * <br>Values are truncated (rounded down) to the nearest 8 bit value.
     * <br>Values between zero and the smallest representable value
     *  are rounded up.
     *
     * @param f the 32 bit float to be converted to an 8 bit float (byte)
     * @param numMantissaBits the number of mantissa bits to use in the byte, with the remainder to be used in the exponent
     * @param zeroExp the zero-point in the range of exponent values
     * @return the 8 bit float representation
     */
    public static byte floatToByte(float f, int numMantissaBits, int zeroExp) {
        // Adjustment from a float zero exponent to our zero exponent,
        // shifted over to our exponent position.
        int fzero = (63 - zeroExp) << numMantissaBits;
        int bits = Float.floatToRawIntBits(f);
        int smallfloat = bits >> (24 - numMantissaBits);
        if (smallfloat <= fzero) {
            return (bits <= 0) ? (byte) 0 // negative numbers and zero both map to 0 byte
                    : (byte) 1; // underflow is mapped to smallest non-zero number.
        } else if (smallfloat >= fzero + 0x100) {
            return -1; // overflow maps to largest number
        } else {
            return (byte) (smallfloat - fzero);
        }
    }

    /** Converts an 8 bit float to a 32 bit float. */
    public static float byteToFloat(byte b, int numMantissaBits, int zeroExp) {
        // on Java1.5 & 1.6 JVMs, prebuilding a decoding array and doing a lookup
        // is only a little bit faster (anywhere from 0% to 7%)
        if (b == 0)
            return 0.0f;
        int bits = (b & 0xff) << (24 - numMantissaBits);
        bits += (63 - zeroExp) << 24;
        return Float.intBitsToFloat(bits);
    }

    //
    // Some specializations of the generic functions follow.
    // The generic functions are just as fast with current (1.5)
    // -server JVMs, but still slower with client JVMs.
    //

    /** floatToByte(b, mantissaBits=3, zeroExponent=15)
     * <br>smallest non-zero value = 5.820766E-10
     * <br>largest value = 7.5161928E9
     * <br>epsilon = 0.125
     */
    public static byte floatToByte315(float f) {
        int bits = Float.floatToRawIntBits(f);
        int smallfloat = bits >> (24 - 3);
        if (smallfloat <= ((63 - 15) << 3)) {
            return (bits <= 0) ? (byte) 0 : (byte) 1;
        }
        if (smallfloat >= ((63 - 15) << 3) + 0x100) {
            return -1;
        }
        return (byte) (smallfloat - ((63 - 15) << 3));
    }

    /** byteToFloat(b, mantissaBits=3, zeroExponent=15) */
    public static float byte315ToFloat(byte b) {
        // on Java1.5 & 1.6 JVMs, prebuilding a decoding array and doing a lookup
        // is only a little bit faster (anywhere from 0% to 7%)
        if (b == 0)
            return 0.0f;
        int bits = (b & 0xff) << (24 - 3);
        bits += (63 - 15) << 24;
        return Float.intBitsToFloat(bits);
    }

    /** Float-like encoding for positive longs that preserves ordering and 4 significant bits. */
    public static int longToInt4(long i) {
        if (i < 0) {
            throw new IllegalArgumentException("Only supports positive values, got " + i);
        }
        int numBits = 64 - Long.numberOfLeadingZeros(i);
        if (numBits < 4) {
            // subnormal value
            return Math.toIntExact(i);
        } else {
            // normal value
            int shift = numBits - 4;
            // only keep the 5 most significant bits
            int encoded = Math.toIntExact(i >>> shift);
            // clear the most significant bit, which is implicit
            encoded &= 0x07;
            // encode the shift, adding 1 because 0 is reserved for subnormal values
            encoded |= (shift + 1) << 3;
            return encoded;
        }
    }

    /**
     * Decode values encoded with {@link #longToInt4(long)}.
     */
    public static final long int4ToLong(int i) {
        long bits = i & 0x07;
        int shift = (i >>> 3) - 1;
        long decoded;
        if (shift == -1) {
            // subnormal value
            decoded = bits;
        } else {
            // normal value
            decoded = (bits | 0x08) << shift;
        }
        return decoded;
    }

    private static final int MAX_INT4 = longToInt4(Integer.MAX_VALUE);
    private static final int NUM_FREE_VALUES = 255 - MAX_INT4;

    /**
     * Encode an integer to a byte. It is built upon {@link #longToInt4(long)}
     * and leverages the fact that {@code longToInt4(Integer.MAX_VALUE)} is
     * less than 255 to encode low values more accurately.
     */
    public static byte intToByte4(int i) {
        if (i < 0) {
            throw new IllegalArgumentException("Only supports positive values, got " + i);
        }
        if (i < NUM_FREE_VALUES) {
            return (byte) i;
        } else {
            return (byte) (NUM_FREE_VALUES + longToInt4(i - NUM_FREE_VALUES));
        }
    }

    /**
     * Decode values that have been encoded with {@link #intToByte4(int)}.
     */
    public static int byte4ToInt(byte b) {
        int i = Byte.toUnsignedInt(b);
        if (i < NUM_FREE_VALUES) {
            return i;
        } else {
            long decoded = NUM_FREE_VALUES + int4ToLong(i - NUM_FREE_VALUES);
            return Math.toIntExact(decoded);
        }
    }
}