如何获取小于/大于给定数字的最接近的浮点数

Den*_*nis 5 javascript

这是一个示例函数:

function step(x, min, max) {
  return x >= min && x <= max ? x : 0;
}

console.log(step(-3 - Number.EPSILON, -3, 5)); // Expected 0, actual -3
console.log(step(5 + Number.EPSILON, -3, 5)); // Expected 0, actual 5
Run Code Online (Sandbox Code Playgroud)

我需要检查,对于 [min, max] 间隔之外的值,它是否返回零。当然,我可以减去/添加一个更大的数字,例如 1。但我很确定,应该存在一个返回上一个/下一个浮点数的函数。您能否建议一下该功能或如何实现它?

T.J*_*der 9

并非所有相邻的可表示数字彼此之间的数学距离都相同。浮点阿尔卡纳不是我的强项,但如果你想找到下一个可表示的数字,我认为你需要不断增加你添加/减去的内容,Number.EPSILON只要你不断得到相同的数字。

非常天真的、简单化的方法看起来像这样(但请继续阅读):

// DON'T USE THIS
function next(x) {
    const ep = x < 0 ? -Number.EPSILON : Number.EPSILON;
    let adder = ep;
    let result;
    do {
        result = x + adder;
        adder += ep;
    } while (result === x);
    return result;
}

console.log(`Next for -3: ${next(-3)}`);
console.log(`Next for 5: ${next(5)}`);
Run Code Online (Sandbox Code Playgroud)

(这是根据给定数字的符号假设方向,这可能不是您真正想要的,但很容易切换。)

但是,这(至少)需要几个小时next(Number.MAX_SAFE_INTEGER)才能处理。

当我最初在上面发布我的警告时,我说更好的方法将考虑x......或者做一些旋转(这肯定会让我们进入浮点奥秘领域)......”的大小,并且你指出了Java的Math.nextAfter操作,所以我必须找出他们做了什么。事实上,这有点麻烦,而且非常简单。下面是 OpenJDK 版本的重新实现(该链接中的行号将会失效):

// A JavaScript implementation of OpenJDK's `Double.nextAfter` method.
function nextAfter(start, direction) {
    // These arrays share their underlying memory, letting us use them to do what
    // Java's `Double.doubleToRawLongBits` and `Double.longBitsToDouble` do.
    const f64 = new Float64Array(1);
    const b64 = new BigInt64Array(f64.buffer);

    // Comments from https://github.com/openjdk/jdk/blob/master/src/java.base/share/classes/java/lang/Math.java:

    /*
     * The cases:
     *
     * nextAfter(+infinity, 0)  == MAX_VALUE
     * nextAfter(+infinity, +infinity)  == +infinity
     * nextAfter(-infinity, 0)  == -MAX_VALUE
     * nextAfter(-infinity, -infinity)  == -infinity
     *
     * are naturally handled without any additional testing
     */

    /*
     * IEEE 754 floating-point numbers are lexicographically
     * ordered if treated as signed-magnitude integers.
     * Since Java's integers are two's complement,
     * incrementing the two's complement representation of a
     * logically negative floating-point value *decrements*
     * the signed-magnitude representation. Therefore, when
     * the integer representation of a floating-point value
     * is negative, the adjustment to the representation is in
     * the opposite direction from what would initially be expected.
     */

    // Branch to descending case first as it is more costly than ascending
    // case due to start != 0.0d conditional.
    if (start > direction) {
        // descending
        if (start !== 0) {
            f64[0] = start;
            const transducer = b64[0];
            b64[0] = transducer + (transducer > 0n ? -1n : 1n);
            return f64[0];
        } else {
            // start == 0.0d && direction < 0.0d
            return -Number.MIN_VALUE;
        }
    } else if (start < direction) {
        // ascending
        // Add +0.0 to get rid of a -0.0 (+0.0 + -0.0 => +0.0)
        // then bitwise convert start to integer.
        f64[0] = start + 0;
        const transducer = b64[0];
        b64[0] = transducer + (transducer >= 0n ? 1n : -1n);
        return f64[0];
    } else if (start == direction) {
        return direction;
    } else {
        // isNaN(start) || isNaN(direction)
        return start + direction;
    }
}

function test(start, direction) {
    const result = nextAfter(start, direction);
    console.log(`${start} ${direction > 0 ? "up" : "down"} is ${result}`);
}

test(-3, -Infinity);
test(5, Infinity);
test(Number.MAX_SAFE_INTEGER, Infinity);
test(Number.MAX_SAFE_INTEGER + 2, Infinity);
Run Code Online (Sandbox Code Playgroud)