简体   繁体   English

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

[英]How to get closest floating point number that is less/greater than a given number

Here is a sample function:这是一个示例函数:

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

I need to check, that it returns zero for values outside [min, max] interval.我需要检查,对于 [min, max] 间隔之外的值,它是否返回零。 For sure I can subtract/add a bigger number, for example 1. But I'm pretty sure, that there should exist a function returning previous/next floating point number.当然,我可以减去/添加一个更大的数字,例如 1。但我很确定,应该存在一个返回上一个/下一个浮点数的函数。 Could you please suggest the function or how to implement it?您能否建议该功能或如何实现它?

Not all adjacent representable numbers are the same mathematical distance from one another.并非所有相邻的可表示数字彼此之间的数学距离都相同。 Floating point arcana isn't my strong suit, but if you want to find the next representable number, I think you need to keep increasing what you add/subtract from it by Number.EPSILON for as long as you keep getting the same number.浮点奥术不是我的强项,但如果你想找到下一个可表示的数字,我认为你需要通过Number.EPSILON不断增加你添加/减去的内容,只要你一直得到相同的数字。

The very naive, simplistic approach would look like this (but keep reading):非常天真、简单的方法看起来像这样(但请继续阅读):

 // 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)}`);

(That's assuming direction based on the sign of the number given, which is probably not what you really want, but is easily switched up.) (这是根据给定数字的符号假设方向,这可能不是您真正想要的,但很容易切换。)

But , that would take hours (at least) to handle next(Number.MAX_SAFE_INTEGER) .但是,处理next(Number.MAX_SAFE_INTEGER)需要几个小时(至少)。

When I posted my caveat on the above originally, I said a better approach would take the magnitude of x into account "...or do bit twiddling (which definitely takes us into floating point arcana land)..." and you pointed to Java's Math.nextAfter operation, so I had to find out what they do.当我最初在上面发布我的警告时,我说一个更好的方法会考虑x的大小“......或者做一些小玩意(这肯定会让我们进入浮点奥术领域)......”你指出Java 的Math.nextAfter操作,所以我不得不找出它们的作用。 And indeed, it's bit twiddling, and it's wonderfully simple.确实,它有点小题大做,而且非常简单。 Here's a re-implementation of the OpenJDK's version from here (the line number in that link will rot):这是从此处重新实现的 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);

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM