为什么 0 的偏移会截断小数
Why does a shift by 0 truncate the decimal?
我最近发现了这段JavaScript代码:
Math.random() * 0x1000000 << 0
我知道第一部分只是生成一个介于 0 和 0x1000000 之间的随机数(== 16777216(。
但第二部分似乎很奇怪。执行位移 0 有什么意义?我不认为它会做任何事情。然而,经过进一步调查,我注意到 0 的偏移似乎截断了数字的小数部分。此外,无论是右移、左移,甚至是无符号的右移都无关紧要。
> 10.12345 << 0
10
> 10.12345 >> 0
10
> 10.12345 >>> 0
10
我用Firefox和Chrome测试了,行为是一样的。那么,这种观察的原因是什么呢?它只是JavaScript的细微差别,还是也出现在其他语言中?我以为我理解了位移,但这让我感到困惑。
你是对的;它用于截断值。
>>
工作的原因是因为它只对 32 位整数进行操作,因此该值被截断。(在此类情况下也经常使用它而不是Math.floor
因为按位运算符的优先级较低,因此可以避免括号的混乱。
由于它只对 32 位整数进行操作,因此它也相当于舍入后带有 0xffffffff
的掩码。所以:
0x110000000 // 4563402752
0x110000000 >> 0 // 268435456
0x010000000 // 268435456
但这不是预期行为的一部分,因为Math.random()
将返回一个介于 0 和 1 之间的值。
此外,它与 | 0
执行相同的操作,后者更常见。
Math.random()
返回一个介于 0(含(和 1(不包括(之间的数字。将此数字乘以整数得到具有小数部分的数字。<<
运算符是消除小数部分的快捷方式:
所有按位运算符的操作数都转换为有符号 32 位 大端序和二进制补码格式的整数。
上述语句意味着 JavaScript 引擎将隐式地将运算符<<
的两个操作数转换为 32 位整数;对于数字,它通过砍掉小数部分来实现(不适合 32 位整数范围的数字比小数部分更松散(。
它只是JavaScript的细微差别,还是发生在其他 还有语言?
您会注意到松散类型语言中的类似行为。例如 PHP:
var_dump(1234.56789 << 0);
// int(1234)
对于强类型语言,程序通常会拒绝编译。C# 像这样抱怨:
Console.Write(1234.56789 << 0);
// error CS0019: Operator '<<' cannot be applied to operands of type 'double' and 'int'
对于这些语言,您已经有类型转换运算符:
Console.Write((int)1234.56789);
// 1234
来自 Mozilla 的按位运算符文档(包括移位运算符(
所有按位运算符的操作数都以大端顺序和二进制补码格式转换为有符号 32 位整数。
所以基本上代码使用移位运算符的那个有点偶然的方面作为它由于移位 0 位而做的唯一重要事情。哎呀。
它只是JavaScript的细微差别,还是也出现在其他语言中?
当然,我不能代表所有语言,但Java和C#都不允许double
值作为左操作数和移位运算符。
根据 ECMAScript 语言规范:
http://ecma-international.org/ecma-262/5.1/#sec-11.7.1
生产 ShiftExpression: ShiftExpression>> AdditiveExpression 评估如下:
- 让 lref 成为评估 ShiftExpression 的结果。
- 让 lval 成为 GetValue(lref(。
- 让 rref 成为评估 AdditiveExpression 的结果。
- 让 rval 成为 GetValue(rref(。
- 设 lnum 为 ToInt32(lval(。
- 设 rnum 为 ToUint32(rval(。
- 让 shiftCount 成为屏蔽除最低有效 5 位之外的所有 rnum 的结果,即计算 rnum & 0x1F。
- 返回通过 shiftCount 位执行 lnum 的符号扩展右移的结果。传播最高有效位。这 结果是一个有符号的 32 位整数。
您观察到的行为在 ECMA-262 标准中定义
以下是<<
左移运算符规范的摘录:
生产 ShiftExpression: ShiftExpression <<AdditiveExpression 评估如下:
- 让 lref 成为评估 ShiftExpression 的结果。
- 让 lval 成为 GetValue(lref(。
- 让 rref 成为评估 AdditiveExpression 的结果。
- 让 rval 成为 GetValue(rref(。
- 设 lnum 为 ToInt32(lval(。
- 设 rnum 为 ToUint32(rval(。
- 让 shiftCount 成为屏蔽除最低有效 5 位之外的所有 rnum 的结果,即计算 rnum & 0x1F。
- 返回左移 lnum 按 shiftCount 位的结果。结果是一个有符号的 32 位整数。
如您所见,两个操作数都被转换为 32 位整数。因此,小数部分消失了。
这同样适用于其他位移运算符。您可以在我链接到的文档的第 11.7 节按位移位运算符中找到它们各自的描述。
在这种情况下,执行移位的唯一效果是类型转换。 Math.random()
返回浮点值。
- 为什么不't Javascript对我的输入值进行了一些重新检查
- 为什么“;未定义的“;在JavaScript中结束循环
- 为什么这在IE中的工作方式与在Firefox中不同
- 货币代码为欧元-金额的格式不应包含小数
- 知道为什么我的旋转木马不会自动更改图片吗
- 为什么会出现错误;未捕获的类型错误:undefined不是函数;
- 为什么在单独的函数中应用时转换会闪烁/断断续续(D3)
- 为什么在变形之前不缺少Fx
- 为什么JavaScript在for循环为3时向所有4发出警报
- 为什么不是't窗口.恢复正常工作吗?(javascript/jquery)
- 为什么Airbnb风格指南说不鼓励依赖函数名称推断
- 为什么要使用0>javascript中的0
- 为什么无法在TypeScript中导出类实例
- 为什么grunt contrib connect的中间件选项的第三个参数是未定义的
- 为什么我的d3.jsselectAll+过滤器没有过滤
- 为什么HTML5拖放的目标是孩子?(可排序列表)
- 为什么忽略了eval()代码中的语法错误
- 为什么 0 的偏移会截断小数
- 为什么JavaScript列表中的多个小数会在元素list'后抛出'错误
- 为什么javascript接受小数作为整数