37 votes

Précision du nombre de Javascript sur le navigateur

Dans JavaScript, des numéros sont définis comme 64 bits double précision. J'ai une utilisation spécifique dans l'esprit de répartie application web, qui ne pouvait fonctionner que si je peux compter sur des résultats cohérents sur tous les navigateurs.

En dépit de la spécification à l'aide de la norme IEEE, je suis naturellement un soupçon qu'il peut y avoir des petites différences dans les implémentations des mathématiques de la bibliothèque ou encore le matériel sous-jacent, ce qui pourrait provoquer composé des erreurs.

Est-il une source de données de compatibilité, ou d'un test fiable de la suite pour vérifier les calculs de double précision dans le navigateur? En particulier, j'ai aussi besoin de considérer les navigateurs mobiles (généralement de BRAS en fonction).

Clarification -

C'est une question à propos de la compatibilité du navigateur. J'essaie de comprendre si tous les navigateurs peut être invoqué pour traiter des chiffres fiables, cohérentes et reproductibles de façon telle que définie par l'IEEE en virgule flottante. Dans la plupart des langues, c'est une hypothèse sûre, mais il est intéressant de voir qu'il y a un peu d'incertitude à ce sujet dans le navigateur.

Il y a eu quelques bons conseils sur la façon d'éviter à virgule flottante problèmes en raison du manque de précision et les erreurs d'arrondi. Dans la plupart des cas, si vous avez besoin de précision, vous devez suivre ces conseils!

Pour cette question, je ne cherche pas à éviter le problème, mais de le comprendre. Les nombres à virgule flottante sont intrinsèquement imprécis de par leur conception, mais aussi longtemps que certaines précautions sont prises avec la façon dont les versions sont des faits que l'inexactitude peut être complètement prévisible et uniforme. La norme IEEE-754 décrit ce à un niveau de détail que seul un organisme de normalisation pourrait.

J'ai décidé de vous offrir un petit bounty si quelqu'un peut citer,

  • Véritable compatibilité des données relatives à la mise en œuvre de la norme IEEE numéros dans les navigateurs grand public.
  • Une suite de tests destinés à vérifier la mise en œuvre dans les navigateurs, y compris la vérification de la bonne utilisation interne de 64 bits à virgule flottante nombre (53 bits de mantisse).

Dans cette question, je ne suis pas à la recherche pour les options alternatives, des solutions de contournement ou des moyens d'éviter le problème. Merci pour les suggestions.

19voto

ckozl Points 4838

C'est juste pour le plaisir, comme vous l'avez déjà dit et j'ai créé une nouvelle réponse, car celui-ci est dans une autre veine. Mais je me sens encore comme il y a un peu de hasard passants qui sont ignorant la futilité du problème. Donc, nous allons commencer par aborder vos points:

Tout d'abord:

Véritable compatibilité des données relatives à la mise en œuvre de la norme IEEE les nombres dans les navigateurs grand public.

n'existe pas, et d'ailleurs n'a même pas de sens, IEEE est juste un corps de normes...? Je ne suis pas sûr si cette vague sur le but ou l'accident, je vais supposer que vous étiez en train de dire la norme IEEE 754, mais il y a là le hic... il n'y a techniquement 2 versions de cette norme IEEE 754-2008 ET IEEE 754-1985. Fondamentalement, l'ancien est plus récente et les adresses de ce dernier oublis. Toute personne saine d'esprit pourrait supposer que tout maintenu JavaScript mise en œuvre mise à jour la plus récente et la plus standard, mais n'importe quelle personne saine d'esprit devrait connaître JavaScript mieux que ça, et même si le JavaScript n'était pas fou, il n'y a pas de spécification de dire que la mise en œuvre doit être/rester à jour (vérifier l'ECMA spec-vous si vous ne me croyez pas, ils n'ont même pas parler de "versions"). Pour compliquer les choses davantage la Norme IEEE 754-2008 de l'Arithmétique à virgule Flottante prend en charge deux les formats d'encodage: décimal format de codage, et le codage binaire format. Qui que l'on attend sont compatibles les uns avec les autres dans le sens où vous pouvez aller et venir sans perte de données, mais c'est en supposant que nous avons accès à la représentation binaire du nombre, ce qui n'est pas (sans y attacher un débogueur et à la recherche à la boutique old school)

