¿Por qué la multiplicación binaria con signo (complemento a 2) tiene un procedimiento diferente al sin signo?

La multiplicación binaria en complemento a 2 no tiene el mismo procedimiento que sin signo si ambos operandos no tienen el mismo signo. ¿Cuál es la lógica detrás de eso?

¿Se aplica una consideración especial a la división también cuando realizamos la división con números en complemento a 2?

enlace útil (explica bastante bien la multiplicación). tl; dr: hay una forma de "muerte cerebral" que siempre funciona, y luego hay una forma más rápida que requiere una lógica especial.
Bueno, si usaras el mismo procedimiento, el resultado sería incorrecto.

Respuestas (2)

La multiplicación binaria en complemento a 2 no tiene el mismo procedimiento que sin signo

En módulo 2 n aritmética -1 y 2 n -1 son equivalentes. De ello se deduce que si la salida es del mismo tamaño que la entrada, entonces podemos usar un multiplicador de módulo 2 n para operaciones con y sin signo.

Sin embargo, si la salida es mayor que las entradas, esta propiedad ya no se cumple. Considere, por ejemplo, multiplicar el número de 8 bits 11111111 (255 si se interpreta como binario directo, -1 si se interpreta como complemento a 2) consigo mismo para producir un resultado de 16 bits. Para números con signo, el resultado correcto es 0000000000000001. Sin embargo, para números sin signo, el resultado correcto es 1111111000000001 (65025 en decimal)

Si quiere pensar en esto en términos de aritmética modular, puede notar que -1 y 255 son el mismo módulo 256 pero diferente módulo 65536.

Esta es la razón por la que cuando observa (por ejemplo) el conjunto de instrucciones arm, solo ve una instrucción de multiplicación 32*32->32 pero dos instrucciones de multiplicación diferentes 32*32->64.

¿Se aplica una consideración especial a la división también cuando realizamos la división con números en complemento a 2?

La división (en el sentido en que la concebimos en las computadoras) no es una operación aritmética modular. Entonces, no hay razón para esperar una equivalencia entre la división con y sin signo y, de hecho, no la hay.

Nuevamente, para dar un ejemplo, considere 11111110/00000010. En aritmética sin signo, esto daría como resultado 01111111 (127) en aritmética con signo, daría como resultado 11111111 (-1)

Al hacer sumas, por ejemplo, si comienza con n bits más n bits, puede obtener todas las respuestas con n+1 bits. Tenemos un bit de acarreo/préstamo para eso. La belleza del complemento a dos es que a - b = a + (-b) = a + ~b + 1. por lo que invierte el segundo operando y el bit de acarreo para la resta y luego lo introduce en un sumador, no se necesitan relojes adicionales . Algunas arquitecturas invierten el acarreo del msbit para que sea un bit de préstamo, algunas lo dejan como un bit de acarreo (no un bit de préstamo). El número de desbordamientos posibles no es enorme al no tener una respuesta de n+1 bits.

Pero para una multiplicación adecuada, n bits por n bits = 2*n bits para almacenar el resultado y es por eso que verá lo que ve en muchos conjuntos de instrucciones (el resultado es el doble del tamaño de los operandos, dos registros dentro y dos registros fuera).

    abcd
  * 1111
  =======
 ...abcd
 ..abcd
 .abcd
+abcd
=========

El problema está en los puntos si esto no está firmado, esos son ceros, pero ¿qué pasa con firmado? Huele raro y ciertamente no funciona.

Tenga en cuenta que si se limita a n bits por n bits = n bits, entonces no hay diferencia entre firmado y sin firmar...

 abcd
 bcd
 cd
+d
======

Tienes un montón de posibilidades de desbordamiento.

Una solución es simplemente firmar extender todo para multiplicar con signo:

1111 x 1111 con signo = 0001 (-1 * -1 = 1), así que intente firmar extendiendo los operandos al doble de su tamaño, entonces sabemos que n bit * n bit = n bit funcionará correctamente.

  11111111
* 11111111
============

para hacer la multiplicación de 4 bits 1111 * 1111

Que se convierte en esta adición

       11111111
      11111111.
     11111111..
    11111111...
   11111111....
  11111111.....
 11111111......
11111111.......
======================

y cuando lo suma (los acarreos se administran arriba para mayor visibilidad)

.....100.......
......100......
........11.....
.........10....
..........10...
............1..
.............0.
.......11111111
......11111111.
.....11111111..
....11111111...
...11111111....
..11111111.....
.11111111......
11111111.......
======================
       00000001

Pero si piensas en cómo lo hicimos en la escuela primaria, ignoramos las señales e hicimos números positivos para la operación y luego lidiamos con las señales al final. -5 por 6, multiplicaríamos 5*6 = 30, luego, si tuviéramos signos que no coinciden, agregaríamos el signo menos al resultado -30.

Entonces, para 1111 * 1111, la multiplicación con signo ambos son negativos, así que hágalos positivos primero (invierta y agregue uno) dando esto para la primera etapa:

    0001
  * 0001
=========
    0001
   0000
  0000
 0000
==========
 0000001  

