Question

Je suis en train d'optimiser ce qui suit. Le soufflet de code fait ceci:

Si a = 0,775 et j'ai besoin de précision 2 dp alors => 0,78

En fait, si le dernier chiffre est 5, il arrondit vers le haut le chiffre suivant, sinon il ne fonctionne pas.

Mon problème était que 0,45 tour ne marche pas à 0,5 avec 1 decimalpoint, la valeur est enregistrée en tant que ,44999999343 .... et SetPrecision arrondit à 0,4.

C'est pourquoi SetPrecision est forcé d'être plus setprecision(p+10) et si elle se termine vraiment dans un 5, ajoutez la petite quantité afin d'arrondir correctement.

Une fois fait, il compare une chaîne avec b et renvoie le résultat. Le problème est, cette fonction est appelée quelques milliards de fois, ce qui rend le jabot du programme. Une meilleure idée sur la façon de réécrire / optimiser cela et quelles fonctions dans le code sont si lourds sur la machine?

bool match(double a,string b,int p) { //p = precision no greater than 7dp

    double t[] = {0.2, 0.02, 0.002, 0.0002, 0.00002, 0.000002, 0.0000002, 0.00000002};

    stringstream buff;
    string temp;

    buff << setprecision(p+10) << setiosflags(ios_base::fixed) << a; // 10 decimal precision
    buff >> temp;

    if(temp[temp.size()-10] == '5')  a += t[p]; // help to round upwards

    ostringstream test;
    test << setprecision(p) << setiosflags(ios_base::fixed) << a;
    temp = test.str();

    if(b.compare(temp) == 0) return true;

    return false;
}
Était-ce utile?

La solution

J'ai écrit un entier sous-programme de racine carrée avec rien de plus que des lignes de deux douzaines de l'ASM, sans appels d'API que ce soit - et il ne pouvait encore faire environ 50 millions SqRoots / seconde (ce qui était il y a cinq ans ...) .

Le point que je fais est que si vous allez pour des milliards d'appels, la technologie, aujourd'hui encore, va étouffer.

Mais si vous voulez vraiment faire un effort pour accélérer, supprimer autant les usages de l'API qu'il est humainement possible. Cela peut vous obliger à effectuer des tâches de l'API manuellement, au lieu de laisser les bibliothèques le faire pour vous. Plus précisément, supprimer tout type d'opération de flux. Ce sont plus lents que la saleté dans ce contexte. Vous pouvez vraiment improviser là-bas.

La seule chose à faire après cela est de remplacer autant de lignes de C ++ que vous pouvez avec ASM personnalisé - mais vous devrez être perfectionniste à ce sujet. Assurez-vous que vous profitez pleinement de chaque cycle CPU et inscrivez-vous - ainsi que chaque octet de cache du processeur et de l'espace de pile.

Vous pouvez envisager d'utiliser des valeurs entières au lieu de flotter points, car ceux-ci sont beaucoup plus ASM conviviale et beaucoup plus efficace. Il faudrait multiplier le nombre par 10 ^ 7 (ou 10 ^ p, selon la façon dont vous décidez de former votre logique) pour déplacer la décimale tout le chemin vers la droite. Ensuite, vous pouvez en toute sécurité le convertir en virgule flottante en un nombre entier de base.

Vous devrez compter sur le matériel informatique pour le reste.

<--Microsoft Specific-->
Je vais aussi ajouter que les identifiants de C (y compris les statiques, comme Donnie DeBoer mentionné) sont directement accessibles à partir de blocs ASM imbriqués dans votre code C ++. Cela rend ASM un jeu d'enfant en ligne.
<--End Microsoft Specific-->

Autres conseils

En fonction de ce que vous voulez que les chiffres pour, vous pouvez utiliser des nombres à virgule fixe au lieu de la virgule flottante. Une recherche rapide se présente cette .

Je pense que vous pouvez simplement ajouter 0,005 précision au centième, 0,0005 pour des milliers, etc. snprintf le résultat avec quelque chose comme « % 1.2f » (centièmes, millièmes de 1.3f, etc.) et de comparer les cordes. Vous devriez être en mesure de table Ize ou paramétrez cette logique.

Vous pourriez économiser quelques cycles majeurs dans votre code affiché simplement en faisant ce double t [] statique, de sorte que ce n'est pas allouer plus et plus.

Essayez ceci:

#include <cmath>

