Tracks
/
x86-64 Assembly
x86-64 Assembly
/
Ejercicios
/
Tarifas de freelancer
Tarifas de freelancer

Tarifas de freelancer

Ejercicio de aprendizaje

Introducción

Números de punto flotante

Un número de punto flotante es un número que puede tener una parte fraccionaria, como -1.23 o 3.1415.

Para representar un número así como una secuencia de bytes, x86-64 usa el estándar IEEE-754. Los dos formatos más comunes de este estándar son los números de punto flotante de precisión simple y de precisión doble. x86-64 admite ambos.

Un número de punto flotante de precisión simple ocupa 32 bits de ancho, mientras que uno de precisión doble ocupa 64 bits de ancho.

Registros XMM

En x86-64 hay 16 registros dedicados a hacer aritmética de punto flotante, llamados xmm0 hasta xmm15.

Esos registros tienen al menos 128 bits de ancho, pero los procesadores recientes aumentan esto a 256 o 512 bits. Sin embargo, en la práctica, la mayoría de los números de punto flotante solo usan los 32 o 64 bits más bajos, según la precisión deseada.

Conversión entre números

Existe una familia de instrucciones para convertir entre formatos de números: cvtxx2yy.

Tanto xx como yy no son literales, sino marcadores de posición:

  • xx es un marcador de posición para el tipo del operando fuente.
  • yy es un marcador de posición para el tipo del operando destino.

Este marcador de posición puede tomar muchas formas, entre ellas:

marcador de posición significado
si entero con signo
ss punto flotante de 32 bits
sd punto flotante de 64 bits

Por ejemplo:

cvtsi2ss xmm0, eax ; converts a 32-bit signed integer to a single-precision floating-point
cvtsd2si rax, xmm1 ; converts a double-precision floating-point to a 64-bit signed integer

Nota que es posible convertir un entero de 32 bits a un número de punto flotante de 64 bits, o un entero de 64 bits a un número de punto flotante de 32 bits. En otras palabras, el tamaño de ambos operandos en un cvtxx2yy no necesita ser el mismo:

cvtsi2sd xmm0, eax ; converts a 32-bit signed integer to a double-precision floating-point
cvtsi2ss xmm0, rax ; converts a 64-bit signed integer to a single-precision floating-point
cvtss2si rax, xmm0 ; converts a single-precision floating-point to a 64-bit signed integer
cvtsd2si eax, xmm0 ; converts a double-precision floating-point to a 32-bit signed integer

Cuando el número de punto flotante no se puede expresar exactamente como un entero, cvtxx2si redondea el número según el control de redondeo de un registro especial llamado mxcsr. Esto puede ser un redondeo al más cercano, un redondeo hacia abajo, un redondeo hacia arriba o un truncamiento. No hay un valor predeterminado garantizado al entrar a la función.

Nota que un resultado exacto no se redondea y, por lo tanto, no lo afecta el control de redondeo. Por ejemplo, 42.0 siempre se convertiría a 42.

Existe una familia similar de instrucciones con el formato cvttxx2si (con una t extra) que siempre trunca el resultado.

Redondeo

La familia de instrucciones roundxx convierte un número de punto flotante del operando fuente en un número de punto flotante redondeado que se guarda en el operando destino.

Aquí, xx es un marcador de posición para el tipo de los operandos, como de costumbre: ss para números de punto flotante de precisión simple y sd para los de precisión doble.

Esas instrucciones toman un número constante como tercer operando. Este número indica el modo de redondeo:

Número Modo
0 Al más cercano
1 Hacia abajo
2 Hacia arriba
3 Truncar

Por ejemplo:

roundss xmm0, xmm1, 1 ; this rounds down (takes the floor) a 32-bit floating-point in xmm1 and stores it in xmm0
roundsd xmm2, xmm2, 2 ; this rounds up (takes the ceil) a 64-bit floating-point in xmm2 and stores it in xmm2

Como esas instrucciones producen un resultado exacto, se pueden usar como preparación para un cvtxx2si.

Instrucciones aritméticas

Muchas operaciones con números de punto flotante usan instrucciones con un nombre similar al de su equivalente para enteros, pero agregando el marcador de posición necesario (ss o sd) al final. Esas instrucciones son de dos operandos, con la semántica habitual, incluso las que realizan multiplicación o división:

addss xmm1, xmm2 ; xmm1 = xmm1 + xmm2 (all 32-bit floating-point)
mulsd xmm4, xmm5 ; xmm4 = xmm4 * xmm5 (all 64-bit floating-point)
divss xmm0, xmm3 ; xmm0 = xmm0 / xmm3 (all 32-bit floating-point)

Movimiento

Las instrucciones para mover de un registro XMM a otro son movss o movsd:

movss xmm1, xmm0 ; this copies a 32-bit floating-point from xmm0 to xmm1
movsd xmm6, xmm4 ; this copies a 64-bit floating-point from xmm4 to xmm6

No es posible mover un inmediato (un entero con signo constante) directamente a un registro xmm. Este número primero debe moverse a un registro de propósito general y luego convertirse usando cvtsi2xx:

mov rax, 10        ; rax is a 64-bit integer with value 10
cvtsi2sd xmm0, rax ; xmm0 is a 64-bit floating-point with value 10.0

Comparaciones

Hay dos grupos de instrucciones para hacer una comparación entre dos números de punto flotante. Toman la forma comixx y ucomixx, donde xx es ss o sd. Ambas establecen banderas en rflags como de costumbre, siguiendo la misma semántica que cmp.