Cependant, de ce que je peux dire, il semble qu'il est de pratique générale de "retour" JavaScript Number avec l'ancienne, double ce qui signifie que nous sommes à la merci du compilateur utilisé pour construire le navigateur. Mais, même dans ce domaine, on ne peut pas et ne doit pas être en supposant l'égalité, même si tous les compilateurs ont été sur la même version de la norme (ils ne le sont pas) et même si tous les compilateurs de mise en œuvre de la norme dans son intégralité (ils ne le font pas). Voici un extrait de ce document, que j'ai jugé intéressant, utile et pertinente à cette boîte de dialogue lire...

De nombreux programmeurs aiment à croire qu'ils peuvent comprendre le comportement d'un programme et de prouver qu'il peut fonctionner correctement sans référence pour le compilateur compile ou l'ordinateur qui exécute. Dans de nombreux des moyens, de soutenir cette croyance est un objectif valable pour les concepteurs de les systèmes informatiques et les langages de programmation. Malheureusement, quand il vient de l'arithmétique à virgule flottante, le but est pratiquement impossible à réaliser. Les auteurs de l'IEEE standards savait qu', et ils ne pas tenter de l'atteindre. En conséquence, en dépit de près universelle la conformité de (plus de) la norme IEEE 754 la norme tout au long de l'ordinateur l'industrie, les programmeurs de logiciel portable doit continuer à faire face à imprévisible arithmétique à virgule flottante.

Mais j'ai aussi trouvé cette référence mise en œuvre fait entièrement en JavaScript (note: je n'ai même pas vérifié la validité de la mise en œuvre).

Tout cela étant dit, passons à votre deuxième demande:

Une suite de tests destinés à vérifier la mise en œuvre au sein de l' les navigateurs, y compris la vérification de la bonne utilisation interne de 64 bits nombre à virgule flottante (53 bits de mantisse).

JavaScript est une interprétation d'une plate-forme, vous devriez voir maintenant qu'il n'y a aucun moyen de tester l'ensemble de script + compilateur (VM/moteur) + compilateur le compilateur + machine dans un absolu et de manière fiable à partir du point de JavaScript. Donc, si vous voulez construire une suite de test qui agit comme un navigateur hôte et fait des "pics" dans l'intimité de la mémoire du processus pour assurer une représentation valide, ce qui serait issue la plus probable de toute façon puisque le nombre le plus probable "soutenu" par un double et qui est conforme comme il le fait dans le C ou le C++ que le navigateur a été construit en. Il n'existe pas de manière absolue à ce faire à partir de JavaScript car tous, nous avons accès est "l'objet" et même lorsque nous considérons l' Number dans une console, nous sommes à la recherche à un .toString version. Pour cette question, je voudrais émettre l'hypothèse que c'est la seule forme qui compte, car il sera déterminé à partir du binaire et ne deviennent un point de défaillance si la déclaration: n1 === n2 && n1.toString() !== n2.toString() vous pouviez trouver une n1, n2 qui est pertinent...