double setprecision(double x, int prec) {
    return 
        ceil( x * pow(10,(double)prec) - .4999999999999)
        / pow(10,(double)prec);
}

Il est probablement plus rapide. Peut-être essayer inline aussi bien, mais cela pourrait faire du mal si elle ne permet pas.

Exemple de comment cela fonctionne:

2.345* 100 (10 to the 2nd power) = 234.5
234.5 - .4999999999999 = 234.0000000000001
ceil( 234.0000000000001 ) = 235
235 / 100 (10 to the 2nd power) = 2.35

Le ,4999999999999 a été choisi en raison de la précision d'un c ++ doubles sur un système 32 bits. Si vous êtes sur une plate-forme 64 bits, vous aurez probablement besoin de plus nines. Si vous augmentez les nines plus loin un système 32 bits, il déborde et arrondit au lieu de monter, i. e. 234,00000000000001 est tronqué à 234 en double dans (mon) 32 bits environnement.

En utilisant à virgule flottante (une représentation inexacte) signifie que vous avez perdu des informations sur le nombre réel. Vous ne pouvez pas simplement « fixer » la valeur stockée dans le double en ajoutant une valeur de fudge. Cela pourrait fixer certains cas (comme .45), mais il se brisera d'autres cas. Vous finirez par arrondir le nombre qui aurait été arrondis.

Voici un article connexe: http://www.theregister.co.uk/2006/08/12 / floating_point_approximation /

Je prends à deviner ce que vous entendez vraiment faire. Je pense que vous essayez de voir si une chaîne contient une représentation décimale d'un double à une certaine précision. Peut-être est un programme quiz arithmétique et que vous essayez de voir si est « assez proche » pour la vraie réponse de réponse de l'utilisateur. Si tel est le cas, il peut être plus simple de convertir la chaîne en un double et si la valeur absolue de la différence entre les deux doubles est dans une certaine tolérance.

double string_to_double(const std::string &s)
{
    std::stringstream buffer(s);
    double d = 0.0;
    buffer >> d;
    return d;
}

bool match(const std::string &guess, double answer, int precision)
{
    const static double thresh[] = { 0.5, 0.05, 0.005, 0.0005, /* etc. */ };
    const double g = string_to_double(guess);
    const double delta = g - answer;
    return -thresh[precision] < delta && delta <= thresh[precision];
}

Une autre possibilité consiste à arrondir la première réponse (alors qu'il est encore numérique) avant de le convertir en une chaîne.

bool match2(const std::string &guess, double answer, int precision)
{
    const static double thresh[] = {0.5, 0.05, 0.005, 0.0005, /* etc. */ };
    const double rounded = answer + thresh[precision];
    std::stringstream buffer;
    buffer << std::setprecision(precision) << rounded;
    return guess == buffer.str();
}

Ces deux solutions devrait être plus rapide que votre exemple de code, mais je ne sais pas s'ils font ce que vous voulez vraiment.

Pour autant que je vois que vous vérifiez si un arrondi sur les points p est égal b.

Insted de changer une chaîne à, faire autre chaîne manière et le changement de doubler - (seulement multiplications et addion ou seulement additoins en utilisant une petite table) - puis soustraire deux nombres et vérifier si la soustraction est dans la plage correcte (si p == 1 => abs (p-a) <0,05)

Les développeurs de temps vieux truc des âges sombres de livres, Shilling et pence dans le vieux pays.

L'astuce était de stocker la valeur comme un nombre entier fo demi-pennys. (Ou quel que soit votre plus petite unité est). Puis tout votre arithmatic ultérieure est un entier simple arithimatic et arrondi etc prendra soin de lui-même.

Dans votre cas, vous stockez vos données en unités de 200ths de ce que vous comptez, faire des calculs entiers simples sur ces valeurs et diviser par 200 en un varaible flotteur chaque fois que vous voulez afficher le résultat.

Je beleive Boost fait une bibliothèque « BigDecimal » ces jours-ci, mais, votre exigence pour la vitesse de l'exécution serait probablement exclure cette solution par ailleurs excellent.

On dirait que ce que vous essayez de faire est pas un arrondi réel. 0.45 est en effet 0.45 en notation binaire et ,44999999343 n'est pas la même chose.

Peut-vous devez faire plusieurs arrondi -. Tout d'abord dire 3 décimales, puis à deux, puis à un

La question est, qu'est-ce que vous essayez d'accomplir? Si votre critère de correspondance est

abs(a-b) < 10 ** -p

au lieu?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top