Javascript。尽可能少地增加或减少浮动

Mar*_*eim 2 javascript floating-point precision rounding-error

在javascipt中,我有如下这样的av浮点数:

var a = 5.;
Run Code Online (Sandbox Code Playgroud)

现在,我想要一个新数字“ b”,它几乎比“ a”大。我可以这样做:

var b = a + 1.e-10;
Run Code Online (Sandbox Code Playgroud)

但是,如果“ a”是一个很小的数字怎么办?

var a = 5.e-20;
var b = a + 1.e-10;
Run Code Online (Sandbox Code Playgroud)

现在“ b”比“ a”大许多数量级。
另外,如果我使“ a”和“ b”之间的差异过小,则较大的“ a”可能会导致差异四舍五入。

如何使数字“ b”大于任何数字“ a”,但比大于“ a”的任何其他数字更接近“ a”,或者如何使数字“ b”小于“ a”,但比小于“ a”的任何其他数字更接近“ a”。

编辑:
太具体了:我正在寻找一个函数“ makeLarger(a)”,该函数需要一个数字“ a”并返回一个数字“ b”,其中“ b> a”将始终为true,而“ c> a” && c <b“对于任何数字” c“始终为false。还有一个类似的函数“ makeSmaller(a)”。我希望“ a”为任意数字,可以是正数,负数或零。

Sim*_*rne 5

假设它a是正数,实数,并且与次正规量(在这种情况下,大于1.0020841800044864e-292)相距足够远,那么以下方法应该起作用:

var u = Number.EPSILON/2 + Number.EPSILON*Number.EPSILON;
var b = a + a*u;
Run Code Online (Sandbox Code Playgroud)

请注意,这b = a * (1+u) 不起作用。(例如,如果a = 0.9999999999999998)。

基本思想是,浮点数之间的差距大致成比例,但只是逐步增加(对于同一binade中的所有数字而言,差距都是相同的)。因此,面临的挑战是选择u足够小的尺寸,使其适用于每个binade中的极端情况。

因此,在不失一般性的前提下,考虑a区间[1.0,2.0)中的数字就足够了。我们需要确保

Machine.EPSILON/2 < a*u < Machine.EPSILON*3/2
Run Code Online (Sandbox Code Playgroud)

这样最终的加法将沿正确的方向舍入(而不是返回a2或1的增量)。可以很直接地证明上述u定义满足这些属性。

要向下走,你可以做

var c = a - a*u;
Run Code Online (Sandbox Code Playgroud)

PS:尽管很难证明,但另一个选择是

var v = 1 - Machine.EPSILON/2;
var b = a / v; # upwards
var c = a * v; # downwards
Run Code Online (Sandbox Code Playgroud)

这具有在更大范围内工作的优势(任何正的,非超自然数)。

对于次法线,您可以添加/减去Number.MIN_VALUE,因此将所有这些组合在一起将得到:

function nextup(a) {
    var v = 1 - Number.EPSILON/2;
    if (a >= Number.MIN_VALUE / Number.EPSILON) {
        // positive normal
        return (a/v);
    } else if (a > -Number.MIN_VALUE / Number.EPSILON) {
        // subnormal or zero
        return (a+Number.MIN_VALUE);
    } else {
        // negative normal or NaN
        return (a*v);
    }
 }

 function nextdown(a) {
    var v = 1 - Number.EPSILON/2;
    if (a >= Number.MIN_VALUE / Number.EPSILON) {
        // positive normal
        return (a*v);
    } else if (a > -Number.MIN_VALUE / Number.EPSILON) {
        // subnormal or zero
        return (a-Number.MIN_VALUE);
    } else {
        // negative normal or NaN
        return (a/v);
    }
 }
Run Code Online (Sandbox Code Playgroud)