Y luego tuvimos - veces a - entonces eso es a + el resultado es +1.

Entonces, si un operando es negativo, niéguelo para cada operando, luego multiplique como sin signo y luego aplique los signos nuevamente porque en matemáticas a * b * c * d = b * c * a * d o en el orden que desee

-5 * 6 = (-1*5)*6 = -1 * 5 * 6 = -1 * (5 * 6)

pero 1111 * 1111 sin firmar, no inviertes los operandos

    1111
*   1111
=========
    1111
   1111
  1111
 1111
=========

y eso te da tu 0xE1. Con solo mirar 1111 * 1111 sin firmar y firmado, puede ver claramente que no puede hacer el mismo trabajo en ellos para obtener la respuesta correcta 15 * 15 vs -1 * -1

La división es el mismo trato 1111/0011 vs 1111/0011. 15/3 es 5 pero -1/3 = 0 en matemáticas enteras o -1/3 en punto flotante. Claramente no podemos hacer esa división con unos en el numerador por signo, entonces, lo mismo que multiplicar

-a/b = (-1*a)/b = (-1)*(a/b)  Likewise -a/-b = a/b and so on.

entonces -1/3

1111 / 0011 = -((0000+1)/0011) = -(0001/0011)


     0000
0011)0001
     0000
     ====
        1      

-(resto cero uno), entonces niegas cero 1111+1 = 0000.

¿Qué pasa con 6/-3 0110/1101 = (0110/0011)/-1

    ______     
0011)0110

si está prestando mucha atención, verá que necesitamos rellenar el numerador con ceros solo para hacer la división correctamente (los ceros están bien, estamos usando solo números positivos en esta etapa de la operación)

        00010
0011)00000110
     0000
     ====
      0000
      0000
      ====
       0001
       0000
       ====
        0011
        0011
        ====
         0000
         0000
         ====
            0

El 6/3 = 2 resto cero o 2, por lo que estamos en 2/-1 y cualquier cosa multiplicada por 1 es en sí misma, por lo que 2/-1 * -1/-1 = (-1 2)/(-1 -1) = -2. Entonces 6/-3 = -2.

Obviamente, no puede simplemente tomar los bits como están y multiplicar o dividir. Para los operandos con signo, primero debe hacer algo para que el resultado salga correctamente.

Claramente tienes que meterte con algo para que se firme, multiplicar y dividir para trabajar, si piensas en la resta y entiendes el complemento de dos, ves la belleza allí para usar menos lógica usando la suma para hacer la resta y puedes invertir el acarreo para hacer el agregue una parte de invertir y agregue uno. Probablemente haya una manera de pegar eso allí para multiplicar y dividir también, pero no lo veo tan obvio.

Los primeros procesadores quemaron suficientes relojes para implicar que estaban ocurriendo largas multiplicaciones y divisiones de registros (multiplicar para binario es simplemente cambiar y sumar para N relojes). Puede escribir ecuaciones para cada bit para obtener una solución de reloj única, pero la lógica crece exponencialmente a medida que reduce los relojes 8 o 9 relojes para operandos de 8 bits pero 4 relojes, 2 relojes y 1 reloj crece dramáticamente. Esta es la razón por la que ve que algunos procesadores no ofrecen una división en absoluto (más complicado que multiplicar) y algunos en forma de IP ofrecerán una opción de tiempo de compilación de más relojes lógicos más pequeños frente a menos/un reloj más lógico. Estas dos operaciones pueden consumir una parte significativa de la lógica de su procesador. Probablemente pueda enterrar algo de esto en la tubería y tener algunos relojes, pero aún parece tener un promedio de uno por instrucción.


Entonces, en resumen, 15 * 15 frente a -1 veces -1 si simplemente hace esto con los bits que tiene:

    1111
*   1111
=========
    1111
   1111
  1111
 1111
=========

Si completo eso obtengo

1.......
.1......
.10.....
..10....
...10...
.....1..
......0.
....1111
...1111.
..1111..
.1111...
=========
11100001

0xF * 0xF = 0xE1
15 * 15 = 225

Obviamente, eso nunca puede ser igual a 0x01 = 1 cuando los operandos se consideran firmados.

0xF * 0xF = 0x01
-1 * -1 = 1

Por lo tanto, se requiere cierta manipulación. Y puedes ver lo mismo con la división, la misma operación con los mismos bits no puede dar como resultado dos números diferentes. 1111/0011 = tanto 3 como 0 al mismo tiempo dependiendo de sin firmar frente a firmado.

Entonces, sí, tanto para la multiplicación como para la división con signo, se requiere un "procedimiento" diferente en comparación con el que no tiene signo.

En la escuela primaria hicimos todas nuestras matemáticas (sumar, restar, multiplicar y dividir) usando números positivos con una marca para indicar negativo. No teníamos -5 siendo 9996 (invertir y sumar uno) en la escuela primaria teníamos -5 del mismo modo aquí un menos tres queremos ver -0011 y no 1101 y luego se vuelve fácil. Pero a veces necesita hacer el trabajo adicional para que todos los números sean positivos, por lo tanto, un procedimiento diferente.