🌐 AIæœçŽą & 代理 䞻饔
18 juin 2023

Nombres

En JavaScript moderne, il existe deux types de nombres :

  1. Les nombres standards 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 le plus souvent, et nous en parlerons dans ce chapitre.

  2. Les nombres BigInt pour reprĂ©senter des entiers de longueur arbitraire. Ils sont parfois nĂ©cessaires, car un nombre rĂ©gulier ne peut pas dĂ©passer de maniĂšre prĂ©cise 253 ou ĂȘtre infĂ©rieur Ă  -253. Comme les bigints sont utilisĂ©s dans quelques zones spĂ©ciales, nous leur consacrons un chapitre spĂ©cial BigInt.

Nous allons donc parler ici des nombres réguliers. Développons nos connaissances à leur sujet.

Plus de façons d’écrire un nombre

Imaginez que nous ayons besoin d’écrire 1 milliard. Le moyen Ă©vident est :

let milliard = 1000000000;

Nous pouvons Ă©galement utiliser l’underscore _ comme sĂ©parateur :

let billion = 1_000_000_000;

Ici, l’underscore _ joue le rĂŽle de “sucre syntaxique”, il rend le nombre plus lisible. Le moteur JavaScript ignore simplement _ entre les chiffres, donc c’est exactement le mĂȘme milliard que ci-dessus.

Dans la vraie vie cependant, nous essayons d’éviter d’écrire de longues sĂ©quences de zĂ©ros. Nous sommes trop paresseux pour ça. Nous essaierons d’écrire quelque chose comme “1 milliard” pour un milliard ou “7,3 milliards” pour 7 milliards 300 millions. La mĂȘme chose est vraie 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 :

let milliard = 1e9;  // 1 milliard, littéralement : 1 suivi de 9 zéros

alert( 7.3e9 );  // 7.3 milliards (pareil que 7300000000 ou 7_300_000_000)

En d’autres termes, e multiplie le nombre par 1 suivi du nombre de zĂ©ros spĂ©cifiĂ©.

1e3 === 1 * 1000 // e3 signifie *1000
1.23e6 === 1.23 * 1000000 // e6 signifie *1000000

Maintenant, écrivons quelque chose de trÚs petit. Disons, 1 microseconde (un millioniÚme de seconde) :

let us = 0.000001;

Comme avant, l’utilisation de "e" peut nous aider. Si nous voulons Ă©viter d’écrire les zĂ©ros explicitement, nous pourrions dire la mĂȘme chose comme :

let us = 1e-6; // cinq zéros à gauche de 1

Si nous comptons les zĂ©ros dans 0.000001, il y en a 6. Donc logiquement, c’est 1e-6.

En d’autres termes, un nombre nĂ©gatif aprĂšs "e" signifie une division par 1 suivi du nombre spĂ©cifiĂ© de zĂ©ros :

// -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 / 1000000; // 0.00000123

// an example with a bigger number
1234e-2 === 1234 / 100; // 12.34, decimal point moves 2 times

Nombres hexadécimaux, binaires et octaux

Les nombres HexadĂ©cimaux sont souvent utilisĂ©s en JavaScript pour reprĂ©senter des couleurs, encoder des caractĂšres et pour beaucoup d’autres choses. Alors, naturellement, il existe un moyen plus court de les Ă©crire : 0x puis le nombre.

Par exemple :

alert( 0xff ); // 255
alert( 0xFF ); // 255 (mĂȘme rĂ©sultat car la casse n'a pas d'importance)

Les systÚmes numériques binaires et octaux sont rarement utilisés, mais sont également supportés avec les préfixes 0b et 0o :

let a = 0b11111111; // forme binaire de 255
let b = 0o377; // forme octale de 255

alert( a == b ); // true, car a et b valent le mĂȘme nombre, soit 255

Cependant ça ne fonctionne qu’avec ces 3 systĂšmes de numĂ©ration. Pour les autres systĂšmes numĂ©rique, nous devrions utiliser la fonction parseInt (que nous verrons plus loin dans ce chapitre).

La méthode toString(base)

La méthode num.toString(base) retourne une chaßne de caractÚres représentant num dans le systÚme numérique de la base donnée.

Par exemple :

let num = 255;

