¿Cómo se implementan las estructuras matemáticas básicas como +, -, *, / en los lenguajes de programación?

En última instancia, las operaciones muy básicas (que incluyen +) se implementan realmente en hardware . Su CPU tiene un circuito que puede tomar dos números (pasados ​​por un montón de cables), moverlos adecuadamente y escupir su suma (distribuidos por un montón de cables). (Sí, no soy una persona de arquitectura informática).

Ahora, si haces algo como a + b , en realidad hay algunos pasos para llegar al hardware. Lo primero es averiguar de dónde vienen a y b, lo más probable es que pasen de alguna otra parte del programa. Cuando el compilador compila esa parte del programa, las variables ayb se asignarán a los registros . Los registros son “ranuras” en la CPU que pueden contener un solo número; solo tiene un conjunto limitado de estos y debe usarlos para realizar cualquier operación. Si desea realizar una instrucción de suma, los dos números deben estar en un registro y debe colocar el resultado en otro registro.

Entonces, digamos que tenemos registros para a y b. También necesitamos un registro para poner el resultado. Supongamos que estos registros son $ a0 $ a1 y $ v0. Los registros que comienzan con $ a representan argumentos y los registros que comienzan con $ v se utilizan para obtener resultados. El compilador emitiría un ensamblaje (MIPS, en este caso [1]) que indica a la computadora que agregue $ a0 a $ a1 y ponga el resultado en $ v0:

  agregue $ v0, $ a0, $ a1

Esta instrucción representa una sola directiva para la CPU, que simplemente le dice que haga esa única adición. En última instancia, todo su programa se verá como una larga cadena de pequeñas instrucciones como esta, todo haciendo cosas muy fundamentales como la adición o la búsqueda de memoria.

Una vez que tenga esta instrucción, debe convertirla en una forma binaria que la CPU pueda entender. Afortunadamente, hay una forma muy directa de traducir de ensamblado a binario: cada instrucción tiene un número y cada registro tiene un número, y simplemente los pega en una cadena binaria larga. Un truco especial en MIPS es que un montón de instrucciones como add y sub tienen el mismo número: 000000. Luego se diferencian por una constante adicional pegada al final de la expresión, después de los registros. Para agregar, este código adicional es 100000. Hasta ahora, tenemos una cadena binaria que se ve así:

  000000 $ v0 $ a0 $ a1 00000 100000

Aquí $ v0 $ a0 y $ a1 necesitan ser reemplazados por los números que codifican los registros. No recuerdo para qué sirve el 00000 extra, pero no lo usa la instrucción add para que podamos ignorarlo: P. Los números de los registros son, respectivamente: 00010, 00100 y 00101. Entonces, al final, toda la instrucción se traduce en un solo número de 32 bits:

  00000000010001000010100000100000

Entonces, en última instancia, su a + b se traducirá primero para add $v0, $a0, $a1 y luego a 00000000010001000010100000100000 . La CPU tiene hardware que sabe cómo leer las instrucciones en este formato y cómo agregar en los registros.

[1]: Estoy usando MIPS porque voy a Berkeley y aquí aparentemente creemos que CISC (y por lo tanto x86) es literalmente el demonio.

[Vea la segunda parte de la respuesta para lo que probablemente quiso decir ..]

Busque el complemento de dos (y el sistema posicional [binario]) y la transferencia de ondas para ver cómo se realiza (por lo general) la suma / resta en el hardware. En realidad, es el mismo sistema que aprenden los niños, solo menos dígitos posibles, por lo tanto, números “más simples” pero más largos, por lo que en la práctica es más difícil / inhumano.

[Si su CPU tiene solo 1 bit (sí, ¡esos existen! Al menos uno …), entonces la “suma” es trivial para números de ese tamaño … por ejemplo, solo puertas lógicas. Los bits significan 0s y 1s, es decir, números binarios, siempre puede (para enteros …) convertir ay desde sus sistemas decimales habituales; se han utilizado otras formas, por ejemplo, computadora ternaria de Saturno soviético con tres dígitos posibles ..]

Podría construir n-bit a partir de 1-bit, si aún no lo ha hecho en hardware (en realidad puede incluso entonces … simplemente no es eficiente para realizar sus propias operaciones, que ya están en hardware) mediante el transporte de ondas …

Del mismo modo, puede desarrollar la multiplacción, mediante la adición repetida, o algoritmos más inteligentes (busque en la cabina). Mi primera computadora no tenía instrucciones MUL. Y mucho menos DIV o SQRT, etc.

También hay algoritmos de división. Mi segunda computadora no tenía instrucciones DIV, ahora las computadoras sí y MUL para enteros (las primeras computadoras implementadas solo para enteros, las instrucciones de soporte de punto flotante / “notación científica” se agregaron comúnmente más tarde, ahora están disponibles universalmente y punto flotante decimal en algunas CPU, simplemente no convencionales).

La raíz cuadrada, SQRT, ahora es común en las CPU, solo para coma flotante, ya que un entero solo no es demasiado útil. Para una raíz cuadrada de valor complejo, está construida a partir de un algoritmo, basado en tipos complejos, que tienen algunos idiomas, por ejemplo, Julia. El complejo se implementa mediante dos tipos de máquinas de punto flotante (de valor real), por ejemplo, no es un tipo nativo. Es un poco, solo en dos registros, pero no tiene instrucciones valiosas complejas. De hecho, estoy investigando cómo puedes hacer mejor las operaciones con coordenadas polares (parece raro hacerlo, debido a la adición, creo …).


