2016-07-11 2 views
2

en Javascript (dans le panneau de la console DevTools Chrome et Node.js v0.12.5), que je reçois une réponse incorrecte pour le produit de ces deux grands nombres:Est-il possible de gérer le débordement d'entier sans une bibliothèque externe en JavaScript?

entrée

: 41962049 * 1827116622

sortie: 76669557221078480

En C++ et C#, j'obtiens la réponse correcte de 76669557221078478 lors de la conversion de l'expression en un int de 64 bits. Je suppose que c'est un problème de dépassement d'entier, mais je peux certainement me tromper.

Existe-t-il un moyen d'obtenir des produits arithmétiques précis pour les grands nombres en Javascript sans utiliser une bibliothèque externe comme BigInteger? C'est pour une classe en ligne qui n'autorise pas les bibliothèques supplémentaires.

Merci pour votre aide.

EDIT: Merci pour l'explication expliquant que ce n'est pas réellement un débordement d'entier, Patrick Roberts! Très utile.

EDIT 2: jfriend00, Je pense que cette question est différente de celle que vous avez liée à parce que j'essaie de comprendre s'il existe un moyen de contourner les limites de JS sans s'appuyer sur une bibliothèque externe. La réponse que vous avez fournie dans les commentaires a aidé à répondre à ma question, alors merci!

+1

