All Downloads are FREE. Search and download functionalities are using the official Maven repository.

lecho.lib.hellocharts.util.FloatUtils Maven / Gradle / Ivy

package lecho.lib.hellocharts.util;

public class FloatUtils {
    public static final int POW10[] = {1, 10, 100, 1000, 10000, 100000, 1000000};

    /**
     * Returns next bigger float value considering precision of the argument.
     */
    public static float nextUpF(float f) {
        if (Float.isNaN(f) || f == Float.POSITIVE_INFINITY) {
            return f;
        } else {
            f += 0.0f;
            return Float.intBitsToFloat(Float.floatToRawIntBits(f) + ((f >= 0.0f) ? +1 : -1));
        }
    }

    /**
     * Returns next smaller float value considering precision of the argument.
     */
    public static float nextDownF(float f) {
        if (Float.isNaN(f) || f == Float.NEGATIVE_INFINITY) {
            return f;
        } else {
            if (f == 0.0f) {
                return -Float.MIN_VALUE;
            } else {
                return Float.intBitsToFloat(Float.floatToRawIntBits(f) + ((f > 0.0f) ? -1 : +1));
            }
        }
    }

    /**
     * Returns next bigger double value considering precision of the argument.
     */
    public static double nextUp(double d) {
        if (Double.isNaN(d) || d == Double.POSITIVE_INFINITY) {
            return d;
        } else {
            d += 0.0;
            return Double.longBitsToDouble(Double.doubleToRawLongBits(d) + ((d >= 0.0) ? +1 : -1));
        }
    }

    /**
     * Returns next smaller float value considering precision of the argument.
     */
    public static double nextDown(double d) {
        if (Double.isNaN(d) || d == Double.NEGATIVE_INFINITY) {
            return d;
        } else {
            if (d == 0.0f) {
                return -Float.MIN_VALUE;
            } else {
                return Double.longBitsToDouble(Double.doubleToRawLongBits(d) + ((d > 0.0f) ? -1 : +1));
            }
        }
    }

    /**
     * Method checks if two float numbers are similar.
     */
    public static boolean almostEqual(float a, float b, float absoluteDiff, float relativeDiff) {
        float diff = Math.abs(a - b);
        if (diff <= absoluteDiff) {
            return true;
        }

        a = Math.abs(a);
        b = Math.abs(b);
        float largest = (a > b) ? a : b;

        if (diff <= largest * relativeDiff) {
            return true;
        }

        return false;
    }

    /**
     * Rounds the given number to the given number of significant digits. Based on an answer on Stack Overflow.
     */
    public static float roundToOneSignificantFigure(double num) {
        final float d = (float) Math.ceil((float) Math.log10(num < 0 ? -num : num));
        final int power = 1 - (int) d;
        final float magnitude = (float) Math.pow(10, power);
        final long shifted = Math.round(num * magnitude);
        return shifted / magnitude;
    }

    /**
     * Formats a float value to the given number of decimals. Returns the length of the string. The string begins at
     * [endIndex] - [return value] and ends at [endIndex]. It's up to you to check indexes correctness.
     * Parameter [endIndex] can be helpful when you want to append some text to formatted value.
     *
     * @return number of characters of formatted value
     */
    public static int formatFloat(final char[] formattedValue, float value, int endIndex, int digits, char separator) {
        if (digits >= POW10.length) {
            formattedValue[endIndex - 1] = '.';
            return 1;
        }
        boolean negative = false;
        if (value == 0) {
            formattedValue[endIndex - 1] = '0';
            return 1;
        }
        if (value < 0) {
            negative = true;
            value = -value;
        }
        if (digits > POW10.length) {
            digits = POW10.length - 1;
        }
        value *= POW10[digits];
        long lval = Math.round(value);
        int index = endIndex - 1;
        int charsNumber = 0;
        while (lval != 0 || charsNumber < (digits + 1)) {
            int digit = (int) (lval % 10);
            lval = lval / 10;
            formattedValue[index--] = (char) (digit + '0');
            charsNumber++;
            if (charsNumber == digits) {
                formattedValue[index--] = separator;
                charsNumber++;
            }
        }
        if (formattedValue[index + 1] == separator) {
            formattedValue[index--] = '0';
            charsNumber++;
        }
        if (negative) {
            formattedValue[index--] = '-';
            charsNumber++;
        }
        return charsNumber;
    }

    /**
     * Computes the set of axis labels to show given start and stop boundaries and an ideal number of stops between
     * these boundaries.
     *
     * @param start     The minimum extreme (e.g. the left edge) for the axis.
     * @param stop      The maximum extreme (e.g. the right edge) for the axis.
     * @param steps     The ideal number of stops to create. This should be based on available screen space; the more
     *                  space
     *                  there is, the more stops should be shown.
     * @param outValues The destination {@link AxisAutoValues} object to populate.
     */
    public static void computeAutoGeneratedAxisValues(float start, float stop, int steps, AxisAutoValues outValues) {
        double range = stop - start;
        if (steps == 0 || range <= 0) {
            outValues.values = new float[]{};
            outValues.valuesNumber = 0;
            return;
        }

        double rawInterval = range / steps;
        double interval = roundToOneSignificantFigure(rawInterval);
        double intervalMagnitude = Math.pow(10, (int) Math.log10(interval));
        int intervalSigDigit = (int) (interval / intervalMagnitude);
        if (intervalSigDigit > 5) {
            // Use one order of magnitude higher, to avoid intervals like 0.9 or 90
            interval = Math.floor(10 * intervalMagnitude);
        }

        double first = Math.ceil(start / interval) * interval;
        double last = nextUp(Math.floor(stop / interval) * interval);

        double intervalValue;
        int valueIndex;
        int valuesNum = 0;
        for (intervalValue = first; intervalValue <= last; intervalValue += interval) {
            ++valuesNum;
        }

        outValues.valuesNumber = valuesNum;

        if (outValues.values.length < valuesNum) {
            // Ensure stops contains at least numStops elements.
            outValues.values = new float[valuesNum];
        }

        for (intervalValue = first, valueIndex = 0; valueIndex < valuesNum; intervalValue += interval, ++valueIndex) {
            outValues.values[valueIndex] = (float) intervalValue;
        }

        if (interval < 1) {
            outValues.decimals = (int) Math.ceil(-Math.log10(interval));
        } else {
            outValues.decimals = 0;
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy