Tipi Numerici in Javascript
- In Javascript, i numeri sono rappresentati come valori di tipo
Number
, che possono essere interi o a virgola mobile. - I numeri in Javascript seguono lo standard IEEE 754 per i numeri a virgola mobile a 64 bit.
- I numeri interi in Javascript possono essere rappresentati come
Number
o comeBigInt
per numeri di precisione arbitraria. - I valori
BigInt
sono utili per rappresentare numeri interi molto grandi e sono scritti con unan
finale. - Le operazioni aritmetiche con
BigInt
funzionano come con i normali numeri, ma non è possibile mescolareBigInt
eNumber
in operazioni aritmetiche.
Numeri in JavaScript
Il tipo numerico primario di JavaScript, Number
, viene usato per rappresentare numeri interi e per approssimare numeri reali. JavaScript rappresenta i numeri usando il formato floating-point a 64 bit definito dallo standard IEEE 754, il che significa che può rappresentare numeri grandi quanto
Il formato numerico di JavaScript permette di rappresentare esattamente tutti gli interi tra BigInt
in JavaScript.
Quando un numero appare direttamente in un programma JavaScript, viene chiamato letterale numerico. JavaScript supporta letterali numerici in diversi formati, come descritto nelle sezioni seguenti. Si noti che qualsiasi letterale numerico può essere preceduto da un segno meno (-) per rendere il numero negativo.
Letterali Interi
In un programma JavaScript, un intero in base 10 è scritto come una sequenza di cifre. Ad esempio:
0
3
10000000
Oltre ai letterali interi in base 10, JavaScript riconosce i valori esadecimali (base 16). Un letterale esadecimale inizia con 0x
o 0X
, seguito da una stringa di cifre esadecimali. Una cifra esadecimale è una delle cifre da 0 a 9 o le lettere da a (o A) a f (o F), che rappresentano valori da 10 a 15. Ecco esempi di letterali interi esadecimali:
0xff // => 255: (15*16 + 15)
0xBADCAFE // => 195939070
In ES6 e versioni successive, è anche possibile esprimere interi in binario (base 2) o ottale (base 8) utilizzando i prefissi 0b
e 0o
(o 0B
e 0O
) invece di 0x
:
0b10101 // => 21: (1*16 + 0*8 + 1*4 + 0*2 + 1*1)
0o377 // => 255: (3*64 + 7*8 + 7*1)
Letterali in Virgola Mobile
I letterali a virgola mobile possono avere un punto decimale; utilizzano la sintassi tradizionale per i numeri reali. Un valore reale è rappresentato come la parte intera del numero, seguita da un punto decimale e la parte frazionaria del numero.
I letterali a virgola mobile possono anche essere rappresentati utilizzando la notazione esponenziale: un numero reale seguito dalla lettera e
(o E
), seguita da un segno più o meno opzionale, seguito da un esponente intero. I letterali a virgola mobile possono avere un punto decimale; utilizzano la sintassi tradizionale per i numeri reali. Un valore reale è rappresentato come la parte intera del numero, seguita da un punto decimale e la parte frazionaria del numero.
I letterali a virgola mobile possono anche essere rappresentati utilizzando la notazione esponenziale: un numero reale seguito dalla lettera e
(o E
), seguita da un segno più o meno opzionale, seguito da un esponente intero. Questa notazione rappresenta il numero reale moltiplicato per 10 elevato alla potenza dell'esponente.
Più sinteticamente, la sintassi è:
[cifre][.cifre][(E|e)[(+|-)]cifre]
Ad esempio:
3.14
2345.6789
.333333333333333333
6.02e23 // 6.02 × 10²³
1.4738223E-32 // 1.4738223 × 10⁻³²
Separatori nei Letterali Numerici
È possibile utilizzare underscore all'interno dei letterali numerici per suddividere i letterali lunghi in parti più facili da leggere:
// Underscore come separatore delle migliaia.
let miliardo = 1_000_000_000;
// Come separatore di byte.
let byte = 0x89_AB_CD_EF;
// Come separatore di nibble.
let bit = 0b0001_1101_0111;
// Funziona anche nella parte frazionaria.
let frazione = 0.123_456_789;
Aritmetica in JavaScript
I programmi JavaScript lavorano con i numeri utilizzando gli operatori aritmetici che il linguaggio fornisce. Questi includono +
per l'addizione, -
per la sottrazione, *
per la moltiplicazione, /
per la divisione, e %
per il modulo (resto dopo la divisione). ES2016 aggiunge **
per l'elevamento a potenza. Si studieranno in dettaglio questi operatori quando si affronteranno le espressioni.
Oltre a questi operatori aritmetici di base, JavaScript supporta operazioni matematiche più complesse attraverso un insieme di funzioni e costanti definite come proprietà dell'oggetto Math
:
Math.pow(2,53) // => 9007199254740992: 2 alla potenza 53
Math.round(.6) // => 1.0: arrotonda al numero intero più vicino
Math.ceil(.6) // => 1.0: arrotonda per eccesso a un numero intero
Math.floor(.6) // => 0.0: arrotonda per difetto a un numero intero
Math.abs(-5) // => 5: valore assoluto
Math.max(x,y,z) // Restituisce l'argomento più grande
Math.min(x,y,z) // Restituisce l'argomento più piccolo
Math.random() // Numero pseudo-casuale x dove 0 <= x < 1.0
Math.PI // π: circonferenza di un cerchio / diametro
Math.E // e: La base del logaritmo naturale
Math.sqrt(3) // => 3**0.5: la radice quadrata di 3
Math.pow(3, 1/3) // => 3**(1/3): la radice cubica di 3
Math.sin(0) // Trigonometria: anche Math.cos, Math.atan, ecc.
Math.log(10) // Logaritmo naturale di 10
Math.log(100)/Math.LN10 // Logaritmo in base 10 di 100
Math.log(512)/Math.LN2 // Logaritmo in base 2 di 512
Math.exp(3) // Math.E al cubo
ES6 definisce più funzioni sull'oggetto Math
:
Math.cbrt(27) // => 3: radice cubica
Math.hypot(3, 4) // => 5: radice quadrata della somma dei quadrati di tutti gli argomenti
Math.log10(100) // => 2: Logaritmo in base 10
Math.log2(1024) // => 10: Logaritmo in base 2
Math.log1p(x) // Logaritmo naturale di (1+x); accurato per x molto piccoli
Math.expm1(x) // Math.exp(x)-1; l'inverso di Math.log1p()
Math.sign(x) // -1, 0, o 1 per argomenti <, ==, o > 0
Math.imul(2,3) // => 6: moltiplicazione ottimizzata di interi a 32 bit
Math.clz32(0xf) // => 28: numero di bit zero iniziali in un intero a 32 bit
Math.trunc(3.9) // => 3: converte in un intero troncando la parte frazionaria
Math.fround(x) // Arrotonda al numero float a 32 bit più vicino
Math.sinh(x) // Seno iperbolico. Anche Math.cosh(), Math.tanh()
Math.asinh(x) // Arcoseno iperbolico. Anche Math.acosh(), Math.atanh()
L'aritmetica in JavaScript non genera errori in casi di overflow, underflow, o divisione per zero. Quando il risultato di un'operazione numerica è più grande del numero rappresentabile più grande (overflow), il risultato è un valore infinito speciale, Infinity
. Similmente, quando il valore assoluto di un valore negativo diventa più grande del valore assoluto del numero negativo rappresentabile più grande, il risultato è infinito negativo, -Infinity
. I valori infiniti si comportano come ci si aspetterebbe: aggiungerli, sottrarli, moltiplicarli, o dividerli per qualsiasi cosa risulta in un valore infinito (possibilmente con il segno invertito).
L'underflow si verifica quando il risultato di un'operazione numerica è più vicino a zero rispetto al numero rappresentabile più piccolo. In questo caso, JavaScript restituisce 0. Se l'underflow si verifica da un numero negativo, JavaScript restituisce un valore speciale noto come "zero negativo." Questo valore è quasi completamente indistinguibile dallo zero normale e i programmatori JavaScript raramente hanno bisogno di rilevarlo.
La divisione per zero non è un errore in JavaScript: semplicemente restituisce infinito o infinito negativo. C'è un'eccezione, tuttavia: zero diviso per zero non ha un valore ben definito, e il risultato di questa operazione è il valore speciale not-a-number, NaN
. NaN
si verifica anche se si tenta di dividere infinito per infinito, prendere la radice quadrata di un numero negativo, o usare operatori aritmetici con operandi non numerici che non possono essere convertiti in numeri.
JavaScript predefinisce le costanti globali Infinity
e NaN
per contenere l'infinito positivo e il valore not-a-number, e questi valori sono anche disponibili come proprietà dell'oggetto Number
:
Infinity // Un numero positivo troppo grande da rappresentare
Number.POSITIVE_INFINITY // Stesso valore
1/0 // => Infinity
Number.MAX_VALUE * 2 // => Infinity; overflow
-Infinity // Un numero negativo troppo grande da rappresentare
Number.NEGATIVE_INFINITY // Lo stesso valore
-1/0 // => -Infinity
-Number.MAX_VALUE * 2 // => -Infinity
NaN // Il valore not-a-number
Number.NaN // Lo stesso valore, scritto in un altro modo
0/0 // => NaN
Infinity/Infinity // => NaN
Number.MIN_VALUE/2 // => 0: underflow
-Number.MIN_VALUE/2 // => -0: zero negativo
-1/Infinity // -> -0: anche zero negativo
-0
// Le seguenti proprietà Number sono definite in ES6
Number.parseInt() // Stesso della funzione globale parseInt()
Number.parseFloat() // Stesso della funzione globale parseFloat()
Number.isNaN(x) // x è il valore NaN?
Number.isFinite(x) // x è un numero e finito?
Number.isInteger(x) // x è un intero?
Number.isSafeInteger(x) // x è un intero -(2**53) < x < 2**53?
Number.MIN_SAFE_INTEGER // => -(2**53 - 1)
Number.MAX_SAFE_INTEGER // => 2**53 - 1
Number.EPSILON // => 2**-52: differenza più piccola tra numeri
Il valore not-a-number ha una caratteristica insolita in JavaScript: non si confronta uguale a nessun altro valore, incluso se stesso. Questo significa che non si può scrivere x === NaN
per determinare se il valore di una variabile x
è NaN
. Invece, si deve scrivere x != x
o Number.isNaN(x)
. Quelle espressioni saranno true se, e solo se, x
ha lo stesso valore della costante globale NaN
.
La funzione globale isNaN()
è simile a Number.isNaN()
. Restituisce true
se il suo argomento è NaN
, o se quell'argomento è un valore non numerico che non può essere convertito in un numero. La funzione correlata Number.isFinite()
restituisce true
se il suo argomento è un numero diverso da NaN
, Infinity
, o -Infinity
. La funzione globale isFinite()
restituisce true
se il suo argomento è, o può essere convertito in, un numero finito.
Il valore zero negativo è anche piuttosto insolito. Si confronta uguale (anche usando il test di uguaglianza rigorosa di JavaScript) allo zero positivo, il che significa che i due valori sono quasi indistinguibili, eccetto quando usati come divisore:
let zero = 0; // Zero normale
let negz = -0; // Zero negativo
zero === negz // => true: zero e zero negativo sono uguali
1/zero === 1/negz // => false: Infinity e -Infinity non sono uguali
Floating Point e Errori di Arrotondamento
Esistono infiniti numeri reali, ma solo un numero finito di essi (18.437.736.874.454.810.627, per essere precisi) può essere rappresentato esattamente dal formato floating-point di JavaScript. Ciò significa che quando si lavora con numeri reali in JavaScript, la rappresentazione del numero sarà spesso un'approssimazione del numero effettivo.
La rappresentazione floating-point IEEE-754 utilizzata da JavaScript (e praticamente da ogni altro linguaggio di programmazione moderno) è una rappresentazione binaria, che può rappresentare esattamente frazioni come 1/2
, 1/8
e 1/1024
. Sfortunatamente, le frazioni che utilizziamo più comunemente (specialmente quando eseguiamo calcoli finanziari) sono frazioni decimali: 1/10
, 1/100
e così via. Le rappresentazioni floating-point binarie non possono rappresentare esattamente numeri semplici come 0.1
.
I numeri JavaScript hanno molta precisione e possono approssimare 0.1
molto da vicino. Ma il fatto che questo numero non possa essere rappresentato esattamente può portare a problemi. Consideriamo questo codice:
let x = .3 - .2; // trenta centesimi meno 20 centesimi
let y = .2 - .1; // venti centesimi meno 10 centesimi
x === y // => false: i due valori non sono uguali!
x === .1 // => false: .3-.2 non è uguale a .1
y === .1 // => true: .2-.1 è uguale a .1
A causa dell'errore di arrotondamento, la differenza tra le approssimazioni di .3 e .2 non è esattamente la stessa della differenza tra le approssimazioni di .2 e .1. È importante capire che questo problema non è specifico di JavaScript: colpisce qualsiasi linguaggio di programmazione che utilizza numeri floating-point binari. Inoltre, si noti che i valori x
e y
nel codice mostrato qui sono molto vicini l'uno all'altro e al valore corretto. I valori calcolati sono adeguati per quasi ogni scopo; il problema sorge solo quando si tenta di confrontare i valori per l'uguaglianza.
Se queste approssimazioni floating-point risultano problematiche per i programmi, si consideri l'utilizzo di interi scalati. Per esempio, si potrebbero manipolare i valori monetari come centesimi interi piuttosto che dollari frazionari.
Interi a Precisione Arbitraria con BigInt
Una delle caratteristiche più recenti di JavaScript, definita in ES2020, è un nuovo tipo numerico noto come BigInt
. Come suggerisce il nome, BigInt
è un tipo numerico i cui valori sono interi. Il tipo è stato aggiunto a JavaScript principalmente per consentire la rappresentazione di interi a 64-bit, che sono necessari per la compatibilità con molti altri linguaggi di programmazione e API. Ma i valori BigInt
possono avere migliaia o anche milioni di cifre, nel caso si abbia bisogno di lavorare con numeri così grandi. Si noti, tuttavia, che le implementazioni BigInt
non sono adatte per la crittografia perché non tentano di prevenire gli attacchi temporali.
I letterali BigInt
sono scritti come una stringa di cifre seguita da una lettera minuscola n
. Per impostazione predefinita, sono in base 10, ma si possono usare i prefissi 0b
, 0o
e 0x
per BigInt
binari, ottali ed esadecimali:
1234n // Un letterale BigInt non così grande
0b111111n // Un BigInt binario
0o7777n // Un BigInt ottale
0x8000000000000000n // => 2n**63n: Un intero a 64-bit
È possibile usare BigInt()
come una funzione per convertire numeri JavaScript regolari o stringhe in valori BigInt
:
BigInt(Number.MAX_SAFE_INTEGER) // => 9007199254740991n
let stringa = "1" + "0".repeat(100); // 1 seguito da 100 zeri.
BigInt(stringa) // => 10n**100n: un googol
L'aritmetica con i valori BigInt
funziona come l'aritmetica con i normali numeri JavaScript, eccetto che la divisione elimina qualsiasi resto e arrotonda verso il basso (verso zero):
1000n + 2000n // => 3000n
3000n - 2000n // => 1000n
2000n * 3000n // => 6000000n
3000n / 997n // => 3n: il quoziente è 3
3000n % 997n // => 9n: e il resto è 9
(2n ** 131071n) - 1n // Un primo di Mersenne con 39457 cifre decimali
Anche se gli operatori standard +
, -
, *
, /
, %
e **
funzionano con BigInt
, è importante capire che non si possono mescolare operandi di tipo BigInt
con operandi numerici regolari. Questo può sembrare confuso all'inizio, ma c'è una buona ragione per questo. Se un tipo numerico fosse più generale dell'altro, sarebbe facile definire l'aritmetica su operandi misti per restituire semplicemente un valore del tipo più generale. Ma nessuno dei due tipi è più generale dell'altro: BigInt
può rappresentare valori straordinariamente grandi, rendendolo più generale dei numeri regolari. Ma BigInt
può rappresentare solo interi, rendendo il tipo numero JavaScript regolare più generale. Non c'è modo di aggirare questo problema, quindi JavaScript lo evita semplicemente non consentendo operandi misti agli operatori aritmetici.
Gli operatori di confronto, al contrario, funzionano con tipi numerici misti:
1 < 2n // => true
2 > 1n // => true
0 == 0n // => true
0 === 0n // => false: il === controlla anche l'uguaglianza del tipo
Gli operatori bitwise (che si studieranno in seguito) generalmente funzionano con operandi BigInt
. Nessuna delle funzioni dell'oggetto Math
accetta operandi BigInt
, tuttavia.
Date e Orari
JavaScript definisce una semplice classe Date
per rappresentare e manipolare i numeri che rappresentano date e orari. Le Date di JavaScript sono oggetti, ma hanno anche una rappresentazione numerica nota come timestamp che specifica il numero di millisecondi trascorsi dal 1° gennaio 1970:
let timestampCorrente = Date.now(); // L'orario corrente come timestamp (un numero).
let adesso = new Date(); // L'orario corrente come oggetto Date.
let ms = adesso.getTime(); // Converti in un timestamp in millisecondi.
let iso = adesso.toISOString(); // Converti in una stringa in formato standard.
La classe Date
e i suoi metodi sono trattati in dettaglio nelle lezioni future.
La classe Date
e i suoi metodi sono trattati in dettaglio nelle lezioni future.