La diferencia entre ellas es que ucomixx establece la parity flag (PF) si uno de los dos números de punto flotante tiene un valor NaN, mientras que comixx genera una excepción en ese caso.

La comparación entre valores de punto flotante es sin signo:

ucomisd xmm0, xmm1
ja .greater         ; 'ja' must be used for "greater", not 'jg'
jb .lesser          ; 'jb' must be used for "lesser", not 'jl'

Convención de llamada

En la ABI System V, los primeros ocho argumentos de punto flotante se pasan a las funciones en orden, de xmm0 a xmm7. Los valores de punto flotante se devuelven de las funciones en xmm0 y, si es necesario, en xmm1.

Todos los registros xmm están disponibles para usarse libremente; ninguno de ellos está reservado para el llamador.

Memoria

Los valores de punto flotante se pueden definir en memoria siguiendo la misma semántica que los enteros del mismo tamaño. NASM (The Netwide Assembler, el ensamblador que usa esta pista) también permite declarar valores de punto flotante en notación científica.

section .data
    pi dq 3.141592653589793 ; this is a double-precision floating-point value
    exp_num dd 1.e5 ; this is a single precision floating-point value equal to 1 * 10⁵
    exp_num2 dd 1.e+5 ; this is a single precision floating-point value equal to 1 * 10⁵
    frac dq 1.e-10 ; this is a double precision floating-point value equal to 1 * 10⁻¹⁰, i.e., 0.0000000001

Instrucciones

En este ejercicio, escribirás código para ayudar a un freelancer a comunicarse con un gerente de proyecto. Tu tarea es proporcionar algunas utilidades para calcular rápidamente tarifas diarias y mensuales, opcionalmente con un descuento dado.

Primero, establecemos algunas reglas entre el freelancer y el gerente de proyecto:

  • La tarifa diaria es 8 veces la tarifa por hora.
  • Un mes tiene 22 días facturables.

A veces, el freelancer ofrece aplicar un descuento sobre su tarifa diaria (por ejemplo, para sus clientes más leales o para clientes sin fines de lucro).

Los descuentos se modelan como números fraccionarios que representan porcentajes; por ejemplo, 25.0 (25%).

Note

Estos son los sufijos de las instrucciones de punto flotante:

Sufijo Tamaño del operando
ss 32 bits
sd 64 bits

Estas son algunas de las instrucciones mencionadas en este concepto:

Instrucción Descripción
movss/movsd a, b copia b en a
addss/addsd a, b a = a + b
subss/subsd a, b a = a - b
mulss/mulsd a, b a = a * b
divss/divsd a, b a = a / b
cvtsi2ss/cvtsi2sd a, b convierte el entero b en el número de punto flotante a
roundss/roundsd a, b, c redondea el número de punto flotante b según c y lo almacena en a
cvtss2si/cvtsd2si a, b convierte el punto flotante b en el entero a
comiss/comisd a, b hace una comparación entre a y b; excepción si es NaN
ucomiss/ucomisd a, b hace una comparación entre a y b; establece PF si es NaN

Estos son los números de control de redondeo en roundss y roundsd:

Número Modo
0 Más cercano
1 Suelo (hacia abajo)
2 Techo (hacia arriba)
3 Truncar

1. Calcula la tarifa diaria dada una tarifa por hora

Implementa una función llamada daily_rate para calcular la tarifa diaria dado como parámetro una tarifa por hora, como un número de punto flotante de 64 bits. El contrato define que un día tiene 8 horas facturables.

daily_rate(60)
// => 480.0

La tarifa diaria devuelta debe ser un número de punto flotante de 64 bits.

2. Calcula un precio con descuento

Implementa una función apply_discount para calcular el precio después de un descuento. Debe aceptar dos parámetros: el precio original y la tasa de descuento en porcentaje, ambos como un número de punto flotante de 64 bits.

apply_discount(150.0, 10.0)
// => 135.0

El valor devuelto siempre debe ser un número de punto flotante de 64 bits.

3. Calcula la tarifa mensual, dada una tarifa por hora y un descuento

Implementa una función monthly_rate para calcular la tarifa mensual con descuento. Toma dos parámetros, una tarifa por hora y el descuento en porcentaje, ambos como un número de punto flotante de 64 bits.

monthly_rate(77.0, 10.5)
// => 12130

La tarifa mensual devuelta debe redondearse hacia arriba (tomar el techo) al entero de 64 bits más cercano.

4. Calcula el número de días laborables completos dados un presupuesto, una tarifa por hora y un descuento

Implementa una función days_in_budget que toma como parámetros:

  1. Un presupuesto, como un entero sin signo de 64 bits.
  2. Una tarifa por hora, como un número de punto flotante de 64 bits.
  3. Un descuento en porcentaje, como un número de punto flotante de 64 bits.

Esta función debe devolver el número de días completos de trabajo que cubre el presupuesto.

days_in_budget(20000, 80, 11.0)
// => 35

El número de días devuelto debe redondearse hacia abajo (tomar el suelo) al entero de 32 bits más cercano.

Editar en GitHub El enlace se abre en una ventana o una pestaña nuevas
x86-64 Assembly Exercism

¿Todo listo para empezar Tarifas de freelancer?

Regístrate en Exercism para aprender y dominar x86-64 Assembly con 22 conceptos130 ejercicios y mentoría humana real, todo gratis.