begin process at 2012 05 27 15:36:34
  Trouver un code source :
 
dans
 
Accueil > 

Code

 > 

Maths & Algorithmes

 > [DEV-C++] CALCUL DE LA RACINE CARRÉE D'UN RÉEL

[DEV-C++] CALCUL DE LA RACINE CARRÉE D'UN RÉEL


 Information sur la source

Note :
Aucune note
Catégorie :Maths & Algorithmes Classé sous :maths, racine carrée, mémoire, float, union Niveau :Initié Date de création :21/01/2010 Date de mise à jour :26/01/2010 11:19:07 Vu :4 326

Auteur : Jhep

Ecrire un message privé
Site perso
Commentaire sur cette source (17)
Ajouter un commentaire et/ou une note

 Description

Cette source permet de calculer une racine carrée par la méthode de Newton avec une approximation par division de l'exposant binaire faisant partie de la représentation en virgule flottante (ma réelle contribution).

Source

  • #include <stdio.h>
  • #include <stdlib.h>
  • #include <time.h>
  • #include <math.h>
  • #include <string.h>
  • #define u_int unsigned int
  • #define iter 10
  • // Ce type de structure est peu employé mais parfait pour cette routine :
  • // Elle permet de déclarer deux variable de type/taille différente sur le même espace de
  • // mémoire.
  • union flint {
  • float f;
  • u_int i;
  • };
  • float rac(float x)
  • {
  • float y1;
  • u_int b;
  • flint ya;
  • int i;
  • if (x<=0) return 0;
  • // on duplique le nombre avec un type adéquat pour les opérations binaires
  • ya.f=x;
  • // en tant que float un nombre est sous la forme m * 2 ^ e avec m < 1
  • // la racine carrée = (m * 2 ^ e) ^ 1/2 et comme m < 1
  • // m * 2 ^ (e/2) en est une approximation
  • //
  • // le type float est grossièrement codé ainsi (sur 32bits) :
  • // 0 01010101 11100011100000001110000
  • // ^ exposant mantisse
  • // '-signe
  • // première opération :
  • // déplace l'exposant (opérateur >>):
  • // 0exposant11100011100000001110000
  • // => 000000000000000000000000exposant
  • b = ya.i>>23;
  • // l'exposant est augmenté de 127 en mémoire pour permettre la représentation de nombre
  • // dont l'exposant serait négatif.
  • // il faut donc soustraire 127 avant de diviser par 2
  • // b=(b-127)/2+127=(b+127)/2
  • // => 000000000000000000000000demiexpo
  • b=(b+127)/2;
  • // finalement on "réassemble" le nombre avec le nouvel exposant :
  • // d'abord on annule les bit de l'ancien exposant
  • // 00101010111100011100000001110000 & 10000000011111111111111111111111
  • // x 0x807fffff
  • // => 00000000011100011100000001110000
  • // << repositionne les bit au bon endroit
  • // 000000000000000000000000demiexpo => 0demiexpo00000000000000000000000
  • // le | ou binaire permet "d'ajouter" notre exposant
  • // 00000000011100011100000001110000 | 0demiexpo00000000000000000000000
  • // => 0demiexpo11100011100000001110000
  • ya.i = (ya.i&0x807fffff)|(b<<23);
  • // méthode de Newton
  • ya.f=(ya.f+x/ya.f)*0.5;
  • ya.f=(ya.f+x/ya.f)*0.5;
  • ya.f=(ya.f+x/ya.f)*0.5;
  • return ya.f;
  • }
  • int main(int argc, char *argv[])
  • {
  • float a = 54444.000549, b;
  • clock_t t;
  • int i, n=10000000;
  • printf("%f\n", sqrtf(a));
  • printf("%f\n", rac(a));
  • t = clock();
  • for(i=0;i<n;++i) sqrtf(a);
  • printf("%i\n", clock()-t);
  • t = clock();
  • for(i=0;i<n;++i) rac(a);
  • printf("%i\n", clock()-t);
  • getchar();
  • }
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>
#include <string.h>

