Rutas
/
x86-64 Assembly
x86-64 Assembly
/
Ejercicios
/
Tarifas de freelance
Tarifas de freelance

Tarifas de freelance

Ejercicio de aprendizaje

Introducción

Números de coma flotante

Un número de coma flotante es un número que puede tener 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 coma flotante de precisión simple y de precisión doble. x86-64 admite ambos.

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

Registros XMM

En x86-64 hay 16 registros dedicados a la aritmética de coma flotante, llamados xmm0 a xmm15.

Esos registros tienen al menos 128 bits de ancho, pero los procesadores recientes los amplían a 256 o 512 bits. Sin embargo, en la práctica, la mayoría de los números de coma 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 numéricos: 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 origen.
  • yy es un marcador de posición para el tipo del operando destino.

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

marcador de posición significado
si entero con signo
ss coma flotante de 32 bits
sd coma 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

Ten en cuenta que es posible convertir un entero de 32 bits en un número de coma flotante de 64 bits o un entero de 64 bits en un número de coma flotante de 32 bits. Del mismo modo, también es posible convertir un número de coma flotante de 32 bits en un entero de 64 bits o un número de coma flotante de 64 bits en un entero de 32 bits. En otras palabras, el tamaño de ambos operandos en una instrucción cvtxx2yy no tiene por qué 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 coma 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. Este puede ser un redondeo al más cercano, hacia abajo, hacia arriba o un truncamiento. No hay un valor predeterminado garantizado al entrar en una función.

Ten en cuenta que un resultado exacto no se redondea y, por tanto, no le 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 adicional) que siempre trunca el resultado.

Redondeo

La familia de instrucciones roundxx convierte un número de coma flotante del operando origen en un número de coma flotante redondeado que se almacena en el operando destino.

Aquí, xx es un marcador de posición para el tipo de los operandos, como de costumbre: ss para coma flotante de precisión simple y sd para coma flotante 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 (suelo)
2 Hacia arriba (techo)
3 Truncamiento

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 coma flotante usan instrucciones con un nombre similar al de su equivalente para enteros, pero añadiendo al final el marcador de posición necesario (ss o sd). 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 valor inmediato (un entero con signo constante) directamente a un registro xmm. Este número primero debe moverse a un registro de uso 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 comparar dos números de coma flotante. Tienen la forma comixx y ucomixx, donde xx es ss o sd. Ambas establecen los flags de rflags como de costumbre, siguiendo la misma semántica que cmp.

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

La comparación entre valores de coma 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 de System V, los ocho primeros argumentos de coma flotante se pasan a las funciones en orden, de xmm0 a xmm7. Los valores de coma flotante se devuelven de las funciones en xmm0 y, si es necesario, en xmm1.

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

Memoria

Los valores de coma 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 este track) también permite declarar valores de coma 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 vas a escribir código para ayudar a un freelancer a comunicarse con un jefe de proyecto. Tu tarea es proporcionar unas cuantas utilidades para calcular rápidamente tarifas diarias y mensuales, opcionalmente con un descuento dado.

Primero establecemos unas cuantas reglas entre el freelancer y el jefe 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 fieles o para clientes sin ánimo de lucro).

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

Note

Estos son los sufijos de las instrucciones de coma 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 coma flotante a
roundss/roundsd a, b, c redondea el número de coma flotante b según c y lo almacena en a
cvtss2si/cvtsd2si a, b convierte el número de coma flotante b en el entero a
comiss/comisd a, b hace una comparación entre a y b, lanza una excepción si NaN
ucomiss/ucomisd a, b hace una comparación entre a y b, establece PF si NaN

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

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

1. Calcula la tarifa diaria a partir de una tarifa por hora

Implementa una función llamada daily_rate que calcule la tarifa diaria a partir de una tarifa por hora recibida como parámetro, como número de coma 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 coma flotante de 64 bits.

2. Calcula un precio con descuento

Implementa una función apply_discount que calcule el precio después de aplicar un descuento. Debe aceptar dos parámetros: el precio original y el porcentaje de descuento, ambos como números de coma flotante de 64 bits.

apply_discount(150.0, 10.0)
// => 135.0

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

3. Calcula la tarifa mensual a partir de una tarifa por hora y un descuento

Implementa una función monthly_rate que calcule la tarifa mensual con descuento. Recibe dos parámetros: una tarifa por hora y el porcentaje de descuento, ambos como números de coma 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 completos de trabajo a partir de un presupuesto, una tarifa por hora y un descuento

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

  1. Un presupuesto, como entero sin signo de 64 bits.
  2. Una tarifa por hora, como número de coma flotante de 64 bits.
  3. Un porcentaje de descuento, como número de coma 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 pestaña nueva
x86-64 Assembly Exercism

¿Listo para empezar Tarifas de freelance?

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