No. autre que d'écrire votre propre capacité de bigint vous-même ou en utilisant une bibliothèque qui le fait déjà, il n'y a pas de capacité intégrée pour cela. Voir [Quelle est la valeur entière la plus élevée de JavaScript qu'un nombre peut atteindre sans perdre en précision?] (Http://stackoverflow.com/questions/307179/what-is-javascripts-highest-integer-value-that-a-number- can-go-to-without-losin). – jfriend00

+0

Btw, http://ideone.com/lyXfdU --- C#, http://ideone.com/F6DZ71 --- C++. – zerkms

+0

Je ne savais pas sur ce site zerkms. Assez chouette! En utilisant long (C#) ou long long (C++), j'obtiens les bons résultats. http://ideone.com/OshLAb - C# http://ideone.com/NMs0L2 - C++ – aman

Répondre

5

Ce dépassement n'est pas un nombre entier, cela est dû aux limitations de double precision floating point. L'entier le plus élevé en JavaScript est de 2^53 parce que l'epsilon dans la plage de 2^52 à 2^53 est exactement 1. Au-dessus, la précision de l'entier décompose, mais le résultat de cette multiplication n'est pas dû à un entier débordement.

est inférieure à la citation pertinente de wikipedia sur la norme IEEE 754:

Entre 2 = 4,503,599,627,370,496 et 2 = 9,007,199,254,740,992 les nombres représentables sont exactement les entiers. Pour la plage suivante, de 2 -2 , tout est multiplié par 2, de sorte que les nombres représentables sont les même ceux, etc. Par contre, pour la gamme précédente de 2 -2 , l'espacement est de 0,5, etc.

l'espacement en tant que fraction du nombre dans la plage de 2 n-2 n + 1 est 2 n-. L'erreur d'arrondi relative maximale lors de l'arrondi d'un nombre au chiffre représentable le plus proche (la machine epsilon) est donc 2 -53.

Pour répondre à votre question cependant, c'est très possible.Voici une petite bibliothèque que je viens d'écrire dans les dernières heures de couple pour plus d'entier non signé et la multiplication qui est capable d'afficher les valeurs en base 10:

class BigUint { 
 
    static get WORD() { 
 
    return 100000000000000; 
 
    } 
 

 
    static get HALF() { 
 
    return 10000000; 
 
    } 
 

 
    static map(word, index) { 
 
    if (index === 0) { 
 
     return word.toString(); 
 
    } 
 

 
    return `000000${word}`.slice(-7); 
 
    } 
 

 
    static from(array) { 
 
    return Object.create(BigUint.prototype, { 
 
     words: { 
 
     configurable: true, 
 
     enumerable: true, 
 
     value: new Uint32Array(array), 
 
     writable: true 
 
     } 
 
    }); 
 
    } 
 

 
    constructor(number) { 
 
    if (/\D/.test(`${number}`)) { 
 
    \t throw new TypeError('seed must be non-negative integer as number or string'); 
 
    } 
 
    
 
    this.words = new Uint32Array(`${number}`.split(/(?=(?:.{7})+$)/).map(s => +s)); 
 
    } 
 

 
    [Symbol.toPrimitive](hint) { 
 
    let string = this.toString(); 
 

 
    switch (hint) { 
 
    case 'number': 
 
     return +string; 
 
    default: 
 
     return string; 
 
    } 
 
    } 
 

 
    get length() { 
 
    return this.words.length; 
 
    } 
 

 
    toString() { 
 
    return Array.from(this.words).map(BigUint.map).join(''); 
 
    } 
 

 
    add(that) { 
 
    const thisLength = this.length; 
 
    const thatLength = that.length; 
 
    const maxLength = Math.max(thisLength, thatLength); 
 
    const minLength = Math.min(thisLength, thatLength); 
 
    const max = maxLength === thisLength ? this : that; 
 

 
    const words = []; 
 

 
    let augend, addend, sum, keep, carry = 0, index; 
 

 
    for (index = 1; index <= minLength; index++) { 
 
     augend = this.words[thisLength - index]; 
 
     addend = that.words[thatLength - index]; 
 

 
     sum = augend + addend + carry; 
 

 
     keep = sum % BigUint.HALF; 
 
     carry = (sum - keep)/BigUint.HALF; 
 

 
     words.unshift(keep); 
 
    } 
 

 
    for (; index <= maxLength; index++) { 
 
     sum = max.words[maxLength - index] + carry; 
 

 
     keep = sum % BigUint.HALF; 
 
     carry = (sum - keep)/BigUint.HALF; 
 

 
     words.unshift(keep); 
 
    } 
 

 
    if (carry > 0) { 
 
     words.unshift(carry); 
 
    } 
 

 
    return BigUint.from(words); 
 
    } 
 

 
    multiply(that) { 
 
    const thisLength = this.length; 
 
    const thatLength = that.length; 
 
    const minLength = Math.min(thisLength, thatLength); 
 
    const maxLength = Math.max(thisLength, thatLength); 
 
    const min = minLength === thisLength ? this.words : that.words; 
 
    const max = maxLength === thatLength ? that.words : this.words; 
 

 
    const partials = []; 
 

 
    let product, words, keep, carry = 0, sum, addend; 
 

 
    for (let outerIndex = minLength - 1; outerIndex >= 0; outerIndex--) { 
 
     words = []; 
 

 
     partials.push(words); 
 

 
     for (let j = minLength - 1; j > outerIndex; j--) { 
 
     words.unshift(0); 
 
     } 
 

 
     for (let innerIndex = maxLength - 1; innerIndex >= 0; innerIndex--) { 
 
     product = min[outerIndex] * max[innerIndex] + carry; 
 

 
     keep = product % BigUint.HALF; 
 
     carry = (product - keep)/BigUint.HALF; 
 

 
     words.unshift(keep); 
 
     } 
 

 
     if (carry > 0) { 
 
     words.unshift(carry); 
 

 
     carry = 0; 
 
     } 
 
    } 
 

 
    sum = BigUint.from(partials.pop()); 
 

 
    while (partials.length > 0) { 
 
     sum = sum.add(BigUint.from(partials.pop())); 
 
    } 
 

 
    return sum; 
 
    } 
 
} 
 

 
const a = document.getElementById('a'); 
 
const b = document.getElementById('b'); 
 
const c = document.getElementById('c'); 
 
const op = document.querySelector('select'); 
 

 
function calculate() { 
 
\t c.textContent = new BigUint(a.value)[op.value](new BigUint(b.value)); 
 
} 
 

 
document.addEventListener('input', calculate); 
 

 
calculate();
<input id="a" type="number" min="0" step="1" value="41962049"> 
 
<select> 
 
    <option value="add">+</option> 
 
    <option value="multiply" selected>&times;</option> 
 
</select> 
 
<input id="b" type="number" min="0" step="1" value="1827116622"> 
 
= 
 
<span id="c"></span>

+0

Patrick, c'est formidable, merci. Je vais le modifier pour travailler en CLI pour me soumettre à la classe de ma classe. le modèle pour analyser 'stdin' et' stdout' que j'utilise est: 'process.stdin.setEncoding ('utf8'); données var = "" fonction leastCM (a, b) {// Code } process.stdin.on ('fin', function() { entrée var = data.split (» «); var a = parseInt (entrée [0], 10); var b = parseInt (entrée [1], 10); console.log (moinsCM (a, b)); processus.exit(); }) process.stdin.on ('lisible', function() { new_data = process.stdin.read()! if (new_data == null) { data = données + new_data } }); ' –

+0

@ AnyaGlowa-Kollisch Avec ceci, vous pouvez utiliser 'var a = new BigUint (entrée [0])' et 'var b = new BigUint (entrée [1])', et multiplication par l'utilisation détermine le multiple le moins commun. Je pourrais essayer de mettre en place une division de précision arbitraire si cela vous facilitait la vie, mais vous devrez attendre jusqu'à ce soir. –

+0

ce serait très apprécié. Tu es le meilleur, Patrick! –