En JavaScript moderno, existen dos tipos de números:
Los números normales en JavaScript se almacenan en formato de 64 bits IEEE-754, también conocido como "números de coma flotante de doble precisión". Estos son números que usamos la mayor parte del tiempo y hablaremos de ellos en este capítulo.
Los números BigInt representan números enteros de longitud arbitraria. A veces son necesarios porque un número entero normal no puede exceder con seguridad (2 53 -1)
o ser menor que -(2 53 -1)
, como mencionamos anteriormente en el capítulo Tipos de datos. Como los bigints se utilizan en algunas áreas especiales, los dedicamos a un capítulo especial BigInt.
Entonces aquí hablaremos de números regulares. Ampliemos nuestro conocimiento sobre ellos.
Imaginemos que necesitamos escribir mil millones. La forma obvia es:
dejemos mil millones = 1000000000;
También podemos usar guión bajo _
como separador:
sea mil millones = 1_000_000_000;
Aquí el guión bajo _
desempeña el papel de "azúcar sintáctico", hace que el número sea más legible. El motor JavaScript simplemente ignora _
entre dígitos, por lo que es exactamente el mismo billón que el anterior.
Sin embargo, en la vida real intentamos evitar escribir largas secuencias de ceros. Somos demasiado vagos para eso. Intentaremos escribir algo como "1bn"
por mil millones o "7.3bn"
por 7.300 millones. Lo mismo ocurre con la mayoría de los números grandes.
En JavaScript, podemos acortar un número añadiéndole la letra "e"
y especificando el recuento de ceros:
dejemos mil millones = 1e9; // 1 billón, literalmente: 1 y 9 ceros alerta (7.3e9); // 7,3 mil millones (igual que 7300000000 o 7_300_000_000)
En otras palabras, e
multiplica el número por 1
con el recuento de ceros dado.
1e3 === 1 * 1000; // e3 significa *1000 1.23e6 === 1.23 * 1000000; // e6 significa *1000000
Ahora escribamos algo muy pequeño. Digamos, 1 microsegundo (una millonésima de segundo):
sea mсs = 0,000001;
Al igual que antes, usar "e"
puede ayudar. Si quisiéramos evitar escribir los ceros explícitamente, podríamos escribir lo mismo como:
sea mcs = 1e-6; // cinco ceros a la izquierda del 1
Si contamos los ceros en 0.000001
, hay 6. Entonces, naturalmente, es 1e-6
.
En otras palabras, un número negativo después de "e"
significa una división entre 1 con el número dado de ceros:
// -3 divide por 1 con 3 ceros 1e-3 === 1/1000; // 0.001 // -6 divide por 1 con 6 ceros 1,23e-6 === 1,23/1000000; // 0.00000123 // un ejemplo con un número mayor 1234e-2 === 1234/100; // 12.34, el punto decimal se mueve 2 veces
Los números hexadecimales se utilizan ampliamente en JavaScript para representar colores, codificar caracteres y muchas otras cosas. Naturalmente, existe una forma más corta de escribirlos: 0x
y luego el número.
Por ejemplo:
alerta (0xff); // 255 alerta (0xFF); // 255 (lo mismo, no importa el caso)
Los sistemas de numeración binarios y octales rara vez se utilizan, pero también se admiten el uso de los prefijos 0b
y 0o
:
sea a = 0b11111111; // forma binaria de 255 sea b = 0o377; // forma octal de 255 alerta( a == b ); // cierto, el mismo número 255 en ambos lados
Sólo existen 3 sistemas de numeración con dicho soporte. Para otros sistemas numéricos, deberíamos usar la función parseInt
(que veremos más adelante en este capítulo).
El método num.toString(base)
devuelve una representación de cadena de num
en el sistema numérico con la base
dada.
Por ejemplo:
sea número = 255; alerta( num.toString(16) ); //ff alerta( num.toString(2) ); // 11111111
La base
puede variar de 2
a 36
. Por defecto, es 10
.
Los casos de uso comunes para esto son:
base=16 se usa para colores hexadecimales, codificaciones de caracteres, etc., los dígitos pueden ser 0..9
o A..F
.
base=2 es principalmente para depurar operaciones bit a bit, los dígitos pueden ser 0
o 1
.
base=36 es el máximo, los dígitos pueden ser 0..9
o A..Z
. Todo el alfabeto latino se utiliza para representar un número. Un caso divertido pero útil para 36
es cuando necesitamos convertir un identificador numérico largo en algo más corto, por ejemplo, para crear una URL corta. Simplemente puedo representarlo en el sistema numérico con base 36
:
alerta (123456..toString(36)); // 2n9c
Dos puntos para llamar a un método
Tenga en cuenta que dos puntos en 123456..toString(36)
no son un error tipográfico. Si queremos llamar a un método directamente en un número, como toString
en el ejemplo anterior, entonces debemos colocar dos puntos ..
después de él.
Si colocamos un solo punto: 123456.toString(36)
, entonces habría un error, porque la sintaxis de JavaScript implica la parte decimal después del primer punto. Y si colocamos un punto más, entonces JavaScript sabe que la parte decimal está vacía y ahora sigue el método.
También podría escribir (123456).toString(36)
.
Una de las operaciones más utilizadas a la hora de trabajar con números es el redondeo.
Hay varias funciones integradas para redondear:
Math.floor
Redondea hacia abajo: 3.1
se convierte en 3
y -1.1
se convierte en -2
.
Math.ceil
Redondea hacia arriba: 3.1
se convierte en 4
y -1.1
se convierte en -1
.
Math.round
Redondea al entero más cercano: 3.1
se convierte en 3
, 3.6
se convierte en 4
. En los casos intermedios, 3.5
se redondea a 4
y -3.5
se redondea a -3
.
Math.trunc
(no compatible con Internet Explorer)
Elimina todo lo que esté después del punto decimal sin redondear: 3.1
se convierte en 3
, -1.1
se convierte en -1
.
Aquí tienes la tabla para resumir las diferencias entre ellos:
Math.floor | Math.ceil | Math.round | Math.trunc | |
---|---|---|---|---|
3.1 | 3 | 4 | 3 | 3 |
3.5 | 3 | 4 | 4 | 3 |
3.6 | 3 | 4 | 4 | 3 |
-1.1 | -2 | -1 | -1 | -1 |
-1.5 | -2 | -1 | -1 | -1 |
-1.6 | -2 | -1 | -2 | -1 |
Estas funciones cubren todas las formas posibles de tratar con la parte decimal de un número. Pero, ¿qué pasa si quisiéramos redondear el número al n-th
dígito después del decimal?
Por ejemplo, tenemos 1.2345
y queremos redondearlo a 2 dígitos, obteniendo solo 1.23
.
Hay dos maneras de hacerlo:
Multiplica y divide.
Por ejemplo, para redondear el número al segundo dígito después del decimal, podemos multiplicar el número por 100
, llamar a la función de redondeo y luego volver a dividirlo.
sea número = 1,23456; alerta( Math.round(num * 100) / 100 ); // 1,23456 -> 123,456 -> 123 -> 1,23
El método toFixed(n) redondea el número a n
dígitos después del punto y devuelve una representación de cadena del resultado.
sea número = 12,34; alerta( num.toFixed(1) ); // "12.3"
Esto redondea hacia arriba o hacia abajo al valor más cercano, similar a Math.round
:
sea número = 12,36; alerta( num.toFixed(1) ); // "12.4"
Tenga en cuenta que el resultado de toFixed
es una cadena. Si la parte decimal es más corta de lo requerido, se añaden ceros al final:
sea número = 12,34; alerta( num.toFixed(5) ); // "12.34000", se agregaron ceros para formar exactamente 5 dígitos
Podemos convertirlo en un número usando el signo unario más o una llamada a Number()
, por ejemplo, escribir +num.toFixed(5)
.
Internamente, un número se representa en formato de 64 bits IEEE-754, por lo que existen exactamente 64 bits para almacenar un número: 52 de ellos se utilizan para almacenar los dígitos, 11 de ellos almacenan la posición del punto decimal y 1 bit es para la señal.
Si un número es realmente grande, puede desbordar el almacenamiento de 64 bits y convertirse en un valor numérico especial Infinity
:
alerta (1e500); // Infinidad
Lo que puede ser un poco menos obvio, pero que ocurre con bastante frecuencia, es la pérdida de precisión.
Considere esta (¡falsa!) prueba de igualdad:
alerta (0,1 + 0,2 == 0,3); // FALSO
Así es, si comprobamos si la suma de 0.1
y 0.2
es 0.3
, obtenemos false
.
¡Extraño! ¿Qué es entonces si no 0.3
?
alerta (0,1 + 0,2); // 0.30000000000000004
¡Ay! Imagine que está creando un sitio de compras electrónicas y el visitante pone productos $0.10
y $0.20
en su carrito. El total del pedido será $0.30000000000000004
. Eso sorprendería a cualquiera.
¿Pero por qué sucede esto?
Un número se almacena en la memoria en su forma binaria, una secuencia de bits: unos y ceros. Pero fracciones como 0.1
, 0.2
que parecen simples en el sistema numérico decimal son en realidad fracciones interminables en su forma binaria.
alerta(0.1.toString(2)); // 0.0001100110011001100110011001100110011001100110011001101 alerta(0.2.toString(2)); // 0.001100110011001100110011001100110011001100110011001101 alerta((0.1 + 0.2).toString(2)); // 0.0100110011001100110011001100110011001100110011001101
¿Qué es 0.1
? Es uno dividido por diez 1/10
, un décimo. En el sistema de numeración decimal, estos números son fácilmente representables. Compárelo con un tercio: 1/3
. Se convierte en una fracción infinita 0.33333(3)
.
Por lo tanto, se garantiza que la división por potencias 10
funcionará bien en el sistema decimal, pero la división por 3
no. Por la misma razón, en el sistema de numeración binario, se garantiza que la división por potencias de 2
funciona, pero 1/10
se convierte en una fracción binaria sin fin.
Simplemente no hay forma de almacenar exactamente 0,1 o exactamente 0,2 usando el sistema binario, al igual que no hay forma de almacenar un tercio como fracción decimal.
El formato numérico IEEE-754 resuelve esto redondeando al número más cercano posible. Estas reglas de redondeo normalmente no nos permiten ver esa “pequeña pérdida de precisión”, pero existe.
Podemos ver esto en acción:
alerta (0.1.toFixed (20)); // 0.10000000000000000555
Y cuando sumamos dos números, sus “pérdidas de precisión” se suman.
Por eso 0.1 + 0.2
no es exactamente 0.3
.
No sólo JavaScript
El mismo problema existe en muchos otros lenguajes de programación.
PHP, Java, C, Perl y Ruby dan exactamente el mismo resultado porque se basan en el mismo formato numérico.
¿Podemos solucionar el problema? Claro, el método más confiable es redondear el resultado con la ayuda de un método toFixed(n):
sea suma = 0,1 + 0,2; alerta( suma.toFixed(2) ); // "0,30"
Tenga en cuenta que toFixed
siempre devuelve una cadena. Asegura que tiene 2 dígitos después del punto decimal. Esto es realmente conveniente si tenemos una tienda electrónica y necesitamos mostrar $0.30
. Para otros casos, podemos usar el signo unario más para convertirlo en un número:
sea suma = 0,1 + 0,2; alerta( +sum.toFixed(2) ); // 0.3
También podemos multiplicar temporalmente los números por 100 (o un número mayor) para convertirlos en números enteros, hacer los cálculos y luego volver a dividir. Luego, mientras hacemos cálculos con números enteros, el error disminuye un poco, pero aún lo obtenemos en la división:
alerta ((0,1 * 10 + 0,2 * 10) / 10); // 0.3 alerta( (0,28 * 100 + 0,14 * 100) / 100); // 0.4200000000000001
Entonces, el enfoque de multiplicar/dividir reduce el error, pero no lo elimina por completo.
A veces podríamos intentar evadir las fracciones. Como si estuviéramos tratando con una tienda, entonces podemos almacenar los precios en centavos en lugar de dólares. Pero ¿y si aplicamos un descuento del 30%? En la práctica, rara vez es posible evadir fracciones por completo. Simplemente redondeelos para cortar "colas" cuando sea necesario.
lo gracioso
Intenta ejecutar esto:
// ¡Hola! ¡Soy un número que crece por sí mismo! alerta (9999999999999999); // muestra 10000000000000000
Esto sufre el mismo problema: una pérdida de precisión. Hay 64 bits para un número, 52 de ellos se pueden usar para almacenar dígitos, pero eso no es suficiente. Entonces los dígitos menos significativos desaparecen.
JavaScript no genera un error en tales eventos. Hace todo lo posible para ajustar el número al formato deseado, pero desafortunadamente este formato no es lo suficientemente grande.
dos ceros
Otra curiosa consecuencia de la representación interna de los números es la existencia de dos ceros: 0
y -0
.
Esto se debe a que un signo está representado por un solo bit, por lo que se puede configurar o no para cualquier número, incluido un cero.
En la mayoría de los casos, la distinción es imperceptible, porque los operadores están capacitados para tratarlos como si fueran iguales.
¿Recuerda estos dos valores numéricos especiales?
Infinity
(y -Infinity
) es un valor numérico especial que es mayor (menor) que cualquier cosa.
NaN
representa un error.
Pertenecen al tipo number
, pero no son números “normales”, por lo que existen funciones especiales para comprobarlos:
isNaN(value)
convierte su argumento en un número y luego prueba que sea NaN
:
alerta( esNaN(NaN) ); // verdadero alerta( isNaN("str") ); // verdadero
¿Pero necesitamos esta función? ¿No podemos simplemente usar la comparación === NaN
? Lamentablemente no. El valor NaN
es único porque no equivale a nada, ni siquiera a sí mismo:
alerta( NaN === NaN ); // FALSO
isFinite(value)
convierte su argumento en un número y devuelve true
si es un número normal, no NaN/Infinity/-Infinity
:
alerta( esFinito("15") ); // verdadero alerta( esFinite("str") ); // falso, porque un valor especial: NaN alerta( esFinito(Infinito) ); // falso, porque un valor especial: Infinito
A veces, isFinite
se usa para validar si un valor de cadena es un número normal:
let num = +prompt("Ingrese un número", ''); // será verdadero a menos que ingreses Infinito, -Infinito o no sea un número alerta( esFinito(núm) );
Tenga en cuenta que una cadena vacía o de solo espacios se trata como 0
en todas las funciones numéricas, incluido isFinite
.
Number.isNaN
y Number.isFinite
Los métodos Number.isNaN y Number.isFinite son las versiones más "estrictas" de las funciones isNaN
e isFinite
. No convierten automáticamente su argumento en un número, sino que comprueban si pertenece al tipo number
.
Number.isNaN(value)
devuelve true
si el argumento pertenece al tipo number
y es NaN
. En cualquier otro caso, devuelve false
.
alerta (Número.isNaN(NaN)); // verdadero alerta( Número.isNaN("str" / 2) ); // verdadero // Note la diferencia: alerta( Número.isNaN("str") ); // falso, porque "str" pertenece al tipo de cadena, no al tipo de número alerta( isNaN("str") ); // verdadero, porque isNaN convierte la cadena "str" en un número y obtiene NaN como resultado de esta conversión
Number.isFinite(value)
devuelve true
si el argumento pertenece al tipo number
y no es NaN/Infinity/-Infinity
. En cualquier otro caso, devuelve false
.
alerta (Número.isFinite(123)); // verdadero alerta (Número.isFinite(Infinito)); // FALSO alerta (Número.isFinite(2/0)); // FALSO // Note la diferencia: alerta( Número.isFinite("123") ); // falso, porque "123" pertenece al tipo de cadena, no al tipo de número alerta( esFinito("123") ); // verdadero, porque isFinite convierte la cadena "123" en un número 123
En cierto modo, Number.isNaN
y Number.isFinite
son más simples y directas que las funciones isNaN
e isFinite
. Sin embargo, en la práctica, isNaN
e isFinite
se utilizan principalmente, ya que son más cortos de escribir.
Comparación con Object.is
Hay un método especial incorporado Object.is
que compara valores como ===
, pero es más confiable para dos casos extremos:
Funciona con NaN
: Object.is(NaN, NaN) === true
, eso es algo bueno.
Los valores 0
y -0
son diferentes: Object.is(0, -0) === false
, técnicamente eso es correcto porque internamente el número tiene un bit de signo que puede ser diferente incluso si todos los demás bits son ceros.
En todos los demás casos, Object.is(a, b)
es lo mismo que a === b
.
Mencionamos Object.is
aquí porque se usa a menudo en la especificación de JavaScript. Cuando un algoritmo interno necesita comparar dos valores para determinar si son exactamente iguales, utiliza Object.is
(internamente llamado SameValue).
La conversión numérica usando más +
o Number()
es estricta. Si un valor no es exactamente un número, falla:
alerta( +"100px" ); //NaN
La única excepción son los espacios al principio o al final de la cadena, ya que se ignoran.
Pero en la vida real, normalmente tenemos valores en unidades, como "100px"
o "12pt"
en CSS. Además, en muchos países, el símbolo de la moneda va después del importe, por lo que tenemos "19€"
y nos gustaría extraer un valor numérico de ahí.
Para eso están parseInt
y parseFloat
.
"Leen" un número de una cadena hasta que ya no pueden. En caso de error, se devuelve el número recopilado. La función parseInt
devuelve un número entero, mientras que parseFloat
devolverá un número de punto flotante:
alerta( parseInt('100px') ); // 100 alerta( parseFloat('12.5em') ); // 12.5 alerta( parseInt('12.3') ); // 12, solo se devuelve la parte entera alerta (parseFloat ('12.3.4')); // 12.3, el segundo punto detiene la lectura
Hay situaciones en las que parseInt/parseFloat
devolverá NaN
. Ocurre cuando no se pueden leer dígitos:
alerta( parseInt('a123') ); // NaN, el primer símbolo detiene el proceso
El segundo argumento de parseInt(str, radix)
La función parseInt()
tiene un segundo parámetro opcional. Especifica la base del sistema numérico, por lo que parseInt
también puede analizar cadenas de números hexadecimales, números binarios, etc.:
alerta( parseInt('0xff', 16) ); // 255 alerta( parseInt('ff', 16) ); // 255, sin 0x también funciona alerta( parseInt('2n9c', 36) ); // 123456
JavaScript tiene un objeto Math incorporado que contiene una pequeña biblioteca de funciones y constantes matemáticas.
Algunos ejemplos:
Math.random()
Devuelve un número aleatorio de 0 a 1 (sin incluir 1).
alerta (Math.random()); // 0,1234567894322 alerta (Math.random()); // 0.5435252343232 alerta (Math.random()); // ... (cualquier número aleatorio)
Math.max(a, b, c...)
y Math.min(a, b, c...)
Devuelve el mayor y el menor de un número arbitrario de argumentos.
alerta( Math.max(3, 5, -10, 0, 1) ); // 5 alerta( Math.min(1, 2) ); // 1
Math.pow(n, power)
Devuelve n
elevado a la potencia dada.
alerta( Math.pow(2, 10) ); // 2 en potencia 10 = 1024
Hay más funciones y constantes en el objeto Math
, incluida la trigonometría, que puede encontrar en los documentos del objeto Math.
Para escribir números con muchos ceros:
Agregue "e"
con los ceros al número. Como: 123e6
es lo mismo que 123
con 6 ceros 123000000
.
Un número negativo después de "e"
hace que el número se divida por 1 con ceros dados. Por ejemplo, 123e-6
significa 0.000123
( 123
millonésimas).
Para diferentes sistemas numéricos:
Puede escribir números directamente en sistemas hexadecimal ( 0x
), octal ( 0o
) y binario ( 0b
).
parseInt(str, base)
analiza la cadena str
en un número entero en un sistema numérico con base
dada, 2 ≤ base ≤ 36
.
num.toString(base)
convierte un número en una cadena en el sistema numérico con la base
dada.
Para pruebas numéricas regulares:
isNaN(value)
convierte su argumento en un número y luego prueba que sea NaN
Number.isNaN(value)
comprueba si su argumento pertenece al tipo number
y, de ser así, prueba que sea NaN
isFinite(value)
convierte su argumento en un número y luego prueba que no sea NaN/Infinity/-Infinity
Number.isFinite(value)
comprueba si su argumento pertenece al tipo number
y, de ser así, lo prueba para que no sea NaN/Infinity/-Infinity
Para convertir valores como 12pt
y 100px
a un número:
Utilice parseInt/parseFloat
para la conversión "suave", que lee un número de una cadena y luego devuelve el valor que podían leer antes del error.
Para fracciones:
Ronda usando Math.floor
, Math.ceil
, Math.trunc
, Math.round
o num.toFixed(precision)
.
Asegúrate de recordar que hay una pérdida de precisión al trabajar con fracciones.
Más funciones matemáticas:
Vea el objeto Math cuando los necesite. La biblioteca es muy pequeña pero puede cubrir las necesidades básicas.
importancia: 5
Cree un script que solicite al visitante que ingrese dos números y luego muestre su suma.
Ejecute la demostración
PD: Hay un problema con los tipos.
let a = +prompt("¿El primer número?", ""); let b = +prompt("¿El segundo número?", ""); alerta (a + b);
Tenga en cuenta el signo unario más +
antes prompt
. Inmediatamente convierte el valor en un número.
De lo contrario, a
y b
serían cadenas, su suma sería su concatenación, es decir: "1" + "2" = "12"
.
importancia: 4
Según la documentación Math.round
y toFixed
ambos redondean al número más cercano: 0..4
hacia abajo mientras que 5..9
hacia arriba.
Por ejemplo:
alerta (1.35.toFixed(1)); // 1.4
En el ejemplo similar siguiente, ¿por qué 6.35
se redondea a 6.3
y no 6.4
?
alerta (6.35.toFixed(1)); // 6.3
¿Cómo redondear 6.35
de la manera correcta?
Internamente, la fracción decimal 6.35
es un binario sin fin. Como siempre en estos casos, se almacena con pérdida de precisión.
Vamos a ver:
alerta (6.35.toFixed(20)); // 6.34999999999999964473
La pérdida de precisión puede provocar tanto un aumento como una disminución de un número. En este caso particular, el número se vuelve un poquito menor, por eso se redondeó hacia abajo.
¿Y cuánto cuesta 1.35
?
alerta (1.35.toFixed(20)); // 1.35000000000000008882
Aquí la pérdida de precisión hizo que el número fuera un poco mayor, por lo que se redondeó hacia arriba.
¿Cómo podemos solucionar el problema con 6.35
si queremos que se redondee de la manera correcta?
Deberíamos acercarlo a un número entero antes de redondearlo:
alerta( (6.35 * 10).toFixed(20) ); // 63.500000000000000000000
Tenga en cuenta que 63.5
no tiene ninguna pérdida de precisión. Esto se debe a que la parte decimal 0.5
es en realidad 1/2
. Las fracciones divididas entre potencias de 2
quedan exactamente representadas en el sistema binario, ahora podemos redondearlo:
alerta( Math.round(6.35 * 10) / 10 ); // 6,35 -> 63,5 -> 64(redondeado) -> 6,4
importancia: 5
Cree una función readNumber
que solicite un número hasta que el visitante ingrese un valor numérico válido.
El valor resultante debe devolverse como un número.
El visitante también puede detener el proceso ingresando una línea vacía o presionando “CANCELAR”. En ese caso, la función debería devolver null
.
Ejecute la demostración
Abra una caja de arena con pruebas.
función leerNúmero() { deja número; hacer { num = Prompt("¿Ingrese un número por favor?", 0); } mientras (!isFinite(núm)); if (núm === nulo || núm === '') devuelve nulo; devolver +núm; } alerta(`Leer: ${readNumber()}`);
La solución es un poco más compleja de lo que podría ser porque necesitamos manejar líneas null
/vacías.
Entonces aceptamos la entrada hasta que sea un "número normal". Tanto la línea null
(cancelar) como la vacía también cumplen con esa condición, porque en forma numérica son 0
.
Después de detenernos, debemos tratar las líneas null
y vacías de manera especial (devolver null
), porque convertirlas en un número devolvería 0
.
Abra la solución con pruebas en un sandbox.
importancia: 4
Este bucle es infinito. Nunca termina. ¿Por qué?
sea yo = 0; mientras (yo! = 10) { yo += 0,2; }
Eso es porque nunca i
igual a 10
.
Ejecútelo para ver los valores reales de i
:
sea yo = 0; mientras (yo < 11) { yo += 0,2; si (i > 9.8 && i < 10.2) alerta (i); }
Ninguno de ellos mide exactamente 10
.
Estas cosas suceden debido a las pérdidas de precisión al sumar fracciones como 0.2
.
Conclusión: evite las comprobaciones de igualdad cuando trabaje con fracciones decimales.
importancia: 2
La función incorporada Math.random()
crea un valor aleatorio de 0
a 1
(sin incluir 1
).
Escriba la función random(min, max)
para generar un número aleatorio de punto flotante de min
a max
(sin incluir max
).
Ejemplos de su trabajo:
alerta (aleatorio (1, 5)); // 1.2345623452 alerta (aleatorio (1, 5)); // 3.7894332423 alerta (aleatorio (1, 5)); // 4.3435234525
Necesitamos "mapear" todos los valores del intervalo 0...1 en valores del min
al max
.
Esto se puede hacer en dos etapas:
Si multiplicamos un número aleatorio de 0…1 por max-min
, entonces el intervalo de valores posibles aumenta de 0..1
a 0..max-min
.
Ahora, si sumamos min
, el intervalo posible pasa a ser de min
a max
.
La función:
función aleatoria (mínimo, máximo) { return min + Math.random() * (max - min); } alerta (aleatorio (1, 5)); alerta (aleatorio (1, 5)); alerta (aleatorio (1, 5));
importancia: 2
Cree una función randomInteger(min, max)
que genere un número entero aleatorio desde min
hasta max
incluidos min
y max
como valores posibles.
Cualquier número del intervalo min..max
debe aparecer con la misma probabilidad.
Ejemplos de su trabajo:
alerta( entero aleatorio(1, 5) ); // 1 alerta( entero aleatorio(1, 5) ); // 3 alerta( entero aleatorio(1, 5) ); // 5
Puedes utilizar la solución de la tarea anterior como base.
La solución más simple, pero incorrecta, sería generar un valor del min
al max
y redondearlo:
función EnteroAleatorio(mínimo, máximo) { let rand = min + Math.random() * (max - min); devolver Math.round(rand); } alerta( entero aleatorio(1, 3) );
La función funciona, pero es incorrecta. La probabilidad de obtener valores de borde min
y max
es dos veces menor que cualquier otro.
Si ejecuta el ejemplo anterior muchas veces, verá fácilmente que 2
aparece con mayor frecuencia.
Esto sucede porque Math.round()
obtiene números aleatorios del intervalo 1..3
y los redondea de la siguiente manera:
los valores de 1 ... a 1,4999999999 se convierten en 1 los valores de 1,5 ... a 2,4999999999 se convierten en 2 los valores de 2,5 ... a 2,9999999999 se convierten en 3
Ahora podemos ver claramente que 1
obtiene el doble de valores que 2
. Y lo mismo con 3
.
Hay muchas soluciones correctas para la tarea. Uno de ellos es ajustar los límites de intervalo. Para asegurar los mismos intervalos, podemos generar valores de 0.5 to 3.5
, sumando así las probabilidades requeridas a las aristas:
función EnteroAleatorio(mínimo, máximo) { // ahora rand es de (min-0,5) a (max+0,5) let rand = min - 0.5 + Math.random() * (max - min + 1); devolver Math.round(rand); } alerta( entero aleatorio(1, 3) );
Una forma alternativa podría ser utilizar Math.floor
para un número aleatorio del min
al max+1
:
función EnteroAleatorio(mínimo, máximo) { // aquí rand es de min a (max+1) let rand = min + Math.random() * (max + 1 - min); devolver Math.floor(rand); } alerta( entero aleatorio(1, 3) );
Ahora todos los intervalos se asignan de esta manera:
los valores de 1 ... a 1,9999999999 se convierten en 1 los valores de 2 ... a 2,9999999999 se convierten en 2 los valores de 3... a 3,9999999999 se convierten en 3
Todos los intervalos tienen la misma longitud, lo que hace que la distribución final sea uniforme.