Javascript 2.9999999999999>>。5.

Javascript 2.9999999999999>>。5.,javascript,bit-manipulation,precision,Javascript,Bit Manipulation,Precision,我听说你可以将数字右移0.5,而不用Math.floor()。我决定检查它的限制,以确保它是一个合适的替代品,因此我检查了以下值,并在Google Chrome中得到以下结果: 2.5 >> .5 == 2; 2.9999 >> .5 == 2; 2.999999999999999 >> .5 == 2; // 15 9s 2.9999999999999999 >> .5 == 3; // 16 9s 在做了一些修改之后,我发现在Chrome和Firefox中,当右移0.5时,可能产生2

我听说你可以将数字右移0.5,而不用Math.floor()。我决定检查它的限制,以确保它是一个合适的替代品,因此我检查了以下值,并在Google Chrome中得到以下结果:


2.5 >> .5 == 2;
2.9999 >> .5 == 2;
2.999999999999999 >> .5 == 2;  // 15 9s
2.9999999999999999 >> .5 == 3;  // 16 9s
在做了一些修改之后,我发现在Chrome和Firefox中,当右移0.5时,可能产生2的最大值是2.99999999977795539507496869191915273666381835937499999'(重复9次)。IE中的号码是2.9999999999779

我的问题是:00000000000000077795539507496869152736663818359374这个数字的意义是什么?这是一个非常奇怪的数字,它确实激起了我的好奇心

我一直在试图找到答案,或者至少是某种模式,但我认为我的问题在于我真的不理解按位运算。我在原则上理解这个想法,但将位序列移动0.5对我来说毫无意义。感谢您的帮助

对于记录,奇怪的数字序列以2^x变化。仍然正确截断的下列数字的最高可能值:

for 0: 0.9999999999999999444888487687421729788184165954589843749¯ for 1: 1.9999999999999999888977697537484345957636833190917968749¯ for 2-3: x+.99999999999999977795539507496869191527366638183593749¯ for 4-7: x+.9999999999999995559107901499373838305473327636718749¯ for 8-15: x+.999999999999999111821580299874767661094665527343749¯ ...and so forth 对于0:0.999999999999444848487687421729788184165954589843749“ 对于1:1.999999999999999888977697537484345957636833190917968749“ 对于2-3:x+.999999999999777955395074968691527366638183593749“ 对于4-7:x+.9999999999995559107901499383838305473327636718749“ 对于8-15:x+.999999999999111821580299874767661094665527343749“ 等等
这可能是我见过的最糟糕的想法。它存在的唯一可能目的是赢得一场模糊代码竞赛。您发布的长数字没有任何意义——它们是底层浮点实现的产物,通过不知多少中间层过滤。按小数字节移位是疯狂的,我很惊讶它没有引发异常——但这是Javascript,总是愿意重新定义“疯狂”

如果我是你,我会避免使用这个“功能”。其唯一值是异常错误条件的可能根本原因。使用
Math.floor()
,可怜下一个维护代码的程序员


证实了我在阅读问题时的一些怀疑:

  • 将任何分数
    x
    右移任何分数
    y
    只会截断
    x
    ,产生与
    Math.floor()
    相同的结果,同时彻底混淆读者
  • 2.999999999999777955395074968691915。。。是可以与“3”区分的最大数字。试着自己对它求值——如果你给它添加了什么,它将求值为3。这是浏览器和本地系统浮点实现的产物

我认为你的右移与此无关。您只是超出了双精度浮点常量的分辨率

镀铬:

var x = 2.999999999999999777955395074968691915273666381835937499999;
var y = 2.9999999999999997779553950749686919152736663818359375;

document.write("x=" + x);
document.write(" y=" + y);
打印输出:x=2.9999999996 y=3 警报(parseFloat(“2.999999999999777955395074968691915273666381835937499999”)

然后试试这个: 警报(parseFloat(“2.9999999999997779553950749686919152736663818359375”)

您看到的是简单的浮点不准确。有关这方面的更多信息,请参见以下示例:

基本问题是,浮点值与表示第二个数字的最接近值大于或等于3,而浮点值与第一个数字的最接近值严格小于3


至于为什么右移0.5会让一切变得正常,似乎0.5本身就是事先转换成int(0)。然后,原始的浮点(2.999…)像往常一样通过截断转换为整数。

再加上John的答案,这比数学更有效的几率非常小


我不知道JavaScript是否使用浮点数或某种无限精度库,但无论哪种方式,在这样的操作中都会出现舍入错误——即使它定义得很好。

右移运算符只对整数(两边)进行操作。所以,右移0.5位应该完全等同于右移0位。并且,在移位操作之前,左手边被转换为整数,这与Math.floor()的作用相同。

实际上,您只需在第一个操作数上执行floor(),而不进行任何浮点操作。由于左移位和右移位位运算仅对整数操作数有效,JavaScript引擎首先将两个操作数转换为整数:

2.999999 >> 0.5
变成:

Math.floor(2.999999) >> Math.floor(0.5)
这反过来又是:

2 >> 0
按0位移位意味着“不移位”,因此您将得到第一个操作数,只是被截断为整数

SpiderMonkey源代码具有:

switch (op) {
  case JSOP_LSH:
  case JSOP_RSH:
    if (!js_DoubleToECMAInt32(cx, d, &i)) // Same as Math.floor()
        return JS_FALSE;
    if (!js_DoubleToECMAInt32(cx, d2, &j)) // Same as Math.floor()
        return JS_FALSE;
    j &= 31;
    d = (op == JSOP_LSH) ? i << j : i >> j;
    break;
你会得到2.99999999999。现在尝试添加一个或多个9:

alert(2.9999999999999999);

你会得到一个3。

如果你想更深入一点,读一读“每个计算机科学家都应该知道的关于浮点运算的知识”:

我怀疑将2.9999999999997977955395074996869152736681835937499999转换为二进制表示会很有启发性。它可能与真3只有一点不同。

应该注意的是,数字“.000000000000000 77795539507496869191527366818359374”很可能是“最小的数字E,因此(1+E)>1”


我怀疑转换2.99999999999997779553950749686991915273666381835937499999 对于它的二进制表示将是很有启发性的。可能只有一点不同 从真3

猜对了,但没有雪茄。 由于双精度FP数有53位,因此3之前的最后一个FP数实际上是 (精确):2.99999999999995559107901499937383830547332763671875

但为什么呢 2.99999999999999977795
alert(2.9999999999999999);