【问题标题】:Javascript. Increase or decrease float as little as possibleJavascript。尽可能少地增加或减少浮动
【发布时间】:2018-01-18 21:18:21
【问题描述】:

在 javascipt 中,我有一个像这样的 av 浮点“a”:

var a = 5.;

现在我想要一个比“a”略大的新数字“b”。我可以这样做:

var b = a + 1.e-10;

但是如果“a”是一个非常小的数字呢?

var a = 5.e-20;
var b = a + 1.e-10;

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

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

编辑:
太具体了:我正在寻找一个函数“makeLarger(a)”,它接受一个数字“a”并返回一个数字“b”,其中“b>a”将始终计算为真,“c>a && c对于任何数字 "c",

【问题讨论】:

  • 这看起来很像 XY Problem - 你到底想完成什么?
  • 这是一个很酷的问题。相关话题:en.wikipedia.org/wiki/Machine_epsilon
  • @Adrian 我需要帮助的 X 问题是最后一段中的问题。如何使“b”大于/小于“a”,但比所有其他较大/较小的数字更接近 a。我在上一段中提供的解决方案不起作用,因为如果“a”很小,我不希望“b”比“a”大很多数量级。此外,如果“a”很大,“b”将等于“a”。示例:1.e7 + 1.e-10 == 1.e7 计算结果为真,因此如果 a 为 1.e7,我的方法将不起作用。
  • @Adrian 我正在寻找一个函数“makeLarger(a)”,它接受一个数字“a”并返回一个数字“b”,其中“b>a”将始终计算为真并且“对于任何数字“c”,c>a && c
  • 好的,但是您实际上想要完成什么?或者这纯粹是学术性的?

标签: javascript floating-point precision rounding-error


【解决方案1】:

假设a 是正数、实数并且距离次常态足够远(在这种情况下,大于1.0020841800044864e-292),那么以下应该有效:

var u = Number.EPSILON/2 + Number.EPSILON*Number.EPSILON;
var b = a + a*u;

请注意,b = a * (1+u) 不会工作。 (例如a = 0.9999999999999998)。

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

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

Machine.EPSILON/2 < a*u < Machine.EPSILON*3/2

这样最后的加法就会在正确的方向上四舍五入(而不是回到a或2个增量)。很容易证明上面定义的u 满足这些属性。

要往下走,你可以这样做

var c = a - a*u;

P.S.:另一种选择,虽然难以证明,是

var v = 1 - Machine.EPSILON/2;
var b = a / v; # upwards
var c = a * v; # downwards

这具有适用于更大范围(任何正的、非次正规的实数)的优势。

对于次正规,您只需加/减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);
    }
 }

【讨论】:

    【解决方案2】:

    这可能是非正统的,但您可以执行以下操作:

    1. 将浮点数转换为字符串,则无需关心数字的大小(在合理范围内)。
    2. 找到小数点的索引。
    3. 从末尾检索一组重要的数字(此处需要进行调整 - 类似于“直到第一对不是 0 且由 9 或诸如此类的数字”)
    4. 将它们转换为 int 并增加 1
    5. 将第一个字符串中的数字替换为第二个字符串中的数字到 var b 中
    6. 放置小数点(在前一个索引处),转换为浮点数。

    有一些微调方面需要解决,但这会完成。

    我不想为示例编写代码,但这很简单。

    【讨论】:

    • 不过,它可能不会给出想要的结果。将 1 添加到最低有效位可能会导致具有完全相同值的双精度或浮点数。只需尝试使用0.1,它作为双精度,正是0.1000000000000000055511151231257827021181583404541015625。将最终的 5 转换为 6 将在解析时再次产生相同的值。
    • FWIW,将双精度(无 NaN,无无限)转换为十六进制表示,加一然后转换回应该给出下一个可能的值。 OP 真正需要的是某种nextafter() 函数。
    【解决方案3】:

    您可以使用大数字方法——将您的数字存储为数字序列: e. G。 123... many digits here ...45[1, 2, 3, .., 4, 5]

    因此,您可以处理非常冗长的数字,并且您的浮点数非常精确。

    为此有几个 js 模块,例如http://jsfromhell.com/classes/bignumber

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2020-09-19
      • 2017-06-03
      • 2018-06-29
      • 1970-01-01
      • 2015-08-09
      • 1970-01-01
      • 1970-01-01
      • 2011-05-14
      相关资源
      最近更新 更多