Cela dit, on peut tester la version de chaîne et, en réalité, il est tout aussi bon que les tests binaires tant que nous continuons à quelques bizarreries dans l'esprit. Surtout depuis rien en dehors du moteur JavaScript/VM jamais touche la version binaire. Toutefois, cela vous met à la merci d'une étrange spécifiques, peut-être très capricieux et prête à être changé de point de défaillance. Juste pour référence, voici un extrait de webkit est JavaScriptCore du Nombre de Prototypes (NumberPrototype.cpp) l'affichage de la complexité de la conversion:

    // The largest finite floating point number is 1.mantissa * 2^(0x7fe-0x3ff).
    // Since 2^N in binary is a one bit followed by N zero bits. 1 * 2^3ff requires
    // at most 1024 characters to the left of a decimal point, in base 2 (1025 if
    // we include a minus sign). For the fraction, a value with an exponent of 0
    // has up to 52 bits to the right of the decimal point. Each decrement of the
    // exponent down to a minimum of -0x3fe adds an additional digit to the length
    // of the fraction. As such the maximum fraction size is 1075 (1076 including
    // a point). We pick a buffer size such that can simply place the point in the
    // center of the buffer, and are guaranteed to have enough space in each direction
    // fo any number of digits an IEEE number may require to represent.
    typedef char RadixBuffer[2180];

    // Mapping from integers 0..35 to digit identifying this value, for radix 2..36.
    static const char* const radixDigits = "0123456789abcdefghijklmnopqrstuvwxyz";

    static char* toStringWithRadix(RadixBuffer& buffer, double number, unsigned radix)
    {
        ASSERT(isfinite(number));
        ASSERT(radix >= 2 && radix <= 36);

        // Position the decimal point at the center of the string, set
        // the startOfResultString pointer to point at the decimal point.
        char* decimalPoint = buffer + sizeof(buffer) / 2;
        char* startOfResultString = decimalPoint;

        // Extract the sign.
        bool isNegative = number < 0;
        if (signbit(number))
            number = -number;
        double integerPart = floor(number);

        // We use this to test for odd values in odd radix bases.
        // Where the base is even, (e.g. 10), to determine whether a value is even we need only
        // consider the least significant digit. For example, 124 in base 10 is even, because '4'
        // is even. if the radix is odd, then the radix raised to an integer power is also odd.
        // E.g. in base 5, 124 represents (1 * 125 + 2 * 25 + 4 * 5). Since each digit in the value
        // is multiplied by an odd number, the result is even if the sum of all digits is even.
        //
        // For the integer portion of the result, we only need test whether the integer value is
        // even or odd. For each digit of the fraction added, we should invert our idea of whether
        // the number is odd if the new digit is odd.
        //
        // Also initialize digit to this value; for even radix values we only need track whether
        // the last individual digit was odd.
        bool integerPartIsOdd = integerPart <= static_cast<double>(0x1FFFFFFFFFFFFFull) && static_cast<int64_t>(integerPart) & 1;
        ASSERT(integerPartIsOdd == static_cast<bool>(fmod(integerPart, 2)));
        bool isOddInOddRadix = integerPartIsOdd;
        uint32_t digit = integerPartIsOdd;

        // Check if the value has a fractional part to convert.
        double fractionPart = number - integerPart;
        if (fractionPart) {
            // Write the decimal point now.
            *decimalPoint = '.';

            // Higher precision representation of the fractional part.
            Uint16WithFraction fraction(fractionPart);

            bool needsRoundingUp = false;
            char* endOfResultString = decimalPoint + 1;

            // Calculate the delta from the current number to the next & previous possible IEEE numbers.
            double nextNumber = nextafter(number, std::numeric_limits<double>::infinity());
            double lastNumber = nextafter(number, -std::numeric_limits<double>::infinity());
            ASSERT(isfinite(nextNumber) && !signbit(nextNumber));
            ASSERT(isfinite(lastNumber) && !signbit(lastNumber));
            double deltaNextDouble = nextNumber - number;
            double deltaLastDouble = number - lastNumber;
            ASSERT(isfinite(deltaNextDouble) && !signbit(deltaNextDouble));
            ASSERT(isfinite(deltaLastDouble) && !signbit(deltaLastDouble));

            // We track the delta from the current value to the next, to track how many digits of the
            // fraction we need to write. For example, if the value we are converting is precisely
            // 1.2345, so far we have written the digits "1.23" to a string leaving a remainder of
            // 0.45, and we want to determine whether we can round off, or whether we need to keep
            // appending digits ('4'). We can stop adding digits provided that then next possible
            // lower IEEE value is further from 1.23 than the remainder we'd be rounding off (0.45),
            // which is to say, less than 1.2255. Put another way, the delta between the prior
            // possible value and this number must be more than 2x the remainder we'd be rounding off
            // (or more simply half the delta between numbers must be greater than the remainder).
            //
            // Similarly we need track the delta to the next possible value, to dertermine whether
            // to round up. In almost all cases (other than at exponent boundaries) the deltas to
            // prior and subsequent values are identical, so we don't need track then separately.
            if (deltaNextDouble != deltaLastDouble) {
                // Since the deltas are different track them separately. Pre-multiply by 0.5.
                Uint16WithFraction halfDeltaNext(deltaNextDouble, 1);
                Uint16WithFraction halfDeltaLast(deltaLastDouble, 1);

                while (true) {
                    // examine the remainder to determine whether we should be considering rounding
                    // up or down. If remainder is precisely 0.5 rounding is to even.
                    int dComparePoint5 = fraction.comparePoint5();
                    if (dComparePoint5 > 0 || (!dComparePoint5 && (radix & 1 ? isOddInOddRadix : digit & 1))) {
                        // Check for rounding up; are we closer to the value we'd round off to than
                        // the next IEEE value would be?
                        if (fraction.sumGreaterThanOne(halfDeltaNext)) {
                            needsRoundingUp = true;
                            break;
                        }
                    } else {
                        // Check for rounding down; are we closer to the value we'd round off to than
                        // the prior IEEE value would be?
                        if (fraction < halfDeltaLast)
                            break;
                    }

                    ASSERT(endOfResultString < (buffer + sizeof(buffer) - 1));
                    // Write a digit to the string.
                    fraction *= radix;
                    digit = fraction.floorAndSubtract();
                    *endOfResultString++ = radixDigits[digit];
                    // Keep track whether the portion written is currently even, if the radix is odd.
                    if (digit & 1)
                        isOddInOddRadix = !isOddInOddRadix;

                    // Shift the fractions by radix.
                    halfDeltaNext *= radix;
                    halfDeltaLast *= radix;
                }
            } else {
                // This code is identical to that above, except since deltaNextDouble != deltaLastDouble
                // we don't need to track these two values separately.
                Uint16WithFraction halfDelta(deltaNextDouble, 1);

                while (true) {
                    int dComparePoint5 = fraction.comparePoint5();
                    if (dComparePoint5 > 0 || (!dComparePoint5 && (radix & 1 ? isOddInOddRadix : digit & 1))) {
                        if (fraction.sumGreaterThanOne(halfDelta)) {
                            needsRoundingUp = true;
                            break;
                        }
                    } else if (fraction < halfDelta)
                        break;

                    ASSERT(endOfResultString < (buffer + sizeof(buffer) - 1));
                    fraction *= radix;
                    digit = fraction.floorAndSubtract();
                    if (digit & 1)
                        isOddInOddRadix = !isOddInOddRadix;
                    *endOfResultString++ = radixDigits[digit];

                    halfDelta *= radix;
                }
            }

            // Check if the fraction needs rounding off (flag set in the loop writing digits, above).
            if (needsRoundingUp) {
                // Whilst the last digit is the maximum in the current radix, remove it.
                // e.g. rounding up the last digit in "12.3999" is the same as rounding up the
                // last digit in "12.3" - both round up to "12.4".
                while (endOfResultString[-1] == radixDigits[radix - 1])
                    --endOfResultString;

                // Radix digits are sequential in ascii/unicode, except for '9' and 'a'.
                // E.g. the first 'if' case handles rounding 67.89 to 67.8a in base 16.
                // The 'else if' case handles rounding of all other digits.
                if (endOfResultString[-1] == '9')
                    endOfResultString[-1] = 'a';
                else if (endOfResultString[-1] != '.')
                    ++endOfResultString[-1];
                else {
                    // One other possibility - there may be no digits to round up in the fraction
                    // (or all may be been rounded off already), in which case we may need to
                    // round into the integer portion of the number. Remove the decimal point.
                    --endOfResultString;
                    // In order to get here there must have been a non-zero fraction, in which case
                    // there must be at least one bit of the value's mantissa not in use in the
                    // integer part of the number. As such, adding to the integer part should not
                    // be able to lose precision.
                    ASSERT((integerPart + 1) - integerPart == 1);
                    ++integerPart;
                }
            } else {
                // We only need to check for trailing zeros if the value does not get rounded up.
                while (endOfResultString[-1] == '0')
                    --endOfResultString;
            }

            *endOfResultString = '\0';
            ASSERT(endOfResultString < buffer + sizeof(buffer));
        } else
            *decimalPoint = '\0';

        BigInteger units(integerPart);

        // Always loop at least once, to emit at least '0'.
        do {
            ASSERT(buffer < startOfResultString);

            // Read a single digit and write it to the front of the string.
            // Divide by radix to remove one digit from the value.
            digit = units.divide(radix);
            *--startOfResultString = radixDigits[digit];
        } while (!!units);

        // If the number is negative, prepend '-'.
        if (isNegative)
            *--startOfResultString = '-';
        ASSERT(buffer <= startOfResultString);

        return startOfResultString;
    }

