Je travaille avec un ancien module C qui était à l'origine exécuté sur l'architecture Power PC et compilé avec GNU 3.0.6
Je le porte pour qu'il s'exécute dans un projet VS2012 sur du matériel Intel.
Le module crée un modèle 3D d'un nuage d'orage basé sur un fichier bitmap et d'autres IO.
Je continue de voir des amplitudes signées (coordres x et y, différentiels) multipliées par 1024 * 1024 et je ne vois pas pourquoi. Ces quantités sont stockées dans des pièces signées.
Chaque fois qu'une de ces quantités est sortie du module, elle est décalée vers la droite de 20, ce qui annule apparemment la multiplication de 1024 * 1024.
Est-ce que quelqu'un sait ce qui se passe ici?
Merci
EDIT: Un cas est où les différentiels x et y d'une ligne incorporée dans une grille 256x256 sont utilisés pour itérer à travers elle. Ici, nous avons un rayon défini par un certain angle thêta et un point de départ donné (x, y) dans la grille. Les coordonnées du point de départ sont également des produits de 1024 * 1024
#define KINT 1024*1024
float x_disp, y_disp;
int x, y, dx, dy;
//initializing code
x = (int)(KINT*x_disp);
y = (int)(KINT*y_disp);
//as theta ranges across [-pi/2, pi/2], itheta ranges over [0, 720]
dx = (int)(KINT*sin_table[itheta]);
dy = (int)(KINT*cos_table[itheta]);
À bien y penser, je ne sais pas pourquoi il n'y a pas de (-1) devant sin_table (itheta).
La ligne intégrée à l'intérieur de la grille 256x256 est parcourue en calculant les coordonnées comme suit:
for (int i = 0; i < 256; i++)
{
int posx = (x>>20);
int posy = (y>>20);
if((posx|posy)&0xffffff00 == 0)
{
char val = grid[x][y];
//do something with val
}
x += dx;
y += dy;
}
Il est difficile d'être (du tout) certain sans regarder le code, mais cela ressemble un peu à l'implémentation d'une arithmétique à virgule fixe, avec 12 bits avant la virgule décimale et 20 bits après.
Lorsque/si vous multipliez deux de ces éléments ensemble, vous devez effectuer un décalage vers la droite pour ramener le résultat à la bonne représentation. Autrement dit, chaque entrée est un total de 32 bits, donc lorsque vous les multipliez, le résultat sera évidemment de 64 bits, et vous voulez en garder 32 bits. Mais vous devez garder le correct 32 bits. Puisque vous avez commencé avec 20 bits de fraction et 12 bits de mantisse dans les entrées, vous obtiendrez 40 bits de fraction et 24 bits de mantisse dans le résultat. Donc, vous décalez à droite de 20, puis conservez les 32 derniers bits pour obtenir un résultat au format 12:20 (et les bits les plus significatifs du résultat devraient être zéro, ou vous venez de déborder de ce que le résultat peut représenter).
Beaucoup de gens trouvent plus facile de penser en termes décimaux. Considérons donc une représentation décimale où nous pouvons gérer (disons) -999,999 à +999,999, mais les stocker et les manipuler sous forme d'entiers.
Donc, pour stocker un nombre, nous commençons par le multiplier par 1000, donc si nous avions (disons) 123,456, nous obtenons 123'456. Maintenant, si nous ajoutons des chiffres comme celui-ci, nous n'avons rien à faire de spécial. Donc, si nous ajoutons 123'456 à 234'567, nous obtenons 358'023, ce qui représente 358.023 (ce qui est la bonne réponse).
Mais si nous multiplions deux de ces nombres ensemble, notre résultat n'est pas ce que nous voudrions. Par exemple, considérons 2,2 x 2,3. Dans notre format, cela devient 2'200 x 2'300. Lorsque nous les multiplions, nous obtenons 5'060'000, ce qui se transforme en 5'060.000. Notre résultat est trop élevé d'un facteur de 1'000 (le même facteur que nous appliquons à une valeur pour l'intégrer dans notre notation). Donc, pour obtenir la bonne réponse après une multiplication, nous devons la déplacer vers la droite de 3 décimales, donc nous obtenons 5'060, qui se transforme en 5,06.
On dirait que le code que vous regardez fait à peu près la même chose, mais au lieu d'utiliser un multiplicateur qui est Nice et même en décimal, il utilise un multiplicateur qui est Nice et même en binaire. Cela permet à un ordinateur binaire de "fixer" le résultat après une multiplication, simplement en effectuant un décalage à droite (alors que la division par une puissance de 10 serait relativement lente).
Regarder plus loin: pourquoi feraient-ils cela? Je peux voir deux possibilités évidentes. Le premier et le plus évident est la vitesse. Surtout sur de nombreux processeurs plus anciens, il était assez courant que les mathématiques entières soient sensiblement plus rapides que les mathématiques à virgule flottante.
La deuxième possibilité serait la prévisibilité. Avec virgule fixe, vous feriez fondamentalement des mathématiques entières. Les mathématiques entières sont assez simples et compréhensibles. En revanche, si vous utilisez des mathématiques à virgule flottante à la place, vous entrez dans une zone que beaucoup de gens trouvent quelque peu mystérieuse. Quand les gens commencent, ils ont tendance à penser que la virgule flottante est comme un vrai nombre en mathématiques - mais ils apprennent rapidement que les deux ne sont pas vraiment les mêmes, et les différences peuvent être difficiles à comprendre et souvent difficiles pour un débutant dans la région à prévoir. En utilisant des virgules fixes (entiers mis à l'échelle), ils peuvent simplement éviter ces choses imprévisibles et difficiles à comprendre.
Le fait que ce décalage se fasse dans un sens puis à l'envers par la suite suggère que c'est peut-être un hack pour fournir une bande passante supplémentaire dans ces valeurs. Autrement dit, les 20 bits de poids faible sont utilisés pour contenir d'autres informations. C'est ma meilleure supposition.
Une autre option est que c'est une façon loufoque d'effacer les bits de poids fort. Cela semble peu probable, mais je serais préoccupé par le fait que cela se produise, en particulier lors du passage d'une architecture à une autre .
Vous allez tracer le code pour être sûr, mais vous devez absolument vérifier que la taille int sur la nouvelle architecture est au moins aussi grande que l'ancienne.
Une autre raison du changement -
Certains systèmes de données alignent les données à gauche dans un champ au lieu des classiques alignés à droite. Un convertisseur analogique-numérique 10 bits, par exemple, offre généralement des options d'alignement à gauche ou à droite des lectures 10 bits dans un champ de 16 bits. Cette capacité existe dans la puce Atmega32u4 utilisée dans certains périphériques Arduino - voir fiche technique page 313, bit ADLAR dans le registre ADMUX.
Les données alignées à gauche sont essentiellement les mêmes que le système décrit par Jerry Coffin ci-dessus si vous avez toujours la partie fractionnaire à zéro.
Un grand élan derrière ces types de systèmes à virgule fixe est lorsqu'un processeur n'a pas d'unité à virgule flottante et que vous savez à l'avance de combien de chiffres de précision vous avez besoin après la virgule décimale. Puisque 2 ^ 10 est à peu près 10 ^ 3, tous les dix bits décalés vers la gauche déplacent approximativement la virgule décimale vers la droite de trois positions. Vous pouvez effectuer des calculs entiers sur des nombres de la forme X.yyy et lorsque vous affichez le résultat, vous insérez simplement le point décimal à l'emplacement approprié après la conversion en chiffres décimaux.