2011-06-03 10 views
6

¿Hay alguna forma de multiplicar correctamente dos enteros de 32 bits en Javascript?¿Hay alguna manera de multiplicar correctamente dos enteros de 32 bits en Javascript?

Cuando intento esto desde C utilizando long long me sale esto:

printf("0x%llx * %d = %llx\n", 0x4d98ee96ULL, 1812433253, 
     0x4d98ee96ULL * 1812433253); 
==> 0x4d98ee96 * 1812433253 = 20becd7b431e672e 

Pero a partir de Javascript el resultado es diferente:

x = 0x4d98ee97 * 1812433253; 
print("0x4d98ee97 * 1812433253 = " + x.toString(16)); 
==> 0x4d98ee97 * 1812433253 = 20becd7baf25f000 

Los ceros a la derecha me llevan a sospechar que Javascript tiene una extraña resolución entera limitada en algún lugar entre 32 y 64 bits.

¿Hay alguna manera de obtener una respuesta correcta? (Estoy usando Mozilla js-1.8.5 en x86_64 Fedora 15 en caso de que importe)

+2

FYI: es realmente alrededor [53 bits] (http://groups.google.com/group/twitter-api-announce/browse_thread/thread/6a16efa375532182?pli=1). – Thai

+1

puede usar [Math.imul] (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Math/imul) –

Respuesta

9

Esto parece hacer lo que quería sin una dependencia externa:

function multiply_uint32(a, b) { 
    var ah = (a >> 16) & 0xffff, al = a & 0xffff; 
    var bh = (b >> 16) & 0xffff, bl = b & 0xffff; 
    var high = ((ah * bl) + (al * bh)) & 0xffff; 
    return ((high << 16)>>>0) + (al * bl); 
} 

Este realiza una de 32 bits se multiplican módulo 2^32, que es la mitad inferior correcta de la computación. Una función similar podría usarse para calcular una mitad superior correcta y almacenarla en un número entero diferente (ah * bh parece correcto), pero no sucede que necesite eso.

Tenga en cuenta el cambio a cero. Sin eso, la función genera valores negativos siempre que se establece el bit alto.

+1

Si ejecuto 'multiply_uint32 (0xffffffff, 0xffffffff)' con esto, obtengo '0x100000001'. Creo que quisiste hacer otro '& 0xffffffff' después de todo el valor de retorno. –

3

estás en lo correcto. Los enteros Javascript se tratan como flotantes, que tienen poca precisión cuando se trata de ints.

en JavaScript, que es el caso que 10000000000000001%2 == 0

Un amigo también menciona que 10000000000000001 == 10000000000000000, y que esto es debido a la especificación (aunque enteros se utilizan para la optimización, la especificación todavía requiere flotar-como el comportamiento) .

Aunque una vez que estás en este territorio, ya estás casi en el límite de 64 bits de precisión int.

+1

Porque los enteros se almacenan como IEEE 754 double- flotadores de precisión (64 bits), no puede obtener más de 54 (o 53? algo así) bits de precisión, pero en los casos en que JavaScript necesita hacer cosas tipo int (indexación de matriz), desciende a 31 bits de todas formas. – Pointy

0

GWT tiene una emulación del tipo de entero largo con signo de Java (64 bits). Hice una interfaz de JavaScript para él, here's a demo. Usando los números predeterminados, puede ver que el valor es el mismo que el que obtendría en C.

El valor hexadecimal en la columna "emulación" debe corresponder con lo que ve en su depurador, pero puede Serán problemas con la representación hexadecimal ya que estoy usando JavaScript nativo para hacerlo. También se podría hacer en GWT, por supuesto, eso probablemente lo haría más correcto. Si el Número de JavaScript puede representar todas las representaciones de cadena generadas por GWT, la representación hexadecimal también debería ser correcta. Mire en la fuente para el uso. La razón por la que ({sub, mod, div, mul} ss) toman cadenas es porque no sé cómo hacer un objeto GWT Long desde JavaScript.

4

De a forum post:

no hay necesidad de hacer pequeñas cantidades, sólo asuntos de mantener el número de dígitos significativos por debajo de 53

function mult32s(n, m) //signed version 
{ 
    n |= 0; 
    m |= 0; 
    var nlo = n & 0xffff; 
    var nhi = n - nlo; 
    return ((nhi * m | 0) + (nlo * m)) | 0; 
} 

function mult32u(n, m) //unsigned version 
{ 
    n >>>= 0; 
    m >>>= 0; 
    var nlo = n & 0xffff; 
    var nhi = n - nlo; 
    return ((nhi * m >>> 0) + (nlo * m)) >>> 0; 
} 

Ambos | y >>> operadores hacen que el resultado que se convertirá a entero de 32 bits. En el primer caso, se convierte en un entero con signo, en el segundo caso se convierte en un entero sin signo.

En la línea de la multiplicación en el primer operador |/>>> hace que el resultado intermedio de 64 bits con 48 bits significand (en forma 0x NNNN NNNN NNNN 0000) para dejar caer sus bits más altas, por lo que el resultado intermedio es en forma 0x NNNN 0000 .
El segundo operador |/>>> hace que el resultado de la segunda multiplicación y adición se limite a 32 bits.

En caso de que uno de los multiplicanda es una constante que puede simplificar la multiplicación más allá:

function mult32s_with_constant(m) //signed version 
{ 
    m |= 0 
    //var n = 0x12345678; 
    var nlo = 0x00005678; 
    var nhi = 0x12340000; 
    return ((nhi * m | 0) + (nlo * m)) | 0; 
} 

O, si se sabe que el resultado va a ser inferior a 53 bits, entonces puede hacerlo solo:

function mult32s(n, m) //signed version 
{ 
    n |= 0; 
    m |= 0; 
    return (n * m) | 0; 
} 
Cuestiones relacionadas