alert( num.toString(16) ); // ff
alert( num.toString(2) );  // 11111111

La base peut varier de 2 Ă  36. Par dĂ©faut, il s’agit de 10.

Les cas d’utilisation courants 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 utilisĂ© pour le dĂ©bogage d’opĂ©rations binaires, les chiffres pouvant ĂȘtre 0 ou 1.

  • base=36 est le maximum, les chiffres peuvent ĂȘtre 0..9 ou A..Z. L’alphabet latin entier est utilisĂ© pour reprĂ©senter un nombre. Un cas amusant mais utile pour la base 36 consiste Ă  transformer un identifiant numĂ©rique long en quelque chose de plus court, par exemple pour crĂ©er une URL courte. On peut simplement le reprĂ©senter dans le systĂšme numĂ©rique avec base 36 :

    alert( 123456..toString(36) ); // 2n9c
Deux points pour appeler une méthode

Veuillez noter que deux points sur 123456..toString(36) n’est pas une faute de frappe. Si nous voulons appeler une mĂ©thode directement sur un nombre, comme toString dans l’exemple ci-dessus, nous devons placer deux points .. avant la mĂ©thode.

Si nous plaçions un seul point : 123456.toString(36), il y aurait une erreur, car la syntaxe JavaScript applique la partie dĂ©cimale aprĂšs le premier point et il lirait toString(36) comme Ă©tant la partie dĂ©cimale de 123456 ce qui n’est pas le cas. Si nous plaçons un point de plus, alors JavaScript sait que la partie dĂ©cimale est vide et peut maintenant appliquer la mĂ©thode.

Il est aussi possible d’écrire (123456).toString(36).

Arrondir

Arrondir est l’une des opĂ©rations les plus utilisĂ©es pour travailler avec des nombres.

Il existe plusieurs fonctions intégrées pour arrondir :

Math.floor
Arrondis vers le bas : 3.1 devient 3, et -1.1 devient -2.
Math.ceil
Arrondis ver le haut : 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 et pour le cas du milieu, 3,5 est Ă©galement arrondit Ă  4.
Math.trunc (non pris en charge par Internet Explorer)
Supprime tout ce qui suit le point décimal : 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.6 3 4 4 3
-1.1 -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 voulons arrondir le nombre Ă  un certain chiffre aprĂšs la virgule ?

Par exemple, nous avons 1.2345 et voulons l’arrondir à 2 chiffres, pour obtenir seulement 1.23.

Il y a deux façons de le faire:

  1. Multiplier et diviser.

    Par exemple, pour arrondir le nombre au 2Ăšme chiffre aprĂšs la dĂ©cimale, nous pouvons multiplier le nombre par “100”, appeler la fonction d’arrondi puis le diviser.

    let num = 1.23456;
    
    alert( Math.round(num * 100) / 100 ); // 1.23456 -> 123.456 -> 123 -> 1.23
  2. La méthode toFixed(n) arrondit le nombre à n chiffres aprÚs le point et renvoie une chaßne de caractÚres du résultat.

    let num = 12.34;
    alert( num.toFixed(1) ); // "12.3"

    Ceci arrondit Ă  la valeur la plus proche, similaire Ă  Math.round :

    let num = 12.36;
    alert( num.toFixed(1) ); // "12.4"

    Veuillez noter que le rĂ©sultat de toFixed est une chaĂźne de caractĂšres. Si la partie dĂ©cimale est plus courte qu’indiquĂ©e, des zĂ©ros sont ajoutĂ©s Ă  la fin :

    let num = 12.34;
    alert( num.toFixed(5) ); // "12.34000", ajout de zéros pour faire exactement 5 chiffres

    Nous pouvons le convertir en un nombre en utilisant le plus unaire + ou un appel Number() : +num.toFixed(5).

Calculs imprécis

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 sont utilisĂ©s pour stocker les chiffres, 11 d’entre eux stockent la position du point dĂ©cimal (ils sont zĂ©ro pour les nombres entiers), et 1 bit 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 :

alert( 1e500 ); // infini

Ce qui est peut-ĂȘtre un peu moins Ă©vident, mais qui arrive souvent, est la perte de prĂ©cision.

ConsidĂ©rez ce (faux !) test d’égalitĂ© :

