Fastest implementation of log2(int) and log2(float)

前端 未结 9 1484
天命终不由人
天命终不由人 2020-12-08 04:57

The question is

Are there any other (and/or faster) implementations of a basic 2log?

Applications

The log2(int) and

9条回答
  •  醉酒成梦
    2020-12-08 05:10

    There have been quite a few answers providing fast approximate approaches to log2(int) but few for log2(float), so here's two (Java implementation given) that use both a lookup table and mantissa/bit hacking:

    Fast accurate log2(float):

    /**
     * Calculate the logarithm to base 2, handling special cases.
     */
    public static float log2(float x) {
    
        final int bits = Float.floatToRawIntBits(x);
        final int e = (bits >> 23) & 0xff;
        final int m = (bits & 0x7fffff);
    
        if (e == 255) {
            if (m != 0) {
                return Float.NaN;
            }
            return ((bits >> 31) != 0) ? Float.NaN : Float.POSITIVE_INFINITY;
        }
    
        if ((bits >> 31) != 0) {
            return (e == 0 && m == 0) ? Float.NEGATIVE_INFINITY : Float.NaN;
        }
    
        return (e == 0 ? data[m >>> qm1] : e + data[((m | 0x00800000) >>> q)]);
    }
    

    Note:

    • If the argument is NaN or less than zero, then the result is NaN.
    • If the argument is positive infinity, then the result is positive infinity.
    • If the argument is positive zero or negative zero, then the result is negative infinity.

    Fast accurate log2(float) (slightly faster, no checking):

    /**
     * Calculate the logarithm using base 2. Requires the argument be finite and
     * positive.
     */
    public static float fastLog2(float x) {
        final int bits = Float.floatToRawIntBits(x);
        final int e = (bits >> 23) & 0xff;
        final int m = (bits & 0x7fffff);
        return (e == 0 ? data[m >>> qm1] : e + data[((m | 0x00800000) >>> q)]);
    }
    

    This second method forgoes the checking present in the other method and therefore has the following special cases:

    • If the argument is NaN, then the result is incorrect.
    • If the argument is negative, then the result is incorrect.
    • If the argument is positive infinity, then the result is incorrect.
    • If the argument is positive zero or negative zero, then the result is negative infinity.

    Both methods upon rely on a lookup table data (and variables q and qm1). These are populated with the following method. n defines the accuracy-space tradeoff.

    static int q, qm1;
    static float[] data;
    
    /**
     * Compute lookup table for a given base table size.
     * 
     * @param n The number of bits to keep from the mantissa. Table storage =
     *          2^(n+1) * 4 bytes, e.g. 64Kb for n=13. Must be in the range
     *          0<=n<=23
     */
    public static void populateLUT(int n) {
    
        final int size = 1 << (n + 1);
    
        q = 23 - n;
        qm1 = q - 1;
        data = new float[size];
    
        for (int i = 0; i < size; i++) {
            data[i] = (float) (Math.log(i << q) / Math.log(2)) - 150;
        }
    }
    

    populateLUT(12);
    log2(6666); // = 12.702606
    

提交回复
热议问题