... comme vous pouvez le voir, le nombre ici est soutenu par une traditionnelle double et la conversion est tout sauf simple. Donc, ce que j'ai trouvé est celle-ci: puisque je pense que la seule place que ces implémentations diffèrent sont leurs "rendu" à cordes. J'ai construit un générateur de test qui est trois fois:

  1. les tests de la "chaîne de résultats" à l'encontre d'une chaîne de référence résultat
  2. les tests de leur analysé équivalents (en ignorant tout epsilon, je veux dire exact!)
  3. teste une version spéciale de ces chaînes qui ajuste uniquement pour l'arrondissement de "l'interprétation"

Pour accomplir cela, nous avons besoin d'un accès à une référence de construire, ma première pensée a été d'utiliser l'une à partir d'une langue maternelle, mais avec ce que j'ai constaté que le nombre de produits semble avoir une plus grande précision que le JavaScript en général menant à bien plus d'erreurs. Alors j'ai pensé que, si je viens d'utiliser une mise en œuvre déjà à l'intérieur d'un moteur JavaScript. WebKit/JavaScriptCore semblait être un très bon choix, mais il aurait également été beaucoup de travail pour obtenir la référence de l'accumulation et de la course j'ai donc opté pour la simplicité d' .NET car elle a accès à "jScript" tout n'est pas idéal semblait lors de l'examen initial pour produire des résultats plus proches que le natif de contrepartie. Je n'ai pas vraiment envie de le code jScript puisque le langage est tout sauf obsolète j'ai donc opté pour C# amorçage jScript par le biais d'un CodeDomProvider.... Après un peu de bricolage, voici ce qu'elle produit: http://jsbin.com/afiqil (enfin la démo de la sauce!!!!1!), alors maintenant, vous pouvez l'exécuter dans tous les navigateurs et compiler vos propres données, qui, lors de mon inspection, il semble chaîne d'arrondi interprétation varie dans CHAQUE navigateur, j'ai essayé, mais je n'ai pas encore trouver un grand navigateur, qui traite les nombres en coulisses (autres que les stringify-ing) de la même façon...

