01 · Punto de partida
Cortar no es redondear
En el Tema 40 viste el redondeo del hardware: el vecino más cercano, error ≤ ½ ulp y sin sesgo. Truncar es lo contrario: cortar los decimales que sobran y quedarse con lo que entra, siempre hacia cero. Lo hacés cada vez que convertís con int(), que armás un índice con int(u * n) o que mostrás un precio con 2 decimales “cortados”.
El corte parece inofensivo, pero tiene dos pecados: pierde hasta el doble que redondear (1 unidad en vez de ½) y sesga: con positivos siempre resta, con negativos siempre suma. En una cuenta no pasa nada; en un millón de cuentas o en un histograma, el sesgo se ve.
- ¿Qué diferencia hay entre truncar, redondear y pisar (floor)?
- ¿Por qué
int(-2,7)da −2 y no −3? - ¿Cuánto se pierde al truncar a k decimales?
- ¿Dónde muerde el sesgo en simulación: índices, bins, plata?
02 · Definición
Tres cortes, tres destinos
trunc (hacia cero)
trunc(2,7) = 2, trunc(−2,7) = −2. Es lo que hace int() con floats. Corta el decimal y punto.
floor (hacia −∞)
floor(2,7) = 2, floor(−2,7) = −3. Es lo que hace // e int(u*n) bien usado para índices y bins.
round (al cercano)
round(2,7) = 3, round(−2,7) = −3. Error ≤ mitad, sin sesgo sistemático. La opción por defecto para medir.
| x | trunc | floor | round |
|---|---|---|---|
2,675 | 2 | 2 | 3 |
−2,675 | −2 | −3 | −3 |
0,9999 (k = 2) | 0,99 | 0,99 | 1,00 |
03 · Anatomía
Dónde nace el sesgo
El redondeo del Tema 40 a veces suma y a veces resta: en promedio se compensa. El truncamiento con positivos siempre resta: cada corte deja una “basura” entre 0 y 10⁻ᵏ que nunca se recupera. Tres lugares donde aparece:
- Truncamiento de visualización
- Mostrar con
int(x*100)/100o cortar el string a 2 decimales: el ticket dice 2,67 aunque el valor real es 2,679. Para mostrar se usaround/formato, no corte. - Truncamiento de conversión
int(u * n)conuen [0,1): siupuede valer 1,0 por redondeo, el índice se sale de rango. Y con negativos,intno esfloor: los bins se corren un lugar (Tema 35).- Truncamiento de modelo (aviso)
- En métodos numéricos también se llama “truncamiento” a cortar una serie infinita (Taylor, Euler): otro corte con otro análisis. Acá nos ocupamos del corte de decimales; el de series reaparece en el Tema 43 como inestabilidad.
Bien cortado
floor para bins e índices
min(int(u*n), n-1) o math.floor: cada bin mide lo mismo y el borde 1,0 no rompe nada. Predecible con negativos.
Mal cortado
int() para plata o promedios
int(precio*100)/100 siempre regala centavos al mismo lado; promediado miles de veces, el faltante se vuelve plata real.
04 · Ejemplos
Tres cortes que duelen
- 1El centavo fantasma.
Truncar
2,679a 2 decimales da2,67(pierde 0,009); redondear da2,68(pierde 0,001). En 100 000 tickets, el corte regala ~450 $ del mismo bolsillo. - 2El índice que se escapa.
int(0,9999999 * 10) = 9está bien, pero si el redondeo dejau = 1,0,int(1,0*10) = 10explota la lista. Elmin(..., n-1)lo salva (Tema 22). - 3El bin corrido.
Con
int(x)sobre negativos, el intervalo [−3,−2) cae en −2 en vez de −3: el histograma cojea justo donde cruza el cero. Confloorcada bin mide 1 parejo.
05 · Demostración en Python
Cortar a mano y medir la basura
Dos funciones de 2 líneas separan todo: truncar corta, redondear elige al cercano. Medí la basura de cada una sobre el mismo valor.
Python en tu navegador. Probá con 2,675, −2,675 y 0,9999: el corte siempre pierde más y siempre del mismo lado.
import math
def truncar(x, k=2):
f = 10 ** k
return math.trunc(x * f) / f
def redondear(x, k=2):
return round(x, k)
for v in [2.675, -2.675, 0.9999]:
print(v, truncar(v), redondear(v))
Verás 2,67 vs 2,68, −2,67 vs −2,67… y el 0,99 vs 1,0: el corte pierde hasta 0,01 entero.
El sesgo en cámara lenta
import math
import random
random.seed(41)
vals = [random.random() * 10 for _ in range(20000)]
basura_trunc = sum(v - math.trunc(v * 100) / 100 for v in vals)
basura_round = sum(v - round(v, 2) for v in vals)
print("trunc suma:", round(basura_trunc, 2))
print("round suma:", round(basura_round, 4))
06 · Exploración
Laboratorio: truncar frente a redondear
Elegí un valor y cuántos decimales conservar (k). El punto verde es el valor real, el naranja el truncado y el celeste el redondeado, sobre el intervalo entre los dos múltiplos de 10⁻ᵏ que lo encierran. La barra naranja es siempre más larga que la celeste… o igual en el peor empate.
Corte a k decimales
|trunc − x| < 10⁻ᵏ · |round − x| ≤ ½·10⁻ᵏ
Con π y k = 2 el corte pierde más del doble que el redondeo.
La recta es el intervalo de largo 10⁻ᵏ entre los dos candidatos. El truncado cae siempre en el borde hacia cero; el redondeado, en el más cercano al valor real.
Preguntas para explorar
- Con π y k = 2: ¿cuánto pierde truncar (3,14) frente a redondear (3,14)? ¿Y con k = 0?
- Con 2,675 y k = 2: ¿por qué el redondeado da 2,67 y no 2,68? (Pista: Tema 40.)
- Con −2,675: ¿dónde cae el truncado respecto del redondeado? ¿Qué pasaría con
floor?
Ver respuestas sugeridas
- Truncar pierde ≈ 0,00159 y redondear ≈ 0,00159 también acá (π ≈ 3,1416 está casi al medio bajo); con k = 0 truncar pierde 0,14 y redondear solo 0,14… no:
trunc(π) = 3pierde 0,1416 yround(π) = 3pierde lo mismo: el empate se rompe con 1/3 (0,33 vs 0,33 iguales, pero con k = 0: 0 vs 0). - Porque el literal
2,675ya es2,6749999…en binario (Tema 40): el vecino más cercano a 2 decimales es 2,67. El “debería dar 2,68” vive en el pizarrón, no en la grilla. - El truncado (−2,67) queda a la derecha del real y el redondeado (−2,67 o −2,68 según el vecino binario) a la izquierda o igual: el corte tira hacia cero y
floorhacia −∞ (−2,68). Con negativos se separan un paso entero.
07 · Comprensión
Confusiones frecuentes
«Truncar es redondear para abajo»
Solo con positivos. Con negativos truncar es “para arriba” (hacia cero): int(−2,7) = −2. “Para abajo” de verdad es floor. Esa diferencia de 1 rompe bins que cruzan el cero.
«Cortar a 2 decimales es exacto para plata»
Cortar siempre regala la basura al mismo lado: en miles de operaciones el faltante suma. Para plata se redondea (o se trabaja en centavos enteros), no se corta.
«int(u * n) siempre da un índice válido»
Si u llega a 1,0 por redondeo, int(n) = n se sale de la lista. El tope min(..., n-1) no es paranoia: es el borde del intervalo [0,1] (Tema 22).
«Truncamiento y redondeo pierden lo mismo»
Truncar pierde hasta 1 unidad (10⁻ᵏ) y redondear hasta media: el doble. Y el de truncamiento es sesgado. A igual k, redondear es siempre mejor para medir.
08 · Práctica guiada
Ejercicios con Python
Ejercicio 1: los tres destinos de −2,7
Mostrá en una línea que int, // y round no son sinónimos con negativos.
import math
x = -2.7
print(int(x), math.floor(x), round(x))
print(x // 1, math.trunc(x))
Ver solución razonada
−2 −3 −3 y −3,0 −2: int/trunc van hacia cero y floor/// hacia −∞. Con positivos coinciden y el bug se esconde hasta que la grilla cruza el cero.
Ejercicio 2: el centavo que suma
Compará truncar frente a redondear 20 000 importes a 2 decimales: ¿cuánto regala cada uno en total?
import math
import random
random.seed(41)
vals = [random.random() * 100 for _ in range(20000)]
t = sum(v - math.trunc(v * 100) / 100 for v in vals)
r = sum(v - round(v, 2) for v in vals)
print("trunc regala:", round(t, 2))
print("round regala:", round(r, 4))
Ver solución
Truncar regala ≈ 100 $ (media 0,005 × 20 000) siempre del mismo lado; redondear oscila cerca de 0. Ese es el sesgo convertido en plata.
Ejercicio 3: bins sin corrimiento
Escribí bin_int (con int) y bin_floor (con floor) y probalas con valores a ambos lados del cero.
Ver una posible respuesta
import math
def bin_int(x, w=1.0):
return int(x / w)
def bin_floor(x, w=1.0):
return math.floor(x / w)
for v in [-2.7, -2.0, -0.2, 0.2, 2.7]:
print(v, bin_int(v), bin_floor(v))
Con negativos difieren en 1: int junta [−3,−2) con [−2,−1) en el borde. floor deja todos los bins de ancho w parejo.
09 · Síntesis
Ideas para recordar
- Truncar corta hacia cero; floor hacia −∞; round al cercano.
- Truncar pierde hasta 10⁻ᵏ (el doble que redondear) y siempre del mismo lado.
- Con negativos, int ≠ floor por 1 unidad: bins parejos van con floor.
- Índices con
min(int(u*n), n-1); plata con round o centavos enteros. - El corte es sesgo, no ruido: sumado miles de veces se vuelve plata o histograma torcido.
En el próximo tema veremos qué pasa cuando esos cortes y redondeos se suman miles de veces: la acumulación de errores numéricos.