#define u_int unsigned int

#define iter 10

// Ce type de structure est peu employé mais parfait pour cette routine :
// Elle permet de déclarer deux variable de type/taille différente sur le même espace de
// mémoire.

union flint {
	float f;
	u_int i;
};

float rac(float x)
{
	float y1;
 	u_int b;
	flint ya;
	int i;

	if (x<=0) return 0;

//	on duplique le nombre avec un type adéquat pour les opérations binaires
	ya.f=x;

//	en tant que float un nombre est sous la forme m * 2 ^ e avec m < 1
//	la racine carrée = (m * 2 ^ e) ^ 1/2 et comme m < 1
//	m * 2 ^ (e/2) en est une approximation
//	
//	le type float est grossièrement codé ainsi (sur 32bits) :
//	0 01010101 11100011100000001110000
//	^ exposant      mantisse
//	'-signe

//	première opération :
//	déplace l'exposant (opérateur >>):
//		0exposant11100011100000001110000
//	=>	000000000000000000000000exposant
	b = ya.i>>23;

//	l'exposant est augmenté de 127 en mémoire pour permettre la représentation de nombre
//	dont l'exposant serait négatif.
//	il faut donc soustraire 127 avant de diviser par 2
//	b=(b-127)/2+127=(b+127)/2
//	=>	000000000000000000000000demiexpo
	b=(b+127)/2;

//	finalement on "réassemble" le nombre avec le nouvel exposant :

//	d'abord on annule les bit de l'ancien exposant
//		00101010111100011100000001110000 & 10000000011111111111111111111111
//		               x                            0x807fffff
//	=>	00000000011100011100000001110000

//	<< repositionne les bit au bon endroit
//	000000000000000000000000demiexpo => 0demiexpo00000000000000000000000

//	le | ou binaire permet "d'ajouter" notre exposant
//		00000000011100011100000001110000 | 0demiexpo00000000000000000000000
//	=>	0demiexpo11100011100000001110000
	ya.i = (ya.i&0x807fffff)|(b<<23);

//	méthode de Newton
	ya.f=(ya.f+x/ya.f)*0.5;
	ya.f=(ya.f+x/ya.f)*0.5;
	ya.f=(ya.f+x/ya.f)*0.5;
	return ya.f;
}

int main(int argc, char *argv[])
{
	float a = 54444.000549, b;
	clock_t t;
	int i, n=10000000;

	printf("%f\n", sqrtf(a));
	printf("%f\n", rac(a));

	t = clock();
	for(i=0;i<n;++i) sqrtf(a);
	printf("%i\n", clock()-t);

	t = clock();
	for(i=0;i<n;++i) rac(a);
	printf("%i\n", clock()-t);

	getchar();
}



 Historique

21 janvier 2010 16:22:46 :
--
21 janvier 2010 16:36:05 :
--
25 janvier 2010 14:04:53 :
utilisation de union ce qui permet d'économiser un appel de memcpy() pour utiliser la variable dont on a manipulé les bits en tant que float.
25 janvier 2010 16:00:41 :
memset(&ya.i, &x, 4); devient ya.i=*(int *)&x; pourquoi n'y ai-je pas pensé plus tôt ?.... vitesse compétitive atteinte ! 500ms contre 700ms initiales ^^
25 janvier 2010 16:34:07 :
pourquoi faire simple..
26 janvier 2010 11:19:07 :
plus de boucle (stupid) -> 200ms!

 Sources du même auteur

Source avec Zip MINI SERVER FTP
Source avec Zip APPLI WINDOWS POUR UTILISER WINSOCK [DEV-C++]
Source avec Zip SCANNER IP ET PORTS [DEV-C++]

 Sources de la même categorie