maintenant, pour le C# de la sauce:

    using System;
    using System.Collections.Generic;
    using System.ComponentModel;
    using System.Data;
    using System.Drawing;
    using System.Linq;
    using System.Text;
    using System.Windows.Forms;
    using System.CodeDom.Compiler;
    using System.Reflection;

    namespace DoubleFloatJs
    {
        public partial class Form1 : Form
        {

            private static string preamble = @"

    var successes = [];
    var failures = [];

    function fpu_test_add(v1, v2) {
        return '' + (v1 + v2);  
    }

    function fpu_test_sub(v1, v2) {
        return '' + (v1 - v2);
    }

    function fpu_test_mul(v1, v2) {
        return '' + (v1 * v2);
    }

    function fpu_test_div(v1, v2) {
        return '' + (v1 / v2);
    }

    function format(name, result1, result2, result3, received, expected) {
        return '<span style=""display:inline-block;width:350px;"">' + name + '</span>' +
            '<span style=""display:inline-block;width:60px;text-align:center;font-weight:bold; color:' + (result1 ? 'green;"">OK' : 'red;"">NO') + '</span>' + 
            '<span style=""display:inline-block;width:60px;text-align:center;font-weight:bold; color:' + (result2 ? 'green;"">OK' : 'red;"">NO') + '</span>' + 
            '<span style=""display:inline-block;width:60px;text-align:center;font-weight:bold; color:' + (result3 ? 'green;"">OK' : 'red;"">NO') + '</span>' + 
            '<span style=""display:inline-block;width:200px;vertical-align:top;"">' + received + '<br />' + expected + '</span>';
    }

    function check_ignore_round(received, expected) {
        return received.length > 8 &&
            received.length == expected.length && 
            received.substr(0, received.length - 1) === expected.substr(0, expected.length - 1);
    }

    function check_parse_parity_no_epsilon(received, expected) {
        return parseFloat(received) === parseFloat(expected);
    }

    function fpu_test_result(v1, v2, textFn, received, expected) {
        var result = expected === received,
            resultNoRound = check_ignore_round(received, expected),
            resultParse = check_parse_parity_no_epsilon(received, expected),
            resDiv = document.createElement('div');

        resDiv.style.whiteSpace = 'nowrap';
        resDiv.style.fontFamily = 'Courier New, Courier, monospace';
        resDiv.style.fontSize = '0.74em';
        resDiv.style.background = result ? '#aaffaa' : '#ffaaaa';
        resDiv.style.borderBottom = 'solid 1px #696969';
        resDiv.style.padding = '2px';

        resDiv.innerHTML = format(textFn + '(' + v1 + ', ' + v2 + ')', result, resultNoRound, resultParse, received, expected);

        document.body.appendChild(resDiv);
        (result ? successes : failures).push(resDiv);
        return resDiv;
    }

    function fpu_test_run(v1, v2, addRes, subRes, mulRes, divRes) {
        var i, res, 
            fnLst = [fpu_test_add, fpu_test_sub, fpu_test_mul, fpu_test_div],
            fnNam = ['add', 'sub', 'mul', 'div'];

        for (i = 0; i < fnLst.length; i++) {
            res = fnLst[i].call(null, v1, v2);
            fpu_test_result(v1, v2, fnNam[i], res, arguments[i + 2]);
        }
    }

    function setDisplay(s, f) {
        var i;
        for (i = 0; i < successes.length; i++) {
            successes[i].style.display = s;
        }
        for (i = 0; i < failures.length; i++) {
            failures[i].style.display = f;
        }
    }

    var test_header = fpu_test_result('value1', 'value2', 'func', 'received', 'expected'),
        test_header_cols = test_header.getElementsByTagName('span');

    test_header_cols[1].innerHTML = 'string';
    test_header_cols[2].innerHTML = 'rounded';
    test_header_cols[3].innerHTML = 'parsed';
    test_header.style.background = '#aaaaff';

    failures.length = successes.length = 0;

    ";

            private static string summation = @"

    var bs = document.createElement('button');
    var bf = document.createElement('button');
    var ba = document.createElement('button');

    bs.innerHTML = 'show successes (' + successes.length + ')';
    bf.innerHTML = 'show failures (' + failures.length + ')';
    ba.innerHTML = 'show all (' + (successes.length + failures.length) + ')';

    ba.style.width = bs.style.width = bf.style.width = '200px';
    ba.style.margin = bs.style.margin = bf.style.margin = '4px';
    ba.style.padding = bs.style.padding = bf.style.padding = '4px';

    bs.onclick = function() { setDisplay('block', 'none'); };
    bf.onclick = function() { setDisplay('none', 'block'); };
    ba.onclick = function() { setDisplay('block', 'block'); };

    document.body.insertBefore(bs, test_header);
    document.body.insertBefore(bf, test_header);
    document.body.insertBefore(ba, test_header);
    document.body.style.minWidth = '700px';

    ";

            private void buttonGenerate_Click(object sender, EventArgs e)
            {
                var numberOfTests = this.numericNumOfTests.Value;
                var strb = new StringBuilder(preamble);
                var rand = new Random();

                for (int i = 0; i < numberOfTests; i++)
                {
                    double v1 = rand.NextDouble();
                    double v2 = rand.NextDouble();

                    strb.Append("fpu_test_run(")
                        .Append(v1)
                        .Append(", ")
                        .Append(v2)
                        .Append(", '")
                        .Append(JsEval("" + v1 + '+' + v2))
                        .Append("', '")
                        .Append(JsEval("" + v1 + '-' + v2))
                        .Append("', '")
                        .Append(JsEval("" + v1 + '*' + v2))
                        .Append("', '")
                        .Append(JsEval("" + v1 + '/' + v2))
                        .Append("');")
                        .AppendLine();
                }

                strb.Append(summation);

                this.textboxOutput.Text = strb.ToString();
                Clipboard.SetText(this.textboxOutput.Text);
            }

            public Form1()
            {
                InitializeComponent();

                Type evalType = CodeDomProvider
                    .CreateProvider("JScript")
                    .CompileAssemblyFromSource(new CompilerParameters(), "package e{class v{public static function e(e:String):String{return eval(e);}}}")
                    .CompiledAssembly
                    .GetType("e.v");

                this.JsEval = s => (string)evalType.GetMethod("e").Invoke(null, new[] { s });
            }

            private readonly Func<string, string> JsEval;

        }
    }

