如何计算`ulp`when`Math.ulp`丢失了?

时间:2014-06-08 09:28:14

标签: java math double codenameone

对于给定的double值,我需要ulp,但由于我正在为Codename ONE开发,因此未提供ulp(double)。有谁知道在Java中计算ulp的有效算法? Codename ONE仅提供Math类中的一些方法(CN1版本为javadoc),并且MathUtil填充了一些空白。

作为一种解决方法,我使用这个(不正确的)代码,直到找到可用的替换代码:

private double ulp(double y) {
    return y/1e15;
}

编辑:我“推出了自己的”并刚刚发布了review的代码。以防万一其他人需要这个。

2 个答案:

答案 0 :(得分:2)

好的,因为我没有找到工作的替代品(Apache Harmony和OpenJDK最终都使用CN1上没有的本机方法),我编写了自己的版本(针对OpenJDK版本测试的结果)。以防任何人需要它。

至于代号One:我向MathUtil类提交了一个补丁,所以希望迟早会添加补丁。

/*
 * use a precalculated value for the ulp of Double.MAX_VALUE
 */
private static final double MAX_ULP = 1.9958403095347198E292;

/**
 * Returns the size of an ulp (units in the last place) of the argument.
 * @param d value whose ulp is to be returned
 * @return size of an ulp for the argument
 */
@Override
public double ulp(double d) {
    if (Double.isNaN(d)) {
        // If the argument is NaN, then the result is NaN.
        return Double.NaN;
    }

    if (Double.isInfinite(d)) {
        // If the argument is positive or negative infinity, then the
        // result is positive infinity.
        return Double.POSITIVE_INFINITY;
    }

    if (d == 0.0) {
        // If the argument is positive or negative zero, then the result is Double.MIN_VALUE.
        return Double.MIN_VALUE;
    }

    d = Math.abs(d);
    if (d == Double.MAX_VALUE) {
        // If the argument is Double.MAX_VALUE, then the result is equal to 2^971.
        return MAX_ULP;
    }

    return nextAfter(d, Double.MAX_VALUE) - d;
}

@Override
public double copySign(double x, double y) {
    return com.codename1.util.MathUtil.copysign(x,y);
}

private boolean isSameSign(double x, double y) {
    return copySign(x, y) == x;
}

/**
 * Returns the next representable floating point number after the first
 * argument in the direction of the second argument.
 *
 * @param start starting value
 * @param direction value indicating which of the neighboring representable
 *  floating point number to return
 * @return The floating-point number next to {@code start} in the
 * direction of {@direction}.
 */
@Override
public double nextAfter(final double start, final double direction) {
    if (Double.isNaN(start) || Double.isNaN(direction)) {
        // If either argument is a NaN, then NaN is returned.
        return Double.NaN;
    }

    if (start == direction) {
        // If both arguments compare as equal the second argument is returned.
        return direction;
    }

    final double absStart = Math.abs(start);
    final double absDir = Math.abs(direction);
    final boolean toZero = !isSameSign(start, direction) || absDir < absStart;

    if (toZero) {
        // we are reducing the magnitude, going toward zero.
        if (absStart == Double.MIN_VALUE) {
            return copySign(0.0, start);
        }
        if (Double.isInfinite(absStart)) {
            return copySign(Double.MAX_VALUE, start);
        }
        return copySign(Double.longBitsToDouble(Double.doubleToLongBits(absStart) - 1L), start);
    } else {
        // we are increasing the magnitude, toward +-Infinity
        if (start == 0.0) {
            return copySign(Double.MIN_VALUE, direction);
        }
        if (absStart == Double.MAX_VALUE) {
            return copySign(Double.POSITIVE_INFINITY, start);
        }
        return copySign(Double.longBitsToDouble(Double.doubleToLongBits(absStart) + 1L), start);
    }
}

答案 1 :(得分:0)

当ULP返回给定值与下一个浮点数之差的绝对值时,我不确定为什么ULP的实现需要考虑符号和其他标准。

这是你需要做的一个例子;它是用C#编写的,但它足以让Java理解。

public static double ULP(double value)
{
    // This is actually a constant in the same static class as this method, but 
    // we put it here for brevity of this example.
    const double MaxULP = 1.9958403095347198116563727130368E+292;

    if (Double.IsNaN(value))
    {
        return Double.NaN;
    }
    else if (Double.IsPositiveInfinity(value) || Double.IsNegativeInfinity(value))
    {
        return Double.PositiveInfinity;
    }
    else if (value == 0.0)
    {
        return Double.Epsilon;    // Equivalent of Double.MIN_VALUE in Java; Double.MinValue in C# is the actual minimum value a double can hold.
    }
    else if (Math.Abs(value) == Double.MaxValue)
    {
        return MaxULP;
    }

    // All you need to understand about DoubleInfo is that it's a helper struct 
    // that provides more functionality than is used here, but in this situation, 
    // we only use the `Bits` property, which is just the double converted into a 
    // long.
    DoubleInfo info = new DoubleInfo(value);

    // This is safe because we already checked for value == Double.MaxValue.
    return Math.Abs(BitConverter.Int64BitsToDouble(info.Bits + 1) - value);
}