Italiano

Una guida completa al tipo BigInt di JavaScript, che ne illustra le caratteristiche, l'uso e le applicazioni nella gestione di calcoli con interi di grandi dimensioni. Impara a superare i limiti di JavaScript e a eseguire calcoli complessi con precisione.

JavaScript BigInt: Padroneggiare l'Aritmetica con Interi di Grandi Dimensioni

JavaScript, sebbene sia un linguaggio versatile, presenta delle limitazioni quando si tratta di interi molto grandi. Il tipo standard `Number` può rappresentare accuratamente solo interi fino a un certo limite, noto come `Number.MAX_SAFE_INTEGER`. Oltre questo limite, i calcoli diventano imprecisi, portando a risultati inaspettati. È qui che BigInt viene in soccorso. Introdotto in ECMAScript 2020, BigInt è un oggetto integrato che fornisce un modo per rappresentare e manipolare interi di dimensioni arbitrarie, superando i limiti del tipo standard `Number`.

Comprendere la Necessità di BigInt

Prima di BigInt, gli sviluppatori JavaScript dovevano affidarsi a librerie o implementazioni personalizzate per gestire calcoli con interi di grandi dimensioni. Queste soluzioni spesso comportavano un sovraccarico di prestazioni e una maggiore complessità. L'introduzione di BigInt ha fornito un modo nativo ed efficiente per lavorare con interi grandi, aprendo possibilità per applicazioni in vari settori, tra cui:

Creare Valori BigInt

Ci sono due modi principali per creare valori BigInt in JavaScript:

  1. Usando il costruttore `BigInt()`: Questo costruttore può convertire un numero, una stringa o un valore booleano in un BigInt.
  2. Usando il suffisso `n`: Aggiungere `n` a un letterale intero crea un BigInt.

Esempi:

Usando il costruttore `BigInt()`:


const bigIntFromNumber = BigInt(12345678901234567890);
const bigIntFromString = BigInt("98765432109876543210");
const bigIntFromBoolean = BigInt(true); // Risulta in 1n
const bigIntFromFalseBoolean = BigInt(false); // Risulta in 0n

console.log(bigIntFromNumber); // Output: 12345678901234567890n
console.log(bigIntFromString); // Output: 98765432109876543210n
console.log(bigIntFromBoolean); // Output: 1n
console.log(bigIntFromFalseBoolean); // Output: 0n

Usando il suffisso `n`:


const bigIntLiteral = 12345678901234567890n;
console.log(bigIntLiteral); // Output: 12345678901234567890n

Nota Importante: Non è possibile mischiare direttamente valori BigInt e Number nelle operazioni aritmetiche. È necessario convertirli esplicitamente allo stesso tipo prima di eseguire i calcoli. Tentare di mischiarli direttamente risulterà in un `TypeError`.

Operazioni Aritmetiche con BigInt

BigInt supporta la maggior parte degli operatori aritmetici standard, tra cui:

Esempi:


const a = 12345678901234567890n;
const b = 98765432109876543210n;

const sum = a + b;
const difference = a - b;
const product = a * b;
const quotient = a / 2n; // Nota: La divisione tronca verso lo zero
const remainder = a % 7n;
const power = a ** 3n; // L'esponenziazione funziona come previsto

console.log("Somma:", sum); // Output: Sum: 111111111011111111100n
console.log("Differenza:", difference); // Output: Difference: -86419753208641975320n
console.log("Prodotto:", product); // Output: Product: 1219326311370217957951669538098765432100n
console.log("Quoziente:", quotient); // Output: Quotient: 6172839450617283945n
console.log("Resto:", remainder); // Output: Remainder: 5n
console.log("Potenza:", power); // Output: Power: 187641281029182300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000n

Considerazioni Importanti:

Operatori di Confronto

È possibile utilizzare gli operatori di confronto standard (`==`, `!=`, `<`, `>`, `<=`, `>=`) per confrontare valori BigInt con altri valori BigInt o anche con valori Number. Tuttavia, fai attenzione alla potenziale coercizione di tipo.

Esempi:


const a = 10n;
const b = 20n;
const c = 10;

console.log(a == b);   // Output: false
console.log(a != b);   // Output: true
console.log(a < b);    // Output: true
console.log(a > b);    // Output: false
console.log(a <= b);   // Output: true
console.log(a >= b);   // Output: false

