Para el siguiente fragmento de código:
for (let i = 0; i < 60; i++) { let count = 0 const num = Math.pow(2, i) - 1 let n = num while (n > 0) { n = n & (n - 1) count++ } console.log(`The binary representation of the number 2^${i}-1 contains ${count} '1', binary: ${num.toString(2)}`) }La salida es:
si 0 <= i <= 31 entonces count igual a i lo cual es correcto
si 32 <= i entonces cuento igual a 1 . Esto es claramente incorrecto
¿Por qué la cuenta es igual a 1 cuando i es [32, +∞] ?
¿Por qué num.toString(2) siempre es incorrecto cuando i es mayor que 32?
ejemplo:
ies54The binary representation of the number 2^54-1 contains 1 '1', binary: 1000000000000000000000000000000000000000000000000000000
Al usar operadores bit a bit en JavaScript, el
... los operandos se convierten en enteros de treinta y dos bits y se expresan mediante una serie de bits (ceros y unos). Los números con más de 32 bits obtienen sus bits más significativos descartados.
Entonces, al usar el operador & , los valores hasta 2**32 - 1 producirán el resultado correcto y los valores posteriores producirán resultados inesperados ya que el resultado de Math.pow(2, i) - 1 se corta durante la operación & .
Mientras que la especificación define el tipo Número como:
valores IEEE 754-2019 de formato de 64 bits de doble precisión
https://262.ecma-international.org/12.0/#sec-ecmascript-language-types-number-type
En realidad, diferentes motores js generalmente tratarán los números enteros de manera diferente.
Por ejemplo, V8, el motor JS de Chrome cambiará el tipo de datos subyacente según sea necesario de más simple a más complejo:
ECMAScript estandariza los números como valores de punto flotante de 64 bits, también conocidos como punto flotante de doble precisión o Float64. Sin embargo, eso no significa que los motores de JavaScript almacenen números en la representación de Float64 todo el tiempo; hacerlo sería terriblemente ineficiente. Los motores pueden elegir otras representaciones internas, siempre que el comportamiento observable coincida exactamente con Float64.
La mayoría de los números en las aplicaciones de JavaScript del mundo real resultan ser índices de matriz ECMAScript válidos, es decir, valores enteros en el rango de 0 a 2³²−2.
Los motores de JavaScript pueden elegir una representación en memoria óptima para dichos números para optimizar el código que accede a los elementos de la matriz por índice. Para que el procesador realice la operación de acceso a la memoria, el índice de matriz debe estar disponible en complemento a dos. En cambio, representar los índices de matriz como Float64 sería un desperdicio, ya que el motor tendría que convertir de un lado a otro entre Float64 y complemento a dos cada vez que alguien acceda a un elemento de matriz.
La representación en complemento a dos de 32 bits no solo es útil para operaciones de matrices. En general, los procesadores ejecutan operaciones con enteros mucho más rápido que las operaciones con punto flotante.
Eso significa que mientras usa números enteros simples, V8 los almacenará como un número de 32 bits en complemento a dos.
Dicho esto, por el comportamiento que está viendo en su ejemplo de código, la respuesta de Salman A lo aborda mejor que yo.