salut ;
comment convertir un double en int64 avec le langage C sachant que:
Merci par avance
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 struct int64 { unsigned int LSW; unsigned int MSW; };
salut ;
comment convertir un double en int64 avec le langage C sachant que:
Merci par avance
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5 struct int64 { unsigned int LSW; unsigned int MSW; };
J'ai une solution qui n'est pas simple :
Rappelons d'abord le codage le codage des floats en C:
Il me semble que votre problème n'a de sens que pour les GRANDS doubleLe codage d'un nombre est inspiré de la notation scientifique comme -1.5 × 10+3. Chaque nombre est décomposé en trois parties : signe, exposant et mantisse. Le signe est codé par le bit de poids fort. Ensuite un certain nombre de bits sont consacrés à l'exposant et le reste à la mantisse. La table ci-dessous donne le nombre de bits de chacun des éléments dans les différents formats.
Encodage Signe s Exposant e Mantisse m Valeur d'un nombre
Simple précision 32 bits 1 bit 8 bits 1≤e≤254 23 bits (-1)s× 1.m ×2e-127
Double précision 64 bits 1 bit 11 bits 1≤e≤2046 52 bits (-1)s× 1.m ×2e-1023
Précision étendue 80 bits 1 bit 15 bits 1≤e≤32766 64 bits (-1)s× 1.m ×2e-16383
Dans la table ci-dessus, la formule 1.m doit être interprétée de la façon suivante. Si les bits de la mantisse sont b1b2…bn, on a
1.m = 1 + b1/2 + b2/22 + b3/23 + … + bn/2n
Soit l'exemple en simple précision 101111110101100…0 (0xbf580000 en hexadécimal). Il se décompose en le signe s = 1, l'exposant e = 01111110 = (126)10 et la mantisse m = 1010100…0. La valeur de 1.m = 1+1/2+1/8+1/16 = 1,6875. La valeur du nombre est donc -1,6875 × 2-1 = -0,84375.
Je fais l'hypothèse simplificatrice que le signe est >0 donc le bit de signe = 0
je fais aussi l'hypothèse que l'exposant e est positif et >52
La première étape consiste à multiplier la mantisse par 2^52 et à répartir le résultat sur LSW et MSW, il suffir pour cela de glisser les 32 bits de poids faible sur LSW et les 20 bits de poids fort sur MSW.
Il reste à multiplier le resultat par 2^(e-52)
On va le faire par itération de la manière suivante.
Il suffit de savoir faire une multiplication par 2 sur le couple LSW MSW
Voici le processus:
1) regarder si LSW <= 2^31 (consultation du bit de poids fort)
Si c'est le cas tomber le bit de poids fort ce qui correspond à une soustraction de 2^31
2) multiplier par 2 LSW et MSW (simples décalages)
3) si on a tombé le bit de poids fort en 1) alors ajouter 1 unité à MSW
Ce qu'on trouve est plus important que ce qu'on cherche.
Maths de base pour les nuls (et les autres...)
Hja,
Houla, bien compliqué tout ça.
Avec ces valeurs, on doit donc obtenir 2*2^32 + 10 = 8589934602
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 struct int64 { unsigned int LSW; unsigned int MSW; }; int main() { int64 m64; m64.MSW = 2; m64.LSW = 10; double d = (m64.MSW * 4294967296ULL) + m64.LSW; printf("d = %f\n",d); return EXIT_SUCCESS; }
et résultat = 8589934602.000000![]()
C'est la conversion en sens inverse qui est demandée.
Ce qu'on trouve est plus important que ce qu'on cherche.
Maths de base pour les nuls (et les autres...)
Jie,
Houla, lu trop vite .Envoyé par Zavonen
![]()
Sio,
Avec un petit coup d'assembleur, c'est plus court.
En syntaxe AT&T (pour GCC)
Résultat :
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31 struct myint64 { unsigned int LSW; unsigned int MSW; }; void doubleToMy64(double *d, myint64 *r) { __asm__ ( "movl %0,%%eax\t\n" "fldl (%%eax)\t\n" "movl %1,%%eax\t\n" "fistpq (%%eax)" : : "m" (d), "m" (r) : "%eax" ); } int main() { myint64 m64; // double d = 8589934602.0L; // 2^32*2 + 10 // double d = 536870912987.0L; // 2^32*125 + 987 double d = 387086051008789.0L; // 2^32*90125 + 2123456789 doubleToMy64(&d,&m64); printf("d = %f, MSW = %u, LSW = %u\n",d,m64.MSW,m64.LSW); return EXIT_SUCCESS; }
d = 387086051008789.000000, MSW = 90125, LSW = 2123456789
En syntaxe Intel (pour VC++), la fonction devient (pas testé, car je n'ai pas VC++ !)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9 void doubleToMy64(double *d, myint64 *r) { __asm { mov eax, d fld qword ptr [eax] mov eax, r fistp qword ptr [eax] } }
merci tous pour vos reponses. je vais choisir une de ces propositions
comment faire dans les autres cas nombre <2^52 et signe <0.
sachant que:
typedef struct{
unsigned int LSW;
signed int MSW;
}int64;
(j'ai changé MSW en signed)
je n'ai pas besoin du code assembleur mais merci quand meme
D'abord, on ne s'occupe pas du signe (on l'ignore purement et simplement)comment faire dans les autres cas nombre <2^52 et signe <0.
Si par exemple l'exposant est 30. On commence par la puissance 52 pour se débarasser de la mantisse, comme précédemment, mais alors on est allé trop loin, il faut donc revenir en arrière c'est à dire multiplier par 2^-22, ou encore diviser par 2^22.
Pour régler le problème du signe on adopte la convention standard pour n'importe quel type d'entiers.
Par exemple les petits entiers sur 8 octets:
Le négatif -n (n<=128) est représenté par le positif 256-n
Pour des entiers sur 32 bits
le négatif -n (n<= 2^16) est représenté par le unsigned 2^32-n
Donc pour les int64
ne négatif -n (n<=2^32) est représenté par le unsigned 2^64 - n
Il suffit donc de faire une soustraction en binaire suivie d'une addition d'une unité:
(2^63 - n)+1
sachant que le 2^63 ne comporte que des 1, donc c'est en fait le +1 qui pose problème à cause des reports, le 2^63 -n étant lui évident.
La solution de Droggo est bien sûr plus élégante. MAIS ...
Il faut pour la comprendre maîtriser l'assembleur des proc. Intel
Comme toutes les solutions faisant appel au langage machine elle n'est pas portable.
Ce qu'on trouve est plus important que ce qu'on cherche.
Maths de base pour les nuls (et les autres...)
Voici le code complet traitant tous les cas (positifs ou négatifs, < ou > à 2^52.
Il y a quelques changements par rapport au précédent.
La mantisse a été réécrite dans l'ordre des poids croissants.
J'avais été un peu simpliste ou optimiste pour la division par deux des int64, j'ai rectifié le tir.
J'ai inclus de nombreuses fonctions de visualisation au cas où il y ait encore un débogage à faire, mais ette fois je suis presque sûr que le code est OK.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249 #include <stdio.h> #include <stdlib.h> // les entiers sur 64 bits typedef struct { unsigned int LSW; unsigned int MSW; } int64; // voir un int64 en binaire void showbinary (int64 * pBI) { int i,h,k; printf("Visualisation de l'int64- poids faibles-->poids forts\n"); for (i=0,h=1;i<32;i++,h*=2) { k=pBI->LSW&h; printf("%c",k?'1':'0'); } printf(":"); for (i=0,h=1;i<32;i++,h*=2) { k=pBI->MSW&h; printf("%c",k?'1':'0'); } printf("\n"); } // initialiser à 0 un int64 void init64 (int64 *pBI) { pBI->LSW=pBI->MSW=0; } // multiplier par 2 un int64 void multiplyx2( int64* pLI) { int flag ; flag = pLI->LSW>=2147483648? 1:0; if (flag) pLI->LSW -=2147483648; pLI->LSW *=2; pLI->MSW*=2 ; if (flag) pLI->MSW++; } // diviser par 2 un int64 void dividex2(int64 * pLI) { int flag; flag=pLI->MSW%2?1:0; pLI->LSW/=2; pLI->MSW/=2; if (flag) pLI->LSW+=2147483648; } // inverser tous les bits void compl1 (int64 * pLI) { int i; unsigned int h; unsigned int XL=0, XM=0 ; for (i=0,h=1;i<32;i++,h*=2) if ((pLI->LSW&h)==0)XL+=h; pLI->LSW=XL; for (i=0,h=1;i<32;i++,h*=2) if ((pLI->MSW&h)==0)XM+=h; pLI->MSW=XM; } // Ajouter une unité void add1(int64 *pLI) { int i, k; int report=1; unsigned int h, XL=0, XM=0 ; for (i=0,h=1;i<32;i++,h*=2) { k=report+((pLI->LSW)&h)?1:0; XL+=(k%2)*h; report=(k>=2)?1:0; } for (i=0,h=1;i<32;i++,h*=2) { k=report+((pLI->MSW)&h)?1:0; XM+=(k%2)*h; report=(k>=2)?1:0; } pLI->LSW=XL; pLI->MSW=XM; } // structure pour récupérer la décomposition d'un double typedef struct { int n0; int n1; unsigned char s[64]; int signe; int exposant; char mantisse[52]; } doublec; // afficher la structure d'un double (pour vérif) void show(doublec * pDC) { int i; for (i=0;i<64;i++) printf("%d",pDC->s[i]); printf("\n"); printf("signe: %d\n",pDC->signe); printf("exposant: %d\n",pDC->exposant); printf("mantisse: "); // dans l'ordre habituel for (i=0;i<52;i++) printf("%d",pDC->mantisse[51-i]); printf("\n"); } // récupérer la structure d'un double void load (doublec * pDC , double * x) { int i, k,h,j; int * pi = (int *) x; pDC->n0= *pi; pDC->n1=*(pi+1); for (i=0, k=1;i<32;i++) { pDC->s[i]=(pDC->n0)&k?1:0; k*=2; } for (i=32, k=1;i<64;i++) { pDC->s[i]=(pDC->n1)&k?1:0; k*=2; } pDC->signe= (int)pDC->s[63]; pDC->exposant=0; for (i=0,h=62, j=1024; i<11; i++,h--) { pDC->exposant+=j*pDC->s[h]; j/=2; } pDC->exposant-=1023; for (i=0; i<52; i++) pDC->mantisse[i]=pDC->s[i]; } // transformation d'un doublec en int64 void transform (doublec * pDC ,int64* pLI) { int i,h,k; init64(pLI); // mise à 0 de tous les bits // glisser les bits de la mantisse for (i=0,h=1;i<32;i++,h*=2) pLI->LSW+=pDC->mantisse[i]*h; for (i=32,h=1;i<52;i++,h*=2) pLI->MSW+=pDC->mantisse[i]*h; pLI->MSW+=1048576; // pour le 1. devant la mantisse if (pDC->exposant>52) { k=pDC->exposant -52; for (i=1;i<=k;i++) multiplyx2(pLI); } else if (pDC->exposant<52) { k= 52-pDC->exposant; for (i=1;i<=k;i++) dividex2(pLI); } // sinon exposant=52 rien à faire // traitement spécial des négatifs if (pDC->signe) { compl1(pLI); add1(pLI); } } // les puissances de 2 d'un double // juste pour les essais double powerof2 ( int n) { int i; double result=1.0; for (i=1;i<=n;i++) result *=2; return result; } int main() { doublec DC1 ; int64 LI; double x1,x2 ; printf("Essai avec les puissances de 2 d'abord\n"); printf ("test des positifs\n"); x1=powerof2(10); printf("%lf\n",x1); load( &DC1,&x1); show(&DC1); transform (&DC1,&LI); showbinary(&LI); printf("------------------\n"); x1=powerof2(52); printf("%lf\n",x1); load( &DC1,&x1); show(&DC1); transform (&DC1,&LI); showbinary(&LI); printf("------------------\n"); x1=powerof2(62); printf("%lf\n",x1); load( &DC1,&x1); show(&DC1); transform (&DC1,&LI); showbinary(&LI); printf("------------------\n"); printf("Nombres quelconques maintenant\n"); printf("Positifs d'abord\n"); x1=546789548795478.0; printf("%lf\n",x1); load( &DC1,&x1); show(&DC1); transform (&DC1,&LI); x2=powerof2(32)*LI.MSW+LI.LSW ; printf("%lf\n",x2); printf("------------------\n"); x1=1.56789548795478*powerof2(53); printf("%lf\n",x1); load( &DC1,&x1); show(&DC1); transform (&DC1,&LI); x2=powerof2(32)*LI.MSW+LI.LSW ; printf("%lf\n",x2); printf("------------------\n"); printf("Visualisation bit a bit d'un negatif\n"); printf("le reaffichage brut ne marche évidemment pas\n"); x1=-546789548795478.0; printf("%lf\n",x1); load( &DC1,&x1); show(&DC1); transform (&DC1,&LI); showbinary(&LI); return 0; }
Ce qu'on trouve est plus important que ce qu'on cherche.
Maths de base pour les nuls (et les autres...)
exactly what i need.merci
You're welcome !
Ce qu'on trouve est plus important que ce qu'on cherche.
Maths de base pour les nuls (et les autres...)
Ca me semble bien compliqué. Si j'ai bien compris l'énoncé, quelque chose comme ce qui suit devrait faire l'affaire (mais ce n'est pas testé)
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30 typedef struct { unsigned int lsw; unsigned int msw; } int64; int dtoint64(double x, int64* res) { int sign = 1; if (x < -9223372036854775808.0 || x > 9223372036854775807.0) { /* -2**63 2**63-1 */ return 0; } if (x < 0) { sign = -1; x = -x; } res->msw = (unsigned int) (x/4294967296.0); x -= res->msw * 4294967296.0; res->lsw = (unsigned int)x; if (sign == -1) { res->lsw = -res->lsw; if (res->lsw == 0) { res->msw = -res->msw; } else { res->msw = ~res->msw; } } return 1; }
En fait, si on excepte les fonctions de manipulations des entiers 64 bits, mes 'load' et 'transform' font tout le boulot. Le reste ce sont des fonctions d'entrée-sortie et de tests pour vérifier. Cela dit votre code a l'air sympa et il y a de bonnes raisons pour que ça marche, mais à mon avis pas pour les négatifs, enfin disons plutôt que je ne comprends pas cette partie du code.Envoyé par Jean-Marc.Bourguet
Mais c'est bien d'avoir pensé aux tests d'entrée.
Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7 if (sign == -1) { res->lsw = -res->lsw; if (res->lsw == 0) { res->msw = -res->msw; } else { res->msw = ~res->msw; }
Ce qu'on trouve est plus important que ce qu'on cherche.
Maths de base pour les nuls (et les autres...)
Ca fait du complement a 2 sur le lsw. Ensuite s'il est a 0, c'est qu'il y a un report donc il faut faire du complement a deux sur le msw, sinon, il n'y a pas de report et il faut faire du complement a 1.Envoyé par Zavonen
Pourriez vous donner un petit exemple en binaire ?Envoyé par Jean-Marc.Bourguet
Ce qu'on trouve est plus important que ce qu'on cherche.
Maths de base pour les nuls (et les autres...)
Partager