Source avec Zip UN EXAMPLE D'APPLICATION EN CUDA DE L'ALGORITHME DE SCAN POU... par oguzaras
Source avec Zip Source avec une capture CHIFFREMENT DE VIGENERE par lajouad
Source avec Zip Source avec une capture ANALYSE SYNTAXIQUE par lajouad
Source avec Zip Source avec une capture STRUCTURE D'UNE MATRICE PAR LES LISTE LINÉAIRE (NON CONTUGUS... par benzarabel
Source avec Zip Source avec une capture DESSINER UNE ARBRE BINAIRE( MODE CONSOLE): par benzarabel

 Sources en rapport avec celle ci

Source avec une capture MULTIPLICATIONS: PETIT EXERCICE DE MATHS par gmorris
Source avec Zip Source avec une capture RESOLUTION DE SYSTEME LINEAIRE PAR LA METHODE DU GRADIENT CO... par zangul
PROCESS DUMPER par lilxam7
Source avec Zip COMPILATEUR ET DÉCOMPILATEUR DE SOURCES MALBOLGE [MLBC] par youscef
DECODAGE IEEE754 par selligattangip

Commentaires et avis

Commentaire de BruNews le 21/01/2010 23:00:25 administrateur CS

Pour info:
Intel publiait cette version en optmisé (ASM) et la livrait incluse dans sa bibili AMATH.
Comme ça fait un bon moment que tous les PCs ont au moins un CPU équipé de SSE2, on fait 4 racines carrées en 1 seule instruction:
sqrtps xmm1, xmm2
Ce qui fait que toutes ces méthodes n'ont plus cours.

Commentaire de Warny le 22/01/2010 09:46:02

Si on excepte le fait que ton code n'optimise pas les calculs, il n'n reste pas moins un bon tutoriel sur la manipulation en mémoire des nombres réels. A défaut d'utiliser le code un jour, il m'a appris plein de trucs.

Commentaire de Jhep le 25/01/2010 08:26:23

BruNews>merci ! j'avais lu que "certains" CPU avaient la fonction intégrée mais je ne pensais pas que c'était général cela explique la vitesse d'exécution. Tanpis j'aurais essayé ^^.
Warny>c'est exactement ce que je cherche, optimiser les calculs. j'ai fait mon maximum mais dis moi vite comment faire mieux!

Commentaire de Jhep le 25/01/2010 08:44:10

il doit être possible d'utiliser les registres processeur non ?

Commentaire de Warny le 25/01/2010 10:48:20

Pour les registres processeurs, il servent d'arguments et de résultat d'instructions processeur.
Il faut donc récupérer la doc de ton processeur, retrouver l'instruction qui fait la racine carrée et lui passer les bons arguments. Tu bénéficieras dans ce contexte de l'algorithme du processeur.
Cependant, nous perdrions ici l'interêt de ton code qui est de présenter la façon de calculer une racine carrée et la structure des réels en mémoire. Même si ton algorithme est moins rapide que le processeur, il n'en reste pas moins interressant dans une optique de formation.

Commentaire de Jhep le 25/01/2010 13:56:01

oui je cherche toujours à optimiser ce code, je pensais utiliser le type de déclaration register int / register float, mais ca ne donne rien.
j'ai cependant trouver une optimisation (MAJ)
en ce qui concerne les optimisations que suggérait ton premier commentaire?

Commentaire de ctx_man le 25/01/2010 15:15:45