ou une version pré-compilée si vous devez choisir: http://uploading.com/files/ad4a85md/DoubleFloatJs.exe/ c'est un exécutable, télécharger à vos propres risques

screen shot of test generator application

Je devrais mentionner que le but du programme est de produire un fichier JavaScript dans une zone de texte et le copier dans le presse-papiers pour des raisons de commodité pour les coller partout où vous le souhaitez, vous pourrait facilement tourner autour et de le mettre sur un asp.net serveur et ajouter de la déclaration de résultats de la commande ping sur le serveur et de garder trace dans certains massifs de la base de données... qui est ce que je ferais si je le désirais l'information..

...et, ...je suis passé ...j'espère que cela vous aide -ck

7voto

ivan_pozdeev Points 2233

En résumant tout ci-dessous, vous pouvez vous attendre de la conformité sur la majorité des systèmes à l'exception de quelques IE problèmes, mais devrait utiliser un test de cohérence par précaution (proposition est incluse).

Pour la validation d'un système, vous pouvez utiliser float tests de test262. Ils sont situés à l' http://test262.ecmascript.org/json/ch<2-digit # of spec chapter>.json; code de test peut être extraite avec de l' (python 2.6+):

ch="05";  #substitute chapter #
import urllib,json,base64
j=json.load(urllib.urlopen("http://test262.ecmascript.org/json/ch%s.json"%ch))
tt=j['testsCollection']['tests']
f=open('ch%s.js'%ch,'w')
for t in tt:
  print >>f
  print >>f,base64.b64decode(t['code'])
