En JavaScript moderne, il existe deux types de nombres :
Les nombres normaux en JavaScript sont stockés au format 64 bits IEEE-754, également connu sous le nom de « nombres à virgule flottante double précision ». Ce sont des chiffres que nous utilisons la plupart du temps, et nous en parlerons dans ce chapitre.
Les nombres BigInt représentent des entiers de longueur arbitraire. Ils sont parfois nécessaires car un nombre entier régulier ne peut pas dépasser (2 53 -1)
ou être inférieur à -(2 53 -1)
en toute sécurité, comme nous l'avons mentionné plus tôt dans le chapitre Types de données. Comme les bigints sont utilisés dans quelques domaines particuliers, nous leur consacrons un chapitre spécial BigInt.
Nous parlerons donc ici de nombres réguliers. Développons nos connaissances à leur sujet.
Imaginez que nous devions écrire 1 milliard. La manière évidente est la suivante :
soit milliard = 1000000000 ;
Nous pouvons également utiliser le trait de soulignement _
comme séparateur :
soit milliard = 1_000_000_000 ;
Ici le trait de soulignement _
joue le rôle du « sucre syntaxique », il rend le nombre plus lisible. Le moteur JavaScript ignore simplement _
entre les chiffres, c'est donc exactement le même milliard que ci-dessus.
Cependant, dans la vraie vie, nous essayons d’éviter d’écrire de longues séquences de zéros. Nous sommes trop paresseux pour ça. Nous allons essayer d'écrire quelque chose comme "1bn"
pour un milliard ou "7.3bn"
pour 7 milliards 300 millions. Il en va de même pour la plupart des grands nombres.
En JavaScript, nous pouvons raccourcir un nombre en y ajoutant la lettre "e"
et en spécifiant le nombre de zéros :
soit milliard = 1e9 ; // 1 milliard, littéralement : 1 et 9 zéros alerte( 7.3e9 ); // 7,3 milliards (identique à 7300000000 ou 7_300_000_000)
En d’autres termes, e
multiplie le nombre par 1
avec le nombre de zéros donné.
1e3 === 1 * 1000 ; // e3 signifie *1000 1,23e6 === 1,23 * 1 000 000 ; // e6 signifie *1 000 000
Maintenant, écrivons quelque chose de très petit. Disons, 1 microseconde (un millionième de seconde) :
soit mсs = 0,000001 ;
Comme avant, utiliser "e"
peut aider. Si nous souhaitons éviter d’écrire explicitement les zéros, nous pourrions écrire la même chose ainsi :
soit mcs = 1e-6 ; // cinq zéros à gauche de 1
Si on compte les zéros dans 0.000001
, il y en a 6. Alors naturellement c'est 1e-6
.
Autrement dit, un nombre négatif après "e"
signifie une division par 1 avec le nombre de zéros donné :
// -3 divise par 1 avec 3 zéros 1e-3 === 1/1000 ; // 0,001 // -6 divise par 1 avec 6 zéros 1,23e-6 === 1,23 / 1 000 000 ; // 0,00000123 // un exemple avec un plus grand nombre 1234e-2 === 1234/100 ; // 12.34, la virgule décimale bouge 2 fois
Les nombres hexadécimaux sont largement utilisés en JavaScript pour représenter les couleurs, coder des caractères et pour bien d'autres choses. Alors naturellement, il existe une manière plus courte de les écrire : 0x
puis le nombre.
Par exemple:
alerte( 0xff ); // 255 alerte( 0xFF ); // 255 (pareil, la casse n'a pas d'importance)
Les systèmes numériques binaires et octaux sont rarement utilisés, mais sont également pris en charge en utilisant les préfixes 0b
et 0o
:
soit a = 0b11111111 ; // forme binaire de 255 soit b = 0o377 ; // forme octale de 255 alerte( a == b ); // vrai, le même nombre 255 des deux côtés
Il n'existe que 3 systèmes numériques avec un tel support. Pour les autres systèmes numériques, nous devrions utiliser la fonction parseInt
(que nous verrons plus tard dans ce chapitre).
La méthode num.toString(base)
renvoie une représentation sous forme de chaîne de num
dans le système numérique avec la base
donnée.
Par exemple:
soit num = 255 ; alerte( num.toString(16) ); //ff alert( num.toString(2) ); // 11111111
La base
peut varier de 2
à 36
. Par défaut, c'est 10
.
Les cas d'utilisation courants pour cela sont :
base=16 est utilisé pour les couleurs hexadécimales, les encodages de caractères, etc., les chiffres peuvent être 0..9
ou A..F
.
base=2 est principalement destiné au débogage des opérations au niveau du bit, les chiffres peuvent être 0
ou 1
.
base=36 est le maximum, les chiffres peuvent être 0..9
ou A..Z
. L’alphabet latin tout entier est utilisé pour représenter un nombre. Un cas amusant mais utile pour 36
est celui où nous devons transformer un long identifiant numérique en quelque chose de plus court, par exemple pour créer une URL courte. Peut simplement le représenter dans le système numérique en base 36
:
alerte( 123456..toString(36) ); // 2n9c
Deux points pour appeler une méthode
Veuillez noter que deux points dans 123456..toString(36)
ne sont pas une faute de frappe. Si nous voulons appeler une méthode directement sur un nombre, comme toString
dans l'exemple ci-dessus, alors nous devons placer deux points ..
après.
Si nous placions un seul point : 123456.toString(36)
, alors il y aurait une erreur, car la syntaxe JavaScript implique la partie décimale après le premier point. Et si nous plaçons un point supplémentaire, alors JavaScript sait que la partie décimale est vide et suit désormais la méthode.
Pourrait également écrire (123456).toString(36)
.
L’arrondi est l’une des opérations les plus utilisées lorsque l’on travaille avec des nombres.
Il existe plusieurs fonctions intégrées pour l'arrondi :
Math.floor
Arrondit à l'inférieur : 3.1
devient 3
et -1.1
devient -2
.
Math.ceil
Arrondit : 3.1
devient 4
et -1.1
devient -1
.
Math.round
Arrondit à l'entier le plus proche : 3.1
devient 3
, 3.6
devient 4
. Dans les cas du milieu, 3.5
arrondit à 4
et -3.5
arrondit à -3
.
Math.trunc
(non pris en charge par Internet Explorer)
Supprime tout ce qui se trouve après la virgule sans arrondir : 3.1
devient 3
, -1.1
devient -1
.
Voici le tableau pour résumer les différences entre eux :
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 |
Ces fonctions couvrent toutes les manières possibles de traiter la partie décimale d'un nombre. Mais que se passe-t-il si nous souhaitons arrondir le nombre au n-th
chiffre après la virgule ?
Par exemple, nous avons 1.2345
et souhaitons l’arrondir à 2 chiffres, pour obtenir seulement 1.23
.
Il existe deux manières de procéder :
Multiplier et diviser.
Par exemple, pour arrondir le nombre au 2ème chiffre après la virgule, nous pouvons multiplier le nombre par 100
, appeler la fonction d'arrondi puis le diviser.
soit num = 1,23456 ; alerte( Math.round(num * 100) / 100 ); // 1,23456 -> 123,456 -> 123 -> 1,23
La méthode toFixed(n) arrondit le nombre à n
chiffres après le point et renvoie une représentation sous forme de chaîne du résultat.
soit num = 12,34 ; alerte( num.toFixed(1) ); // "12.3"
Cela arrondit à la valeur la plus proche, similaire à Math.round
:
soit num = 12,36 ; alerte( num.toFixed(1) ); // "12.4"
Veuillez noter que le résultat de toFixed
est une chaîne. Si la partie décimale est plus courte que nécessaire, des zéros sont ajoutés à la fin :
soit num = 12,34 ; alerte( num.toFixed(5) ); // "12.34000", ajout de zéros pour faire exactement 5 chiffres
Nous pouvons le convertir en nombre en utilisant le plus unaire ou un appel Number()
, par exemple write +num.toFixed(5)
.
En interne, un nombre est représenté au format 64 bits IEEE-754, il y a donc exactement 64 bits pour stocker un nombre : 52 d'entre eux servent à stocker les chiffres, 11 d'entre eux stockent la position du point décimal et 1 bit c'est pour le signe.
Si un nombre est vraiment énorme, il peut déborder du stockage 64 bits et devenir une valeur numérique spéciale Infinity
:
alerte( 1e500 ); // Infini
Ce qui est peut-être un peu moins évident, mais qui arrive assez souvent, c'est la perte de précision.
Considérez ce (faux !) test d’égalité :
alerte( 0,1 + 0,2 == 0,3 ); // FAUX
C'est vrai, si nous vérifions si la somme de 0.1
et 0.2
est 0.3
, nous obtenons false
.
Étrange! Qu'est-ce que c'est alors sinon 0.3
?
alerte( 0,1 + 0,2 ); // 0,30000000000000004
Aie! Imaginez que vous créez un site d'achat en ligne et que le visiteur met des produits $0.10
et $0.20
dans son panier. Le total de la commande sera de $0.30000000000000004
. Cela surprendrait tout le monde.
Mais pourquoi cela arrive-t-il ?
Un nombre est stocké en mémoire sous sa forme binaire, une séquence de bits – des uns et des zéros. Mais des fractions comme 0.1
, 0.2
qui semblent simples dans le système numérique décimal sont en réalité des fractions interminables sous leur forme binaire.
alerte(0.1.toString(2)); // 0,0001100110011001100110011001100110011001100110011001101 alerte(0.2.toString(2)); // 0,001100110011001100110011001100110011001100110011001101 alerte((0.1 + 0.2).toString(2)); // 0,0100110011001100110011001100110011001100110011001101
Qu'est-ce que 0.1
? C'est un divisé par dix 1/10
, un dixième. Dans le système numérique décimal, ces nombres sont facilement représentables. Comparez-le à un tiers : 1/3
. Cela devient une fraction sans fin 0.33333(3)
.
Ainsi, la division par puissances 10
est garantie de bien fonctionner dans le système décimal, mais pas la division par 3
. Pour la même raison, dans le système numérique binaire, la division par puissances de 2
est garantie de fonctionner, mais 1/10
devient une fraction binaire sans fin.
Il n'y a tout simplement aucun moyen de stocker exactement 0,1 ou exactement 0,2 en utilisant le système binaire, tout comme il n'y a aucun moyen de stocker un tiers sous forme de fraction décimale.
Le format numérique IEEE-754 résout ce problème en arrondissant au nombre le plus proche possible. Ces règles d'arrondi ne nous permettent normalement pas de constater cette « infime perte de précision », mais elle existe.
Nous pouvons voir cela en action :
alerte( 0.1.toFixed(20) ); // 0,10000000000000000555
Et lorsque nous additionnons deux nombres, leurs « pertes de précision » s’additionnent.
C'est pourquoi 0.1 + 0.2
n'est pas exactement 0.3
.
Pas seulement JavaScript
Le même problème existe dans de nombreux autres langages de programmation.
PHP, Java, C, Perl et Ruby donnent exactement le même résultat, car ils sont basés sur le même format numérique.
Pouvons-nous contourner le problème ? Bien sûr, la méthode la plus fiable consiste à arrondir le résultat à l’aide d’une méthode toFixed(n) :
soit somme = 0,1 + 0,2 ; alerte( sum.toFixed(2) ); // "0,30"
Veuillez noter que toFixed
renvoie toujours une chaîne. Il garantit qu'il comporte 2 chiffres après la virgule décimale. C'est en fait pratique si nous avons un e-shopping et que nous devons afficher $0.30
. Dans d’autres cas, nous pouvons utiliser le plus unaire pour le contraindre à un nombre :
soit somme = 0,1 + 0,2 ; alerte( +sum.toFixed(2) ); // 0,3
Nous pouvons également temporairement multiplier les nombres par 100 (ou un nombre plus grand) pour les transformer en nombres entiers, faire le calcul, puis diviser. Ensuite, à mesure que nous faisons des calculs avec des nombres entiers, l'erreur diminue quelque peu, mais nous l'obtenons toujours par division :
alerte( (0,1 * 10 + 0,2 * 10) / 10 ); // 0,3 alerte( (0,28 * 100 + 0,14 * 100) / 100); // 0,4200000000000001
Ainsi, l’approche multiplier/diviser réduit l’erreur, mais ne la supprime pas totalement.
Parfois, nous pourrions essayer d’éviter les fractions. Comme si nous avions affaire à un magasin, nous pouvons alors stocker les prix en centimes au lieu de dollars. Mais que se passe-t-il si nous appliquons une réduction de 30 % ? En pratique, il est rarement possible d’éviter totalement les fractions. Il suffit de les arrondir pour couper les « queues » si nécessaire.
Le truc drôle
Essayez d'exécuter ceci :
// Bonjour! Je suis un nombre auto-croissant ! alerte( 9999999999999999 ); // affiche 10000000000000000
Cela souffre du même problème : une perte de précision. Il y a 64 bits pour le nombre, 52 d'entre eux peuvent être utilisés pour stocker des chiffres, mais ce n'est pas suffisant. Les chiffres les moins significatifs disparaissent donc.
JavaScript ne déclenche pas d'erreur dans de tels événements. Il fait de son mieux pour insérer le numéro dans le format souhaité, mais malheureusement, ce format n'est pas assez grand.
Deux zéros
Une autre conséquence amusante de la représentation interne des nombres est l'existence de deux zéros : 0
et -0
.
En effet, un signe est représenté par un seul bit, il peut donc être défini ou non pour n'importe quel nombre incluant un zéro.
Dans la plupart des cas, la distinction est imperceptible, car les opérateurs sont aptes à les traiter de la même manière.
Vous vous souvenez de ces deux valeurs numériques spéciales ?
Infinity
(et -Infinity
) est une valeur numérique spéciale qui est supérieure (inférieure) à tout.
NaN
représente une erreur.
Ils appartiennent au type number
, mais ne sont pas des nombres « normaux », il existe donc des fonctions spéciales pour les vérifier :
isNaN(value)
convertit son argument en nombre puis le teste pour être NaN
:
alert( estNaN(NaN) ); // vrai alert( isNaN("str") ); // vrai
Mais avons-nous besoin de cette fonction ? Ne pouvons-nous pas simplement utiliser la comparaison === NaN
? Malheureusement non. La valeur NaN
est unique dans le sens où elle n'est égale à rien, y compris elle-même :
alerte( NaN === NaN ); // FAUX
isFinite(value)
convertit son argument en nombre et renvoie true
s'il s'agit d'un nombre régulier, pas NaN/Infinity/-Infinity
:
alert( isFinite("15") ); // vrai alert( isFinite("str") ); // faux, car une valeur spéciale : NaN alert( isFinite(Infinity) ); // faux, car une valeur spéciale : Infinity
Parfois, isFinite
est utilisé pour valider si une valeur de chaîne est un nombre régulier :
let num = +prompt("Entrez un nombre", ''); // sera vrai sauf si vous entrez Infinity, -Infinity ou pas un nombre alert( isFinite(num) );
Veuillez noter qu'une chaîne vide ou contenant uniquement des espaces est traitée comme 0
dans toutes les fonctions numériques, y compris isFinite
.
Number.isNaN
et Number.isFinite
Les méthodes Number.isNaN et Number.isFinite sont les versions les plus « strictes » des fonctions isNaN
et isFinite
. Ils ne convertissent pas automatiquement leur argument en nombre, mais vérifient plutôt s’il appartient au type number
.
Number.isNaN(value)
renvoie true
si l'argument appartient au type number
et qu'il est NaN
. Dans tous les autres cas, il renvoie false
.
alert( Number.isNaN(NaN) ); // vrai alert( Number.isNaN("str" / 2) ); // vrai // Notez la différence : alert( Number.isNaN("str") ); // faux, car "str" appartient au type chaîne, pas au type nombre alert( isNaN("str") ); // vrai, car isNaN convertit la chaîne "str" en nombre et obtient NaN suite à cette conversion
Number.isFinite(value)
renvoie true
si l'argument appartient au type number
et qu'il n'est pas NaN/Infinity/-Infinity
. Dans tous les autres cas, il renvoie false
.
alerte( Number.isFinite(123) ); // vrai alerte( Number.isFinite(Infinity) ); // FAUX alerte( Number.isFinite(2 / 0) ); // FAUX // Notez la différence : alerte( Number.isFinite("123") ); // faux, car "123" appartient au type chaîne, pas au type nombre alerte( isFinite("123") ); // vrai, car isFinite convertit la chaîne "123" en nombre 123
D'une certaine manière, Number.isNaN
et Number.isFinite
sont plus simples et plus directs que les fonctions isNaN
et isFinite
. En pratique cependant, isNaN
et isFinite
sont principalement utilisés, car ils sont plus courts à écrire.
Comparaison avec Object.is
Il existe une méthode intégrée spéciale Object.is
qui compare des valeurs telles que ===
, mais qui est plus fiable pour deux cas extrêmes :
Cela fonctionne avec NaN
: Object.is(NaN, NaN) === true
, c'est une bonne chose.
Les valeurs 0
et -0
sont différentes : Object.is(0, -0) === false
, techniquement, c'est correct car en interne, le nombre a un bit de signe qui peut être différent même si tous les autres bits sont des zéros.
Dans tous les autres cas, Object.is(a, b)
est identique à a === b
.
Nous mentionnons Object.is
ici, car il est souvent utilisé dans les spécifications JavaScript. Lorsqu'un algorithme interne doit comparer deux valeurs pour être exactement identiques, il utilise Object.is
(appelé en interne SameValue).
La conversion numérique utilisant un plus +
ou Number()
est stricte. Si une valeur n’est pas exactement un nombre, cela échoue :
alerte( +"100px" ); // NaN
La seule exception concerne les espaces au début ou à la fin de la chaîne, car ils sont ignorés.
Mais dans la vraie vie, nous avons souvent des valeurs en unités, comme "100px"
ou "12pt"
en CSS. De plus, dans de nombreux pays, le symbole monétaire se trouve après le montant, nous avons donc "19€"
et aimerions en extraire une valeur numérique.
C'est à cela que servent parseInt
et parseFloat
.
Ils « lisent » un nombre à partir d'une chaîne jusqu'à ce qu'ils n'y parviennent plus. En cas d'erreur, le numéro collecté est renvoyé. La fonction parseInt
renvoie un entier, tandis que parseFloat
renvoie un nombre à virgule flottante :
alerte( parseInt('100px') ); // 100 alerte( parseFloat('12.5em') ); // 12,5 alerte( parseInt('12.3') ); // 12, seule la partie entière est renvoyée alerte( parseFloat('12.3.4') ); // 12.3, le deuxième point arrête la lecture
Il existe des situations où parseInt/parseFloat
renvoie NaN
. Cela se produit lorsqu'aucun chiffre n'a pu être lu :
alerte( parseInt('a123') ); // NaN, le premier symbole arrête le processus
Le deuxième argument de parseInt(str, radix)
La fonction parseInt()
a un deuxième paramètre facultatif. Il spécifie la base du système numérique, donc parseInt
peut également analyser des chaînes de nombres hexadécimaux, de nombres binaires, etc. :
alert( parseInt('0xff', 16) ); // 255 alert( parseInt('ff', 16) ); // 255, sans 0x fonctionne aussi alerte( parseInt('2n9c', 36) ); // 123456
JavaScript possède un objet Math intégré qui contient une petite bibliothèque de fonctions et de constantes mathématiques.
Quelques exemples :
Math.random()
Renvoie un nombre aléatoire de 0 à 1 (sans compter 1).
alert( Math.random() ); // 0,1234567894322 alert( Math.random() ); // 0,5435252343232 alert( Math.random() ); // ... (n'importe quel nombre aléatoire)
Math.max(a, b, c...)
et Math.min(a, b, c...)
Renvoie le plus grand et le plus petit parmi un nombre arbitraire d'arguments.
alerte( Math.max(3, 5, -10, 0, 1) ); // 5 alerte( Math.min(1, 2) ); // 1
Math.pow(n, power)
Renvoie n
élevé à la puissance donnée.
alerte( Math.pow(2, 10) ); // 2 en puissance 10 = 1024
Il existe davantage de fonctions et de constantes dans l'objet Math
, notamment la trigonométrie, que vous pouvez trouver dans la documentation de l'objet Math.
Pour écrire des nombres avec plusieurs zéros :
Ajoutez "e"
avec le nombre de zéros au nombre. Comme : 123e6
est identique à 123
avec 6 zéros 123000000
.
Un nombre négatif après "e"
entraîne la division du nombre par 1 avec des zéros donnés. Par exemple, 123e-6
signifie 0.000123
( 123
millionièmes).
Pour différents systèmes numériques :
Peut écrire des nombres directement dans les systèmes hexadécimal ( 0x
), octal ( 0o
) et binaire ( 0b
).
parseInt(str, base)
analyse la chaîne str
en un entier dans un système numérique avec base
donnée, 2 ≤ base ≤ 36
.
num.toString(base)
convertit un nombre en chaîne dans le système numérique avec la base
donnée.
Pour les tests de nombres réguliers :
isNaN(value)
convertit son argument en nombre puis le teste pour être NaN
Number.isNaN(value)
vérifie si son argument appartient au type number
et, si tel est le cas, le teste pour être NaN
isFinite(value)
convertit son argument en nombre puis le teste pour s'assurer qu'il n'est pas NaN/Infinity/-Infinity
Number.isFinite(value)
vérifie si son argument appartient au type number
, et si c'est le cas, le teste pour s'assurer qu'il n'est pas NaN/Infinity/-Infinity
Pour convertir des valeurs telles que 12pt
et 100px
en un nombre :
Utilisez parseInt/parseFloat
pour la conversion « douce », qui lit un nombre à partir d'une chaîne, puis renvoie la valeur qu'ils pouvaient lire avant l'erreur.
Pour les fractions :
Arrondissez en utilisant Math.floor
, Math.ceil
, Math.trunc
, Math.round
ou num.toFixed(precision)
.
N'oubliez pas qu'il y a une perte de précision lorsque vous travaillez avec des fractions.
Plus de fonctions mathématiques :
Consultez l'objet Math lorsque vous en avez besoin. La bibliothèque est très petite mais peut couvrir les besoins de base.
importance : 5
Créez un script qui invite le visiteur à saisir deux nombres, puis affiche leur somme.
Exécutez la démo
PS Il y a un piège avec les types.
let a = +prompt("Le premier numéro ?", ""); let b = +prompt("Le deuxième numéro ?", ""); alerte( a + b );
Notez le unaire plus +
avant prompt
. Il convertit immédiatement la valeur en nombre.
Sinon, a
et b
seraient des chaînes, leur somme serait leur concaténation, c'est-à-dire : "1" + "2" = "12"
.
importance : 4
Selon la documentation Math.round
et toFixed
arrondissent tous deux au nombre le plus proche : 0..4
avance vers le bas tandis que 5..9
avance vers le haut.
Par exemple:
alerte( 1.35.toFixed(1) ); // 1.4
Dans l'exemple similaire ci-dessous, pourquoi 6.35
est-il arrondi à 6.3
, et non 6.4
?
alerte( 6.35.toFixed(1) ); // 6.3
Comment arrondir 6.35
dans le bon sens ?
En interne, la fraction décimale 6.35
est un binaire sans fin. Comme toujours dans de tels cas, il est stocké avec une perte de précision.
Voyons:
alerte( 6.35.toFixed(20) ); // 6.349999999999999964473
La perte de précision peut entraîner à la fois une augmentation et une diminution d'un nombre. Dans ce cas particulier, le nombre devient un peu inférieur, c'est pourquoi il est arrondi à l'inférieur.
Et c'est quoi pour 1.35
?
alerte( 1.35.toFixed(20) ); // 1.35000000000000008882
Ici, la perte de précision a rendu le nombre un peu plus grand, donc il a été arrondi.
Comment pouvons-nous résoudre le problème de 6.35
si nous voulons qu’il soit arrondi dans le bon sens ?
Nous devrions le rapprocher d'un nombre entier avant d'arrondir :
alerte( (6,35 * 10).toFixed(20) ); // 63,50000000000000000000
Notez que 63.5
n’a aucune perte de précision. C'est parce que la partie décimale 0.5
est en réalité 1/2
. Les fractions divisées par des puissances de 2
sont représentées exactement dans le système binaire, on peut maintenant l'arrondir :
alerte( Math.round(6.35 * 10) / 10 ); // 6,35 -> 63,5 -> 64 (arrondi) -> 6,4
importance : 5
Créez une fonction readNumber
qui demande un nombre jusqu'à ce que le visiteur saisisse une valeur numérique valide.
La valeur résultante doit être renvoyée sous forme de nombre.
Le visiteur peut également arrêter le processus en saisissant une ligne vide ou en appuyant sur « ANNULER ». Dans ce cas, la fonction doit renvoyer null
.
Exécutez la démo
Ouvrez un bac à sable avec des tests.
fonction readNumber() { laissez num; faire { num = prompt("Entrez un numéro s'il vous plaît ?", 0); } while ( !isFinite(num) ); if (num === null || num === '') renvoie null ; return +num; } alert(`Lire : ${readNumber()}`);
La solution est un peu plus complexe qu'elle pourrait l'être car nous devons gérer des lignes null
/vides.
Nous acceptons donc l'entrée jusqu'à ce qu'il s'agisse d'un « nombre normal ». Les lignes null
(annulation) et vide correspondent également à cette condition, car sous forme numérique, elles sont 0
.
Après nous être arrêtés, nous devons traiter spécialement les lignes null
et vides (retourner null
), car les convertir en nombre renverrait 0
.
Ouvrez la solution avec des tests dans un bac à sable.
importance : 4
Cette boucle est infinie. Cela ne finit jamais. Pourquoi?
soit je = 0 ; tandis que (je != 10) { je += 0,2 ; }
C'est parce que i
n'atteindrais jamais 10
.
Exécutez-le pour voir les valeurs réelles de i
:
soit je = 0 ; tandis que (i < 11) { je += 0,2 ; si (je > 9,8 && je < 10,2) alerte( je ); }
Aucun d'entre eux ne vaut exactement 10
.
De telles choses se produisent à cause des pertes de précision lors de l'ajout de fractions telles que 0.2
.
Conclusion : évitez les contrôles d'égalité lorsque vous travaillez avec des fractions décimales.
importance : 2
La fonction intégrée Math.random()
crée une valeur aléatoire de 0
à 1
(sans compter 1
).
Écrivez la fonction random(min, max)
pour générer un nombre aléatoire à virgule flottante de min
à max
(sans compter max
).
Exemples de ses travaux :
alerte( aléatoire(1, 5) ); // 1.2345623452 alerte( aléatoire(1, 5) ); // 3.7894332423 alerte( aléatoire(1, 5) ); // 4.3435234525
Nous devons « mapper » toutes les valeurs de l'intervalle 0…1 en valeurs de min
à max
.
Cela peut se faire en deux étapes :
Si nous multiplions un nombre aléatoire de 0…1 par max-min
, alors l'intervalle des valeurs possibles augmente de 0..1
à 0..max-min
.
Maintenant, si nous ajoutons min
, l'intervalle possible devient de min
à max
.
La fonction :
fonction aléatoire (min, max) { return min + Math.random() * (max - min); } alerte( aléatoire(1, 5) ); alerte( aléatoire(1, 5) ); alerte( aléatoire(1, 5) );
importance : 2
Créez une fonction randomInteger(min, max)
qui génère un nombre entier aléatoire de min
à max
incluant min
et max
comme valeurs possibles.
Tout nombre de l'intervalle min..max
doit apparaître avec la même probabilité.
Exemples de ses travaux :
alerte( randomInteger(1, 5) ); // 1 alerte( randomInteger(1, 5) ); // 3 alerte( randomInteger(1, 5) ); // 5
Vous pouvez utiliser la solution de la tâche précédente comme base.
La solution la plus simple, mais erronée, serait de générer une valeur du min
au max
et de l'arrondir :
fonction randomInteger (min, max) { soit rand = min + Math.random() * (max - min); return Math.round(rand); } alerte( randomInteger(1, 3) );
La fonction fonctionne, mais elle est incorrecte. La probabilité d'obtenir les valeurs de bord min
et max
est deux fois inférieure à toute autre.
Si vous exécutez l’exemple ci-dessus plusieurs fois, vous verrez facilement que 2
apparaît le plus souvent.
Cela se produit parce que Math.round()
obtient des nombres aléatoires de l'intervalle 1..3
et les arrondit comme suit :
les valeurs de 1 ... à 1,4999999999 deviennent 1 les valeurs de 1,5 ... à 2,4999999999 deviennent 2 les valeurs de 2,5 ... à 2,9999999999 deviennent 3
Nous pouvons maintenant clairement voir que 1
obtient deux fois moins de valeurs que 2
. Et pareil avec 3
.
Il existe de nombreuses solutions correctes à la tâche. L'un d'eux consiste à ajuster les limites des intervalles. Pour garantir les mêmes intervalles, nous pouvons générer des valeurs de 0.5 to 3.5
, ajoutant ainsi les probabilités requises aux arêtes :
fonction randomInteger (min, max) { // maintenant le rand va de (min-0,5) à (max+0,5) soit rand = min - 0,5 + Math.random() * (max - min + 1); return Math.round(rand); } alerte( randomInteger(1, 3) );
Une autre façon pourrait être d'utiliser Math.floor
pour un nombre aléatoire de min
à max+1
:
fonction randomInteger (min, max) { // ici, le rand va de min à (max+1) soit rand = min + Math.random() * (max + 1 - min); return Math.floor(rand); } alerte( randomInteger(1, 3) );
Désormais, tous les intervalles sont mappés de cette façon :
les valeurs de 1 ... à 1,9999999999 deviennent 1 les valeurs de 2 ... à 2,9999999999 deviennent 2 les valeurs de 3 ... à 3,9999999999 deviennent 3
Tous les intervalles ont la même longueur, ce qui rend la répartition finale uniforme.