Question optimisation tu peux faire plusieurs choses.
Déjà, je trouve plus simple de décaler de 1 vers la gauche, que de 23 vers la droite, tu n'as bit de signe a sauvegarder.
Ensuite, faire un union float/int te permet d'agir sur les deux sans avoir a utiliser memcpy (donc pas d'appel de fonction).
L'utilisation de pointeurs également (poser un int* sur ton float te permet de faire des décalages binaires sans avoir a dupliquer la mémoire).
En faisant une union float/int/char[4] ca te permet d'initialiser via la float, de décaler de 1 vers la gauche via le int, d'obtenir d'exposant via le char[3] (ou char[0] sur certaines plateforme). Ca t'évites toutes les allocations temporaires.
On pourrais également utiliser un champs de bit pour accéder directement aux parties qui nous intéressent.
struct sFloat
{
    unsigned int _signe:1;
    unsigned int _exposant:8;
    unsigned int _mantisse:23;
};
L'utilisation du mot "register" permet d'utiliser directement un registre et donc d'éviter les déplacement mémoire<->registre, mais ca ne se verra pas en mode débug. Passer la fonction en fonction inline boostera aussi quelque peu les perfs, mais idem, ca ne se verra pas en mode débug.
La différence entre la fonction sqrt et la tienne c'est que la sqrt est compilée en utilisant les options d'optimisation du compilateur, si la tienne l'étais aussi il n'y aurait pas une telle différence (bien que je pense que la fonction standard reste bien plus rapide). Le problème est que si tu actives les optimisations, l'optimiseur verra que tes boucles ne font rien (puisque leurs résultat n'est pas utilisé) et donc supprimera la boucle (en tout cas celui de Visual Studio le fait, même si je fait une boucle for de 4 000 000 000, ca prend toujours 0ms quelle que soit la méthode).
Après, quand on veux vraiment faire de l'optimisation faut se tourner vers l'assembleur en étudiant bien la documentation.
Petits exemple :
Mettre 0 dans un registre se fait "mov eax,0" mais "xor eax,eax" donne le même résultat avec 1 cycle de moins. Diviser par deux un entier c'est un décalage de 1bit vers la droite (attention a la perte du reste de la division).
Du coup, en assembleur il existe plusieurs façon de faire les choses, et la méthode la plus rapide n'est pas la même sur toute les plateformes.

PS : le temps d'écrire ce message je vois que tu a trouvé la technique des unions tout seul ^^

Commentaire de ctx_man le 25/01/2010 15:20:59

Vu que tu utilises maintenant une union, autant vraiment l'utiliser :
memcpy(&ya.i, &x, 4);
a remplacer par
ya.f = x;
Ca te fait un appel de fonction en moins, et plus besoin de passer par les adresses des variables.

Commentaire de Jhep le 25/01/2010 16:10:17

XD merci on est arrivé aux deux mêmes conclusions ^^
si quelqu'un a plus d'idée en restant en c (par exemple utilisant des astuces mathématiques)
je pense que cet article :
http://www.azillionmonkeys.com/qed/sqroot.html
permet de clore le sujet, tout y est (c'est Quake 3 Arena qui gagne ^^ IMPRESSIVE!).

a++

Commentaire de Jhep le 25/01/2010 16:13:09

merci pour l'info sur les champs de bit encore quelque chose que j'ignorais.


autre chose, c'est dommage de pas pouvoir éditer ses commentaires...

Commentaire de ctx_man le 25/01/2010 16:22:35

Oui je trouve aussi pour l'édition des commentaires.
Sinon, je comprend pas pourquoi tu fais "ya.i=*(int*)&x;" au lieu de "ya.f = x;" comme je te l'avais indiqué ?
Parce que la tu tapes ca place un pointeur dans la pile qui pointe sur x, pour après faire un dé-référencement pour obtenir la valeur, alors que l'union te permet de placer la valeur directement.
De plus, comme je l'indiquais dans mon commentaire, mettre un tableau de 4 char ca te permet d'accèder aux paquets de 8bits et donc directement à l'exposant sans avoir besoin de faire mumuse avec les masques binaires.
u_int signe = ya.i & 0x80000000; // Sauvegarde le signe
ya.i = ya.i << 1; // Décale l'exposant de 1 vers la gauche plutot que 23 vers la droite
ya.c[3] = (ya.c[3] + 127) / 2; // opération sur char donc pas de masque ca sortira pas du char
ya.i = ya.i >> 1; // On replace le tout comme c'était
ya.i = ya.i | signe; // On remet le signe

Commentaire de Jhep le 25/01/2010 16:32:27

okay. ya.i=*(int*)&x; est quelque chose auquel j'aurais du penser à la première version du code (évitant ainsi le memcpy) donc quand enfin ca m'est venu je l'ai mis sans réfléchir, effectivement autant faire simple ^^.
ton code parfait (j'étais en train de faire de même pour comparer la vitesse d'exécution)
à ceci près que le problème du signe est trivial, s'agissant d'une racine carrée ;)
mais bon du coup c'est plus très pédagogique pour les opérations binaire :)