f.close()

Une autre possibilité est la norme IEEE 754 les tests de conformité dans C.

Les sections pertinentes de test262 (ceux qui permettent de comparer des nombres à virgule flottante) sont comme suit:

{
"S11": "5.1.A4: T1-T8",
"S15": {
    "7": "3: 2.A1 & 3.A1",
    "8": {
        "1": "1-8: A1",
        "2": {
            "4": "A4 & A5",
            "5": "A: 3,6,7,10-13,15,17-19",
            "7": "A6 & A7",
            "13": "A24",
            "16": "A6 & A7",
            "17": "A6",
            "18": "A6"
        }
    }
},
"S8": "5.A2: 1 & 2"
}

cette liste et la concaténation de la source de tous les fichiers de test (comme d'3/9/2012, pas de fichiers à partir du harnais) peut être trouvé ici: http://pastebin.com/U6nX6sKL

5voto

ckozl Points 4838

Règle générale est que lorsque la précision est importante et vous n'avez accès qu'à la précision des nombres à virgule flottante, tous vos calculs doit être fait en entier mathématiques pour mieux s'assurer de la validité (où vous êtes assuré de 15 chiffres assurément des données valides). Et oui il y a un tas de général numérique idiosyncrasies en JavaScript, mais ils sont plus associés avec le manque de précision dans les nombres à virgule flottante et non pas avec de l'UA implémentations de la norme. Regarder autour pour les pièges de calcul en virgule flottante, ils sont nombreux et dangereux.

J'ai l'impression que je devrais en dire un peu plus, par exemple, j'ai écrit un programme (en JavaScript) qui ont utilisé la base de calcul pour déterminer l'aire d'un polygone avec les dimensions en mètres ou en pieds. Au lieu de faire les calculs, le programme converti tout à micromètres et a fait ses calculs il y a comme tout serait plus en plus partie intégrante.

espérons que cela aide -ck


En réponse à vos précisions, commentaires et préoccupations

Je ne vais pas répéter mes commentaires ci-dessous dans leur intégralité, cependant, la réponse courte est que personne ne sera jamais en mesure de dire que CHAQUE APPLICATION est 100% sur 100% des appareils. Période. Ce que je peux dire, et d'autres vont vous dire la même chose, c'est que sur les principaux navigateurs actuels , je n'ai pas vu ni entendu parler de n'importe quel navigateur spécifique préjudiciable bug impliquant des nombres à virgule flottante. Mais votre question elle-même est une sorte d'épée à double tranchant puisque vous voulez "s'appuyer" sur "non fiables" les résultats, ou simplement que vous voulez tous les navigateurs d'être "constamment incompatible" - en d'autres termes, au lieu d'essayer assurez-vous qu'un lion va jouer à rapporter, votre temps serait mieux dépensé à la recherche d'un chien: vous pouvez vous fier à 110% sur entier mathématiques ET les résultats de ces mathématiques, il en va de même pour la chaîne de maths qui a déjà été suggéré de vous...

bonne chance-ck

4voto

Fireblaze Points 134

"J'ai une utilisation spécifique dans l'esprit de répartie application web, qui ne pouvait fonctionner que si je peux compter sur des résultats cohérents sur tous les navigateurs."

Alors la réponse est non. Vous ne pouvez pas le relais sur un cahier des charges pour vous dire qu'un navigateur gère correctement les flotteurs. Chrome se met à jour toutes les 6 semaines, de sorte que même si vous avez les spécifications de Chrome pourrait changer de comportement dans la prochaine version.

Vous devez relais sur la fonction de test qui test vos hypothèses avant à chaque fois avant de calculs est exécuté.

3voto

PiTheNumber Points 8264

Peut-être devriez-vous utiliser une bibliothèque pour vos calculs. Par exemple, bignumber gère correctement les nombres à virgule flottante. Ici, vous devriez être sauvegardé des modifications de l'environnement car il utilise son propre format de stockage.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X