console.log(a == c);   // Output: true (coercizione di tipo)
console.log(a === c);  // Output: false (nessuna coercizione di tipo)

Buona Pratica: Usa l'uguaglianza stretta (`===`) e la disuguaglianza stretta (`!==`) per evitare coercizioni di tipo inaspettate quando si confrontano valori BigInt e Number.

Convertire tra BigInt e Number

Sebbene le operazioni aritmetiche dirette tra BigInt e Number non siano permesse, è possibile convertire tra i due tipi. Tuttavia, sii consapevole della potenziale perdita di precisione quando si converte un BigInt in un Number se il valore BigInt supera `Number.MAX_SAFE_INTEGER`.

Esempi:


const bigIntValue = 9007199254740991n; // Number.MAX_SAFE_INTEGER
const numberValue = Number(bigIntValue); // Conversione da BigInt a Number
console.log(numberValue); // Output: 9007199254740991

const largerBigIntValue = 9007199254740992n; // Supera Number.MAX_SAFE_INTEGER
const largerNumberValue = Number(largerBigIntValue);
console.log(largerNumberValue); // Output: 9007199254740992 (potrebbe essere impreciso)

const numberToBigInt = BigInt(12345); // Conversione da Number a BigInt
console.log(numberToBigInt); // Output: 12345n

Casi d'Uso ed Esempi

Crittografia

Gli algoritmi crittografici si basano spesso su numeri primi molto grandi per la sicurezza. BigInt fornisce un modo per rappresentare e manipolare questi numeri in modo efficiente.


// Esempio: Generazione di una coppia di chiavi semplice (non sicura)
function generateKeyPair() {
  const p = 281n; // Un numero primo
  const q = 283n; // Un altro numero primo
  const n = p * q; // Modulo
  const totient = (p - 1n) * (q - 1n); // Funzione totiente di Eulero

  // Scegli un e (esponente pubblico) tale che 1 < e < totient e mcd(e, totient) = 1
  const e = 17n;

  // Calcola d (esponente privato) tale che (d * e) % totient = 1
  let d = 0n;
  for (let i = 1n; i < totient; i++) {
    if ((i * e) % totient === 1n) {
      d = i;
      break;
    }
  }

  return {
    publicKey: { n, e },
    privateKey: { n, d },
  };
}

const keyPair = generateKeyPair();
console.log("Chiave Pubblica:", keyPair.publicKey);
console.log("Chiave Privata:", keyPair.privateKey);

Nota: Questo è un esempio semplificato solo a scopo dimostrativo. La crittografia del mondo reale utilizza numeri primi molto più grandi e algoritmi più sofisticati.

Calcoli Finanziari

Quando si ha a che fare con grandi somme di denaro, specialmente nelle transazioni internazionali, la precisione è fondamentale. BigInt può prevenire errori di arrotondamento e garantire calcoli accurati.


// Esempio: Calcolo dell'interesse composto
function calculateCompoundInterest(principal, rate, time) {
  const principalBigInt = BigInt(principal * 100); // Converti in centesimi
  const rateBigInt = BigInt(rate * 10000);       // Converti in diecimillesimi di punto percentuale
  const timeBigInt = BigInt(time);

  let amount = principalBigInt;
  for (let i = 0n; i < timeBigInt; i++) {
    amount = amount * (10000n + rateBigInt) / 10000n;
  }

  const amountInDollars = Number(amount) / 100;
  return amountInDollars;
}

const principal = 1000000; // $1.000.000
const rate = 0.05;    // Tasso di interesse del 5%
const time = 10;     // 10 anni

const finalAmount = calculateCompoundInterest(principal, rate, time);
console.log("Importo Finale:", finalAmount); // Output: Final Amount: 1628894.6267774413 (approximately)

In questo esempio, convertiamo il capitale e il tasso in valori BigInt per evitare errori di arrotondamento durante il calcolo. Il risultato viene poi riconvertito in un Number per la visualizzazione.

Lavorare con ID di Grandi Dimensioni

Nei sistemi distribuiti, generare ID unici su più server può essere una sfida. L'uso di BigInt consente di creare ID molto grandi che hanno una bassa probabilità di collisione.