en tous cas ça fait plaisir de pas coder tout seul :D... bon passons à autre chose

Commentaire de ctx_man le 25/01/2010 16:38:23

Oui oui, on s'en tamponne du signe, je l'ai conservé uniquement pour la pédagogie. C'était un bit de perdu lors du déplacement binaire du coup je préférai montrer comment le conserver et le restaurer. Mais je suis d'accord, vu que c'est une racine carrée le signe est forcément positif, donc 0, or ce sont des 0 qui sont insérés dans les décalages. (Encore que, je crois me souvenir que quand tu décales, le dernier bit sorti est posé dans un flag de retenu et est ré-inséré si tu re-décale dans l'autre sens, mais je suis pas catégorique la dessus, ca demande teste)

Commentaire de Jhep le 25/01/2010 18:05:38

bon je viens de tester la vitesse et contrairement à mon attente l'utilisation des char augmente le temps d'exécution. mais bon gcc n'est pas forcement aussi bien que vc++ en terme d'optimisation.
mais en y repensant ca ne devrait même pas marcher car l'opération ya.c[3] = (ya.c[3] + 127) / 2 dépasse le plus grand nombre sur 1 octet : 255...

Commentaire de ctx_man le 26/01/2010 11:44:17

Personnellement je ne pense pas que gcc soit moins bon en optimisation que vc++.
Il y a des tonnes de flags qui peuvent jouer et tellement de choses peuvent influer sur le résultat du bench présent dans ta fonction "main".
Concernant le dépassement de l'opération, normalement ca passe correctement. ya.[3] + 127 fait plus que 255, oui, mais il n'est pas stocké, donc il se retrouve soit en registre, soit en pile, une fois divisé par 2, le nombre ne peux plus faire plus de 8 bits, et donc tiens sur un octet, et là il est stocké dans ya.c[3]. Pour en être absolument certain, tu peux caster.
Après, l'utilisation de ce tableau est surtout une facilité d'écriture du programme (plus simple qu'un masque), question perf ca doit jouer a peine, pas facile de vérifier. J'ai repris le code tel qu'il est actuellement et j'ai ajouté une seconde fonction en utilisant le tableau de char, chez moi, 6 fois sur 10, la fonction utilisant le char est plus rapide de quelques millisecondes, mais c'est pas non plus loins devant. Faudrait refaire le teste encore et encore pour savoir si en moyenne une fonction est plus rapide que l'autre. Le mieux étant de décompiler pour voir l'assembleur résultant et compter le nombre de cycles consommés. Une comparaison par appel de l'horloge ne fait que donner un ordre d'idée.
Si tu veux continuer a optimiser encore plus, il faut passer en assembleur surtout que chaque compilateur va donner un assembleur différent et donc des performances différentes (avec un compilateur une fonction sera plus rapide que l'autre, et ce sera l'inverse sur un autre compilateur). Mais bon, de toute façon tu n'atteindra pas le score de la fonction standard qui se contente d'utiliser l'instruction assembleur du coprocesseur mathématique ou de SSE2 et qui donc te sort le résultat en une seule instruction.

Commentaire de BruNews le 26/01/2010 13:18:48 administrateur CS

ya.c[3] = (ya.c[3] + 127) / 2;
On ne doit surtout pas présupposer sur ce qui est codé par le compilo, quel qu'il soit.
Soit on vérifie le listing ASM et si calculs faits sur DWORD alors ok mais si faits sur AL que nenni.
Dans tous les cas je déconseille fort cette manière, aucune assurance que ira encore quand on change de compilo ou simplement de version de compilo.

DWORD v = (DWORD) ya.c[3];
ya.c[3] = (BYTE) ((v + 127) / 2);
C'est pas épuisant à taper et on est assuré du bon résultat.

Commentaire de ctx_man le 26/01/2010 14:19:25

En l'occurence sur ce genre d'opération, tous les compilos que j'ai testé détectent s'il va y avoir perte de donnée et te préviennent. Mais je suis d'accord sur le fait qu'il ne faut pas présumer de ce que va coder le compilo, je l'ai fais ainsi parce que j'ai vérifié ce que ca donnais avec le mien, mais comme je le disais également, pour en être véritablement certain, il faut caster, exactement comme tu le montres.

 Ajouter un commentaire


Discussions en rapport avec ce code source dans le forum

Conversion d un float [ par Seth ] Comment arrondir un float vers le nombre le plus proche.Exemple : (float)2.8 -&gt; 3 ! (float)2.3 -&gt; 2 Pointeur qui fait planter Windows !!! [ par coyito ] Salutquand je défini moi même une addresse pour un pointeur (exemple pour lire n'importe ou dans la mémoire) j'ai une erreur windows "access violation je cherche quelqu'un fort en maths et qui est en 1ere ES !! [ par GEO ] je cherche quelqu'un qui aurais le livre orange declic maths de 1 ES merci d'avance [C++] precision [ par kikiops ] comment faire pour avoir des resultats float avec deux decimalesj'vous donne un ch'tit exemple , ca sera plus simple pour m'faire comprendrefloat a = Conversion de donnée [ par Johjo ] Salut tout le monde, je cherche à convertir une valeur char en valeur float et inversement de float en char. L'equivalent de Val et Str en basic.Merci Convertir une chaine de caractere en Float [ par Johjo ] Bon, voilà, j'ai encore un probleme.Je dois maintenant convertir un chaine de caractere en float, et je ne trouve pas de fonctions, j'ai regardé dans dépassement capacité d'un float et int [ par golum ] result=scanf("%f",&coef); if (result !=0 && coef !=0)Voila si j'entre un nombre délirant style 9999999999999999999999999999999999999999999999999999999 Segmenter un fichier en mémoire C (seulement) [ par golum ] Voila pour ouvrir a partir de mon prog c un fichier de 20 Mo je met 1min et j'aimerais a tout pris diminuer ce temps.Est-il possible de segmenter le f Conversion Float to String [ par PierreP ] Bonjour à tous !je suis en train de me prendre la tête pour créer une fonction de conversion d'un réel en une chaine de caractère (problème du débutan arrondir un chiffre [ par JosueClement ] en admettant que j'ai une variable de type float:float var = 6.98466;je voudrais pouvoir arrondir le chiffre. par exemple 6.98 !Merci d'avanceJosué Cl


Nos sponsors


Sondage...

Comparez les prix

CalendriCode

Mai 2012
LMMJVSD
 123456
78910111213
14151617181920
21222324252627
28293031   

Consulter la suite du CalendriCode

A découvrir



 
Développement réalisé par Nicolas SOREL (Nix) avec l'aide de : Cyril DURAND et Emmanuel (EBArtSoft), Merci à Vincent pour ses précieux conseils.
CodeS-SourceS.com© Toute reproduction même partielle est interdite sauf accord écrit du Webmaster
CodeS-SourceS.com© est une marque déposée tous droits réservés

Google Coop CodeS-SourceS Google Coop CodeS-SourceS
Temps d'éxécution de la page : 1,232 sec (3)

Nous contacter | Annoncer sur CodeS-SourceS | Mentions légales