La réponse de animal_magic est correcte: vous devez ajouter les nombres du plus petit au plus grand, mais je veux donner un exemple pour montrer pourquoi.
Supposons que nous travaillons dans un format à virgule flottante qui nous donne une précision stupéfiante de 3 chiffres. Maintenant, nous voulons ajouter dix nombres:
[1000, 1, 1, 1, 1, 1, 1, 1, 1, 1]
Bien sûr, la réponse exacte est 1009, mais nous ne pouvons pas l'obtenir dans notre format à 3 chiffres. Arrondi à 3 chiffres, la réponse la plus précise que nous obtenons est 1010. Si nous ajoutons du plus petit au plus grand, sur chaque boucle, nous obtenons:
Loop Index s
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 1009 -> 1010
Nous obtenons donc la réponse la plus précise possible pour notre format. Supposons maintenant que nous ajoutons du plus grand au plus petit.
Loop Index s
1 1000
2 1001 -> 1000
3 1001 -> 1000
4 1001 -> 1000
5 1001 -> 1000
6 1001 -> 1000
7 1001 -> 1000
8 1001 -> 1000
9 1001 -> 1000
10 1001 -> 1000
Étant donné que les nombres à virgule flottante sont arrondis après chaque opération, tous les ajouts sont arrondis, augmentant notre erreur de 1 à 9 par rapport à l'exacte. Imaginez maintenant si votre ensemble de nombres à ajouter avait un 1000, puis cent 1 ou un million. Notez que pour être vraiment précis, vous devez additionner les deux plus petits nombres, puis utiliser le résultat dans votre ensemble de nombres.
Les réponses précédentes discutent déjà de la question dans son ensemble et donnent de bons conseils, mais il y a une bizarrerie supplémentaire que je voudrais mentionner. Sur la plupart des architectures modernes, la
for
boucle que vous avez décrite serait exécutée de toute façon avec une précision étendue de 80 bits , ce qui garantit une précision supplémentaire, car toutes les variables temporaires seront placées dans des registres. Vous disposez donc déjà d'une forme de protection contre les erreurs numériques. Cependant, dans des boucles plus compliquées, les valeurs intermédiaires seront stockées en mémoire entre les opérations, et donc tronquées à 64 bits. je suppose quesuffit pour obtenir une précision moindre dans votre sommation (!!). Soyez donc très prudent si vous souhaitez imprimer-déboguer votre code tout en vérifiant l'exactitude.
Pour les personnes intéressées, cet article décrit un problème dans une routine numérique largement utilisée (factorisation QR révélatrice de rang de Lapack) dont le débogage et l'analyse étaient très délicats précisément à cause de ce problème.
la source
Des 2 options, l'ajout du plus petit au plus grand produira moins d'erreur numérique que l'ajout du plus grand au plus petit.
Cependant, il y a> 20 ans dans ma classe "Méthodes numériques", l'instructeur l'a déclaré et il m'est venu à l'esprit que cela introduisait toujours plus d'erreurs que nécessaire en raison de la différence relative de valeur entre l'accumulateur et les valeurs ajoutées.
Logiquement, une solution préférable consiste à ajouter les 2 plus petits nombres dans la liste, puis à réinsérer la valeur additionnée dans la liste triée.
Pour le démontrer, j'ai élaboré un algorithme qui pourrait le faire efficacement (dans l'espace et dans le temps) en utilisant l'espace libéré lorsque les éléments ont été supprimés du tableau principal pour créer un tableau secondaire des valeurs sommées qui ont été intrinsèquement ordonnées depuis les ajouts. étaient des sommes de valeurs toujours croissantes. À chaque itération, les "astuces" des deux tableaux sont ensuite vérifiées pour trouver les 2 plus petites valeurs.
la source
Puisque vous n'avez pas restreint le type de données à utiliser, pour obtenir un résultat parfaitement précis, utilisez simplement des nombres de longueur arbitraire ... dans ce cas, l'ordre n'aura pas d'importance. Ce sera beaucoup plus lent, mais l'obtention de la perfection prend du temps.
la source
Utiliser l'ajout d'arbre binaire, c'est-à-dire choisir la moyenne de la distribution (nombre le plus proche) comme racine de l'arbre binaire, et créer un arbre binaire trié en ajoutant des valeurs inférieures à gauche du graphique et des valeurs plus grandes à droite et ainsi de suite . L'ajout de tous les nœuds enfants d'un parent unique récursivement dans une approche ascendante. Cela sera efficace car l'erreur moyenne augmente avec le nombre de sommations et dans une approche d'arbre binaire, le nombre de sommations est de l'ordre de log n en base 2. Par conséquent, l'erreur moyenne serait moindre.
la source
Ce que Hristo Iliev a dit ci-dessus à propos des compilateurs 64 bits préférant les instructions SSE et AVX au FPU (AKA NDP) est absolument vrai, du moins pour Microsoft Visual Studio 2013. Cependant, pour les opérations à virgule flottante double précision que j'utilisais, j'ai trouvé il est en fait plus rapide, et en théorie plus précis, d'utiliser le FPU. Si c'est important pour vous, je vous suggère de tester différentes solutions avant de choisir une approche finale.
Lorsque je travaille en Java, j'utilise très fréquemment le type de données BigDecimal à précision arbitraire. C'est tout simplement trop facile, et on ne remarque généralement pas la diminution de la vitesse. Le calcul des fonctions transcendantales avec des séries infinies et sqrt en utilisant la méthode de Newton peut prendre une milliseconde ou plus, mais il est faisable et assez précis.
la source
Je n'ai laissé cela ici /programming//a/58006104/860099 (lorsque vous y allez, cliquez pour «afficher l'extrait de code» et l'exécuter par le bouton
C'est un exemple JavaScript qui montre clairement que la somme à partir de la plus grande donne une plus grande erreur
la source