alert( 0.1 + 0.2 == 0.3 ); // faux

Si on vérifie si la somme de 0.1 et 0.2 est égale à 0.3 on obtient faux.

Étrange ! Qu’est-ce que c’est alors si ce n’est pas 0.3?

alert( 0.1 + 0.2 ); // 0.30000000000000004

Aie ! Imaginez que vous crĂ©iez un site d’e-commerce et que le visiteur mette des produits Ă  0,10 € et 0,20 € dans son panier. Le total de la commande sera de 0,30000000000000004 €. Cela surprendrait n’importe qui.

Mais pourquoi cela se produit-il ?

Un nombre est stocké en mémoire sous sa forme binaire, une séquence de uns et de zéros. Mais les fractions telles que 0.1, 0.2, qui semblent simples dans le systÚme numérique décimal, sont en réalité des fractions sans fin dans leur forme binaire.

En d’autres termes, 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 la division par 3 ne l’est pas. Pour la mĂȘme raison, dans le systĂšme de numĂ©ration binaire, la division par des puissances de 2 est garantie, mais 1/10 devient une fraction binaire sans fin.

Il n’existe aucun moyen de stocker exactement 0.1 ou exactement 0.2 Ă  l’aide du systĂšme binaire, tout comme il n’existe 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’arrondissement ne nous permettent normalement pas de voir cette “petite perte de prĂ©cision”, mais elle existe.

Nous pouvons voir cela en action :

alert( 0.1.toFixed(20) ); // 0.10000000000000000555

Et quand on additionne 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, 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 est d’arrondir le rĂ©sultat Ă  l’aide d’une mĂ©thode toFixed(n):

let sum = 0.1 + 0.2;
alert( sum.toFixed(2) ); // "0.30"

Veuillez noter que toFixed renvoie toujours une chaĂźne de caractĂšres. Il s’assure qu’il a 2 chiffres aprĂšs le point dĂ©cimal. C’est pratique si nous avons un magasin en ligne et devons montrer 0.30$. Pour les autres cas, nous pouvons utiliser le plus unaire + pour le contraindre en un nombre :

let sum = 0.1 + 0.2;
alert( +sum.toFixed(2) ); // 0.3

Nous pouvons Ă©galement multiplier temporairement les nombres par 100 (ou un nombre plus grand) pour les transformer en nombres entiers, faire le calcul, puis rediviser. Ensuite, comme nous faisons des calculs avec des nombres entiers, l’erreur diminue quelque peu, mais nous l’obtenons toujours sur la division :