Ahora veo, “interpretado por el compilador”, por lo que no estaba pensando cómo lo hace la CPU. Para ver cómo obtiene esas instrucciones, explicadas anteriormente, vea primero solo la ejecución (con los pasos ocultos) con los resultados, primero ejemplo a), para todos los pasos que se realizan detrás de escena, vea las macros que agregué para buscar en el compilador; todos los compiladores (o intérpretes) necesitan analizar , dándole b) la forma “bajada” (rara vez uso / miro eso), es decir, el árbol de sintaxis abstracta , sí, no es obvio que es un árbol como se muestra …, c) es generación de código para un CPU hipotético / virtual (aquí “LLVM”), d) código de ensamblaje (para x86, ya que estoy usando eso, por ejemplo ARM sería diferente, pero paso (s) antes del mismo, por qué a menudo use ese paso (a menudo también es más simple), pero el último paso es útil cuando estoy ajustando la velocidad máxima):

julia> 1 + 1

2

julia> @code_lowered 1 + 1

Plantilla LambdaInfo para + {T <: Unión {Int128, Int16, Int32, Int64, Int8, UInt128, UInt16, UInt32, UInt64, UInt8}} (x :: T, y :: T) en int.jl: 32

:(empezar

nada

return (Base.box) ($ (Expr (: static_parameter, 1)), (Base.add_int) ((Base.unbox) ($ (Expr (: static_parameter, 1)), x), (Base.unbox) ( $ (Expr (: parámetro_estático, 1)), y)))

fin)

julia> @code_llvm 1 + 1

defina i64 @ “jlsys _ + _ 44118” (i64, i64) # 0 {

parte superior:

% 2 = agregar i64% 1,% 0

ret i64% 2

}

julia> @code_native 1 + 1

.texto

Nombre de archivo: int.jl

pushq% rbp

movq% rsp,% rbp

Línea fuente: 32

leaq (% rdi,% rsi),% rax

popq% rbp

retq

nopw (% rax,% rax)

Tenga en cuenta lo que realmente está poniendo (en los registros de la CPU, los bits son solo para mostrarlo) y salir (sería la mitad de cadenas cortas si utilizo el lenguaje CPU / Julia de 32 bits):

julia> bits (1)

“0000000000000000000000000000000000000000000000000000000000000001”

julia> bits (2)

“0000000000000000000000000000000000000000000000000000000000000010”

En lenguajes como C que admiten estas operaciones solo en tipos de datos primitivos (como int / float), las operaciones se traducen directamente en instrucciones de la máquina. Por ejemplo, el conjunto de instrucciones x86 tiene una instrucción “agregar” entera que agrega dos enteros y almacena el resultado (consulte la Guía para el ensamblaje x86). También será similar con otras arquitecturas.

En el caso de lenguajes de nivel superior que admitan estas operaciones en tipos de datos no primitivos (por ejemplo, en Python, existe el tipo largo que tiene una precisión “ilimitada”, limitada solo por la memoria – 5. Tipos incorporados), el idioma El intérprete / compilador manejará las operaciones en números más grandes dividiéndolos según sea necesario y utilizando las capacidades limitadas de la CPU.

Editar: puede escribir un programa C simple con solo una declaración de suma y ver cómo se traduce en código de ensamblaje (algo que la CPU puede ejecutar directamente) como se muestra a continuación (test.c es su programa simple, el ensamblaje de salida estará en prueba. s)
$ gcc -S -o test.s test.c

More Interesting

¿Cuál es un buen algoritmo para interpolar datos de series temporales faltantes?

¿De dónde debería comenzar a aprender el algoritmo? ¿Debería unirme a uno de los MOOC disponibles o leer libros como 'Introducción a los algoritmos'?

¿Por qué no puedo resolver la subsecuencia creciente más larga simplemente ordenando la secuencia y luego iterando a través de cada elemento asegurándome de que la secuencia siempre esté aumentando?

Mis ubicaciones están por venir, así que he estado implementando estructuras de datos y algoritmos en Python, pero llegué a saber que muchas empresas no tienen Python instalado en sus estaciones de trabajo. ¿Es verdad? Y si es así, ¿estaría bien cambiar de Python a Java, que no recuerdo mucho?

¿Qué son los algoritmos simples?

Cómo encontrar la Kth ruta más corta de un nodo a otro en un gráfico

¿Cuál es el algoritmo correcto para realizar la diferenciación usando un programa de computadora para cualquier función ingresada por el usuario?

Me resultó difícil entender los algoritmos de clasificación. ¡Cuando profundizo en los algoritmos, siento que mi mente se bloquea! ¿Qué debo hacer para sentirme cómodo con los algoritmos?

¿Cómo resolvemos esta pregunta: Jimmy y NITT WiFi?

En C, el nombre de la matriz denota la dirección del elemento cero de la matriz. ¿Es esto solo una regla, o tiene alguna razón asociada?

Dados N cajas grandes y M cajas pequeñas de diferentes tamaños, ¿cómo elegir una caja grande óptima para empacar todas las cajas más pequeñas?

¿Cuánto estrés se le da a los algoritmos y las estructuras de datos en el curso de pregrado en CMI? ¿Se enseña programación competitiva allí?

¿Qué es la compresión de datos en la base de datos?

¿Cómo se ordenan 10 números en orden creciente?

¿Quién sabe qué hay detrás de la API de Google Nearby Search? ¿Qué algoritmo usan? ¿Cómo encuentra Google una estación de servicio cercana?