// Esempio: Generazione di un ID univoco basato su timestamp e ID del server
function generateUniqueId(serverId) {
  const timestamp = BigInt(Date.now());
  const serverIdBigInt = BigInt(serverId);
  const random = BigInt(Math.floor(Math.random() * 1000)); // Aggiungi un po' di casualità

  // Combina i valori per creare un ID univoco
  const uniqueId = (timestamp << 20n) + (serverIdBigInt << 10n) + random;
  return uniqueId.toString(); // Restituisci come stringa per una gestione più semplice
}

const serverId = 123; // ID server di esempio
const id1 = generateUniqueId(serverId);
const id2 = generateUniqueId(serverId);

console.log("ID Univoco 1:", id1);
console.log("ID Univoco 2:", id2);

BigInt e JSON

I valori BigInt non sono supportati nativamente da JSON. Tentare di serializzare un oggetto JavaScript contenente un BigInt usando `JSON.stringify()` risulterà in un `TypeError`. Per gestire i valori BigInt quando si lavora con JSON, ci sono un paio di opzioni:

  1. Convertire in Stringa: Convertire il BigInt in una stringa prima della serializzazione. Questo è l'approccio più comune e diretto.
  2. Serializzazione/Deserializzazione Personalizzata: Usare una funzione di serializzazione/deserializzazione personalizzata per gestire i valori BigInt.

Esempi:

Conversione in Stringa:


const data = {
  id: 12345678901234567890n,
  name: "Example Data",
};

// Converti BigInt in stringa prima della serializzazione
data.id = data.id.toString();

const jsonData = JSON.stringify(data);
console.log(jsonData); // Output: {"id":"12345678901234567890","name":"Example Data"}

// Durante la deserializzazione, dovrai riconvertire la stringa in un BigInt
const parsedData = JSON.parse(jsonData, (key, value) => {
  if (key === "id") {
    return BigInt(value);
  }
  return value;
});

console.log(parsedData.id); // Output: 12345678901234567890n

Serializzazione/Deserializzazione Personalizzata (usando `replacer` e `reviver`):


const data = {
  id: 12345678901234567890n,
  name: "Example Data",
};

// Serializzazione personalizzata
const jsonData = JSON.stringify(data, (key, value) => {
  if (typeof value === 'bigint') {
    return value.toString();
  } else {
    return value;
  }
});

console.log(jsonData);

// Deserializzazione personalizzata
const parsedData = JSON.parse(jsonData, (key, value) => {
    if (typeof value === 'string' && /^[0-9]+$/.test(value)) { //controlla se è un numero e una stringa
      try {
        return BigInt(value);
      } catch(e) {
          return value;
      }
    }
    return value;
});

console.log(parsedData.id);

Compatibilità dei Browser

BigInt è ampiamente supportato nei browser moderni. Tuttavia, è essenziale verificare la compatibilità con browser o ambienti più vecchi. Puoi usare uno strumento come Can I use per verificare il supporto dei browser. Se hai bisogno di supportare browser più vecchi, potresti considerare l'uso di un polyfill, ma tieni presente che i polyfill possono influire sulle prestazioni.

Considerazioni sulle Prestazioni

Sebbene BigInt fornisca un modo potente per lavorare con interi di grandi dimensioni, è importante essere consapevoli delle potenziali implicazioni sulle prestazioni.

Pertanto, usa BigInt solo quando necessario e ottimizza il tuo codice per le prestazioni se stai eseguendo un gran numero di operazioni con BigInt.

Conclusione

BigInt è un'aggiunta preziosa a JavaScript, che consente agli sviluppatori di gestire l'aritmetica con interi di grandi dimensioni con precisione. Comprendendone le caratteristiche, i limiti e i casi d'uso, puoi sfruttare BigInt per creare applicazioni robuste e accurate in vari settori, tra cui crittografia, calcoli finanziari e calcolo scientifico. Ricorda di considerare la compatibilità dei browser e le implicazioni sulle prestazioni quando usi BigInt nei tuoi progetti.

Approfondimenti

Questa guida fornisce una panoramica completa di BigInt in JavaScript. Esplora le risorse collegate per informazioni più approfondite e tecniche avanzate.