alert( (0.1 * 10 + 0.2 * 10) / 10 ); // 0.3
alert( (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 complĂštement. Par exemple, si nous traitons avec un magasin, nous pouvons stocker les prix en cents 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.

La chose amusante

Essayez de lancer ceci :

// Bonjour! Je suis un nombre auto-croissant!
alert( 9999999999999999 ); // affiche 10000000000000000

La cause est encore une fois le manque de prĂ©cision. Le nombre comporte 64 bits, dont 52 peuvent ĂȘtre utilisĂ©s pour stocker des chiffres, mais cela ne suffit pas. Alors, les chiffres les moins significatifs disparaissent.

JavaScript ne dĂ©clenche pas d’erreur dans de tels Ă©vĂ©nements. il fait de son mieux pour adapter le nombre au 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.

C’est parce que le signe est reprĂ©sentĂ© par un bit, il peut donc ĂȘtre dĂ©fini ou non pour n’importe quel nombre, y compris le zĂ©ro.

Dans le plupart des cas, la distinction est imperceptible, car les opĂ©rateurs peuvent les traiter de la mĂȘme maniĂšre.

Tests : isFinite et isNaN

Vous rappelez-vous de ces deux valeurs numériques spéciales ?

  • Infinite (et -Infinite) sont des valeurs numĂ©riques spĂ©ciales qui sont supĂ©rieures (infĂ©rieure) Ă  tout.
  • NaN reprĂ©sente une erreur.

Ils appartiennent au type number, mais ne sont pas des numĂ©ros “normaux”. Il existe donc des fonctions spĂ©ciales pour les vĂ©rifier :

  • isNaN(valeur) convertit son argument en un nombre et teste ensuite s’il est NaN :

    alert( isNaN(NaN) ); // true
    alert( isNaN("str") ); // true

    Mais avons-nous besoin de cette fonction ? Ne pouvons-nous pas simplement utiliser la comparaison === NaN ? Malheureusement non. La valeur NaN est unique en ce sens qu’elle ne vaut rien, y compris elle-mĂȘme :

    alert( NaN === NaN ); // false
  • isFinite(valeur) convertit son argument en un nombre et renvoie true s’il s’agit d’un nombre rĂ©gulier, pas de NaN / Infinity / -Infinity :

    alert( isFinite("15") ); // true
    alert( isFinite("str") ); // false, car c'est une valeur non réguliÚre: NaN
    alert( isFinite(Infinity) ); // false, car c'est aussi une valeur non réguliÚre: Infinity

Parfois, isFinite est utilisé pour valider si une valeur de chaßne de caractÚres est un nombre régulier :

let num = +prompt("Entrez un nombre", '');

// sera vrai, sauf si vous entrez Infinity, -Infinity ou NaN
alert( isFinite(num) );

Veuillez noter qu’une chaĂźne de caractĂšres vide ou une chaĂźne de caractĂšres contenant seulement des espaces est traitĂ©e comme 0 dans toutes les fonctions numĂ©rique, y compris isFinite.

Number.isNaN et Number.isFinite

Les mĂ©thodes Number.isNaN et Number.isFinite sont des versions plus “strictes” des fonctions isNaN et isFinite. Elles ne convertissent pas automatiquement leur argument en nombre, mais vĂ©rifient s’il appartient au type number.

  • Number.isNaN(value) retourne true si l’argument appartient au type number et s’il vaut NaN. Dans tous les autres cas, elle retourne false.

    alert( Number.isNaN(NaN) ); // true
    alert( Number.isNaN("str" / 2) ); // true
    
    // Notez la différence :
    alert( Number.isNaN("str") ); // false, car "str" est de type "string"
    alert( isNaN("str") ); // true, car isNaN convertit la string "str" en un nombre et obtient NaN comme résultatde la conversion
  • Number.isFinite(value) retourne true si l’argument appartient au type number et s’il vaut ni NaN, ni Infinity, ni -Infinity. Dans tous les autres cas, elle retourne false.

    alert( Number.isFinite(123) ); // true
    alert( Number.isFinite(Infinity) ); // false
    alert( Number.isFinite(2 / 0) ); // false
    
    // Notez la différence :
    alert( Number.isFinite("123") ); // false, car "123" est de type "string"
    alert( isFinite("123") ); // true, car isFinite convertit la string "123" en un nombre 123

En quelque sorte, les fonction Number.isNaN et Number.isFinite sont plus simples et plus directes que les fonctions isNaN et isFinite. Cependant, en pratique isNaN et isFinite sont plus souvent utilisées car elles sont plus courtes à écrire.

Comparer 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 :

  1. Cela fonctionne avec NaN : Object.is(NaN, NaN) === true, c’est une bonne chose.
  2. Les valeurs 0 et -0 sont diffĂ©rentes : Object.is(0, -0) === false, techniquement c’est vrai, car le numĂ©ro a en interne un bit de signe qui peut ĂȘtre diffĂ©rent mĂȘme si tous les autres bits sont Ă  zĂ©ro.

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).

parseInt et parseFloat

La conversion numĂ©rique Ă  l’aide d’un plus + ou Number() est strict. Si une valeur n’est pas exactement un nombre, elle Ă©choue :

alert( +"100px" ); // NaN

La seule exception concerne les espaces au début ou à la fin de la chaßne de caractÚres, car ils sont ignorés.

Mais dans la vraie vie, nous avons souvent des valeurs en unitĂ©s, comme "100px" ou "12pt" en CSS. En outre, dans de nombreux pays, le symbole monĂ©taire se situe aprĂšs le montant. Nous avons donc "19€" et souhaitons en extraire une valeur numĂ©rique.

C’est à quoi servent parseInt et parseFloat.

Ils “lisent” un nombre d’une chaĂźne jusqu’à ce qu’ils ne puissent plus. En cas d’erreur, le numĂ©ro rassemblĂ© est renvoyĂ©. La fonction parseInt renvoie un entier, tandis que parseFloat renvoie un nombre Ă  virgule :

alert( parseInt('100px') ); // 100
alert( parseFloat('12.5em') ); // 12.5

alert( parseInt('12.3') ); // 12, seule la partie entiÚre est renvoyée
alert( parseFloat('12.3.4') ); // 12.3, le deuxiĂšme point arrĂȘte la lecture

Il y a des situations oĂč parseInt / parseFloat retournera NaN. Cela arrive quand on ne peut lire aucun chiffre :

alert( parseInt('a123') ); // NaN, le premier symbole arrĂȘte le processus
Le second argument de parseInt(str, radix)

La fonction parseInt() a un second paramĂštre optionnel. Il spĂ©cifie la base du systĂšme numĂ©rique, ce qui permet Ă  parseInt d’analyser Ă©galement les chaĂźnes de nombres hexadĂ©cimaux, binaires, etc. :

alert( parseInt('0xff', 16) ); // 255
alert( parseInt('ff', 16) ); // 255, sans 0x cela fonctionne également

alert( parseInt('2n9c', 36) ); // 123456

Autres fonctions mathématiques

JavaScript a un objet Math intégré qui contient une petite bibliothÚque de fonctions et de constantes mathématiques.

Quelques exemples :

Math.random()

Retourne un nombre aléatoire de 0 à 1 (1 non compris).

alert( Math.random() ); // 0.1234567894322
alert( Math.random() ); // 0.5435252343232
alert( Math.random() ); // ... (tout nombre aléatoire)
Math.max(a, b, c...) et Math.min(a, b, c...)

Renvoie le plus grand et le plus petit d’un nombre arbitraire d’arguments.

alert( Math.max(3, 5, -10, 0, 1) ); // 5
alert( Math.min(1, 2) ); // 1
Math.pow(n, power)

Renvoie n élevé à la puissance power donnée.

alert( Math.pow(2, 10) ); // 2 puissance 10 = 1024

Il y a plus de fonctions et de constantes dans l’objet Math, y compris la trigonomĂ©trie, que vous pouvez trouver dans la documentation de l’objet Math.

Résumé

Pour écrire des nombres avec beaucoup de zéros :

  • Ajoutez "e" avec le nombre de zĂ©ros au nombre. Comme 123e6 est 123 avec 6 zĂ©ros soit 123000000.
  • Un nombre nĂ©gatif aprĂšs le "e" entraĂźne la division du nombre par 1 suivi par le nombre de zĂ©ros donnĂ©s. Comme 123-e6 est 123 divisĂ© par 1 suivi de 6 zĂ©ros, soit 0.000123 (123 millioniĂšmes).

Pour différents systÚmes de numération :

  • Il est possible d’écrire des nombres directement dans les systĂšmes hex (0x), octal (0o) et binaire (0b).
  • parseInt(str, base) passe la chaĂźne de caractĂšres str vers un systĂšme numĂ©rique avec une base donnĂ©e : 2 ≀ base ≀ 36.
  • num.toString(base) convertit un nombre en chaĂźne de caractĂšres dans le systĂšme numĂ©rique de la base donnĂ©e.

Pour les tests de nombres réguliers :

  • isNaN(value) convertit son argument en nombre puis vĂ©rifie s’il s’agit de NaN.
  • Number.isNaN(value) vĂ©rifie si son argument appartient au type number et, si c’est le cas, vĂ©rifie s’il s’agit de NaN.
  • isFinite(value) convertit son argument en nombre puis vĂ©rifie s’il ne s’agit pas de NaN / Infinity / -Infinity.
  • Number.isFinite(value) vĂ©rifie si son argument appartient au type number et, si c’est le cas, vĂ©rifie s’il ne s’agit pas de NaN / Infinity / -Infinity.

Pour convertir des valeurs telles que 12pt et 100px en nombres :

  • Utiliser parseInt / parseFloat pour la conversion “soft”, qui lit un nombre Ă  partir d’une chaĂźne de caractĂšres, puis renvoie la valeur qu’il pouvait lire avant de rencontrer une erreur.

Pour les fractions :

  • Arrondissez en utilisant Math.floor, Math.ceil, Math.trunc, Math.round ou num.toFixed(prĂ©cision).
  • Assurez-vous de ne pas perdre de prĂ©cision lorsque vous travaillez avec des fractions.

Plus de fonctions mathématiques :

  • Voir l’objet Math quand vous en avez besoin. La bibliothĂšque est trĂšs petite, mais peut couvrir les besoins de base.

Exercices

importance: 5

Créez un script qui invite le visiteur à entrer deux nombres, puis affiche leur somme.

[Lancer la démo]

P.S. Il y a un piĂšge avec les types.

let a = +prompt("Le premier numéro?", "");
let b = +prompt("Le second numéro?", "");

alert( a + b );

Notez le plus unaire + avant le prompt. Il convertit immédiatement la valeur en nombre.

Sinon, a et b seraient des string et leur somme serait leur concatĂ©nation, c’est Ă  dire: "1" + "2" = "12".

importance: 4

Selon la documentation, Math.round et toFixed arrondissent tous les deux au nombre le plus proche : 0..4 arrondi par défaut (à la baisse) tantdis que 5..9 arrondi par excÚs (à la hausse).

Par exemple :

alert( 1.35.toFixed(1) ); // 1.4

Dans l’exemple similaire ci-dessous, pourquoi est-ce que 6.35 est arrondi à 6.3 et non 6.4 ?

alert( 6.35.toFixed(1) ); // 6.3

Comment arrondir 6.35 de la bonne maniĂšre ?

En interne, la fraction décimale 6.35 est un nombre binaire sans fin. Comme toujours dans de tels cas, il est stocké avec une perte de précision.

Voyons cela :

alert( 6.35.toFixed(20) ); // 6.34999999999999964473

La perte de prĂ©cision peut causer Ă  la fois une augmentation et une diminution d’un nombre. Dans ce cas particulier, le nombre diminue un peu, c’est pourquoi il a Ă©tĂ© arrondi Ă  6.3.

Et quand est-il de 1.35 ?

alert( 1.35.toFixed(20) ); // 1.35000000000000008882

Ici, la perte de prĂ©cision rend le nombre un peu plus grand, c’est pourquoi il a Ă©tĂ© arrondi Ă  1.4.

Comment pouvons-nous rĂ©soudre le problĂšme avec 6.35 si nous voulons qu’il soit arrondi correctement ?

Nous devons le rapprocher d’un nombre entier avant d’arrondir :

alert( (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 les puissances de 2 sont reprĂ©sentĂ©es sans perte de prĂ©cision dans le systĂšme binaire, on peut maintenant les arrondir :

alert( Math.round(6.35 * 10) / 10); // 6.35 -> 63.5 -> 64(arrondi) -> 6.4
importance: 5

CrĂ©ez une fonction readNumber qui invite Ă  entrer 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 entrant une ligne vide ou en appuyant sur “CANCEL”. Dans ce cas, la fonction doit renvoyer null.

[Lancer la démo]

Open a sandbox with tests.

function readNumber() {
  let num;

  do {
    num = prompt("Entrez un nombre s'il vous plaĂźt", 0);
  } while ( !isFinite(num) );

  if (num === null || num === '') return null;

  return +num;
}

alert(`Read: ${readNumber()}`);

La solution est un peu plus complexe qu’elle n’y paraĂźt car nous devons gĂ©rer des lignes null / vides.

Nous rĂ©pĂ©tons donc la demande jusqu’à ce qu’il s’agisse d’un “nombre rĂ©gulier”. Les lignes null (cancel) et vide rĂ©pondent Ă©galement Ă  cette condition car, sous forme numĂ©rique, elles valent 0.

AprĂšs que nous nous sommes 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 une sandbox.

importance: 4

Cette boucle est infinie. Ça ne finit jamais. Pourquoi ?

let i = 0;
while (i != 10) {
  i += 0.2;
}

C’est parce que i ne sera jamais exactement Ă©gal Ă  10.

Exécutez-le pour voir les valeurs réelles de i :

let i = 0;
while (i < 11) {
  i += 0.2;
  if (i > 9.8 && i < 10.2) alert( i );
}

Aucun d’entre eux n’est exactement 10.

De telles choses se produisent Ă  cause des pertes de prĂ©cision lors de l’ajout des fractions comme 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 (1 non compris).

Ecrivez la fonction random(min, max) pour générer un nombre aléatoire compris entre min et max (max non compris).

Quelques exemples :

alert( random(1, 5) ); // 1.2345623452
alert( random(1, 5) ); // 3.7894332423
alert( random(1, 5) ); // 4.3435234525

Nous devons “mapper” toutes les valeurs de l’intervalle 0
1 en valeurs de min à max.

Cela peut ĂȘtre fait en deux Ă©tapes :

  1. Si nous multiplions un nombre alĂ©atoire de 0
1 par max-min, l’intervalle des valeurs possible augmente de 0..1 Ă  0..max-min.
  2. Maintenant, si nous ajoutons min, l’intervalle possible devient de min à max.

La fonction :

function random(min, max) {
  return min + Math.random() * (max - min);
}

alert( random(1, 5) );
alert( random(1, 5) );
alert( random(1, 5) );
importance: 2

Créez une fonction randomInteger(min, max) qui génÚre un nombre entier aléatoire compris entre min et max (min et max inclut).

Tout nombre compris dans l’intervalle min..max doit apparaĂźtre avec la mĂȘme probabilitĂ©.

Quelques exemples :

alert( randomInteger(1, 5) ); // 1
alert( randomInteger(1, 5) ); // 3
alert( randomInteger(1, 5) ); // 5

Vous pouvez utiliser la solution de la tùche précédente comme base.

La solution simple mais fausse

La solution la plus simple mais fausse serait de gĂ©nĂ©rer une valeur de min Ă  max et de l’arrondir :

function randomInteger(min, max) {
  let rand = min + Math.random() * (max - min);
  return Math.round(rand);
}

alert( randomInteger(1, 3) );

La fonction marche, mais elle est incorrecte. La probabilitĂ© d’obtenir les valeurs min et max est deux fois infĂ©rieure Ă  toute autre.

Si vous exĂ©cutez l’exemple ci-dessous plusieurs fois, vous verrez facilement que 2 apparaĂźt le plus souvent.

Cela se produit car Math.round() obtient des nombres alĂ©atoires Ă  partir de l’intervalle 1..3 et les arrondit comme ici :

values from 1    ... to 1.4999999999  devient 1
values from 1.5  ... to 2.4999999999  devient 2
values from 2.5  ... to 2.9999999999  devient 3

Maintenant, nous pouvons clairement voir que 1 obtient deux fois moins de valeurs que 2. Et la mĂȘme chose avec 3.

La bonne solution

Il existe de nombreuses solutions correctes Ă  la tĂąche. L’une d’elles consiste Ă  ajuster les limites d’intervalle. Pour garantir les mĂȘmes intervalles, nous pouvons gĂ©nĂ©rer des valeurs comprises entre 0.5 et 3.5, ajoutant ainsi les probabilitĂ©s requises :

function randomInteger(min, max) {
  // maintenant rand est entre (min-0.5) et (max+0.5)
  let rand = min - 0.5 + Math.random() * (max - min + 1);
  return Math.round(rand);
}

alert( randomInteger(1, 3) );

Une autre solution pourrait ĂȘtre d’utiliser Math.floor pour un nombre alĂ©atoie compris entre min et max+1.

function randomInteger(min, max) {
  // ici rand est de min Ă  (max+1)
  let rand = min + Math.random() * (max + 1 - min);
  return Math.floor(rand);
}

alert( randomInteger(1, 3) );

Maintenant, tous les intervalles sont mappés de cette façon :

values from 1  ... to 1.9999999999  devient 1
values from 2  ... to 2.9999999999  devient 2
values from 3  ... to 3.9999999999  devient 3

Tous les intervalles ont la mĂȘme longueur, rendant la distribution finale uniforme.

Carte du tutoriel

Commentaires

lire ceci avant de commenter

  • Si vous avez des amĂ©liorations Ă  suggĂ©rer, merci de soumettre une issue GitHub ou une pull request au lieu de commenter.
  • Si vous ne comprenez pas quelque chose dans l'article, merci de prĂ©ciser.
  • Pour insĂ©rer quelques bouts de code, utilisez la balise <code>, pour plusieurs lignes – enveloppez-les avec la balise <pre>, pour plus de 10 lignes - utilisez une sandbox (plnkr, jsbin, codepen
)