Pascal réponse est OK - mais manque de détails qui implique que certains utilisateurs n'ont pas l'obtenir ;-) . Si vous êtes intéressé par la façon dont il regarde au niveau inférieur (en supposant que le coprocesseur et pas logiciel gère les opérations à virgule flottante) - lisez la suite.
En 32 bits float (IEEE 754) - vous pouvez stocker tous les entiers de l'intérieur [-224...224] gamme. Entiers en dehors de la plage peuvent également avoir une représentation exacte en tant que float mais tous n'ont pas. Le problème est que vous ne pouvez avoir qu'24 bits significatifs pour jouer avec dans le flotteur.
Voici comment la conversion de int->float ressemble généralement à un faible niveau:
fild dword ptr[your int]
fstp dword ptr[your float]
C'est juste séquence de 2 instructions de coprocesseur. Premier chargement de 32 bits int sur comprocessor la pile et le convertit en 80 bits float.
Intel® 64 et IA-32 Architectures Manuel de développement Logiciel
(PROGRAMMATION AVEC LA FPU X87):
Quand à virgule flottante, entier ou emballés BCD entier
les valeurs sont chargés à partir de la mémoire dans toute la FPU x87 registres de données, les valeurs sont
automatiquement converti en double étendu à virgule flottante au format (si ils
ne sont pas déjà dans ce format).
Depuis les registres FPU sont 80bit large flotte - il n'y a pas de problème avec l' fild
ici qu'en 32bit int parfaitement dans 64bit significande de format à virgule flottante.
So far So good.
La deuxième partie - fstp
est un peu délicat et peut-être surprenant. Il est censé stocker 80bit à virgule flottante de 32 bits float. Bien qu'il est tout au sujet des valeurs entières (dans la question) coprocesseur peut effectuer réellement 'arrondi'. Ke? Comment faites-vous faire un tour entier de la valeur même si elle est stockée en format à virgule flottante? ;-).
Je vais vous expliquer en peu de temps - nous allons tout d'abord voir ce que les modes d'arrondi x87 offre (ils sont IEE 754 modes d'arrondi' incarnation). Fpu X87 dispose de 4 modes d'arrondi contrôlé par les bits #10 et #11 de la fpu du mot de contrôle:
- 00 - le plus proche même résultat Arrondi est le plus proche de l'infiniment résultat précis. Si deux
les valeurs sont proches de la même façon, le résultat est le même valeur (qui est, le
celui avec le moins de bits significatifs de zéro). Par défaut
- 01 - vers -Inf
- 10 - vers +inf
- 11 - vers 0 (c'est à dire. tronquer)
Vous pouvez jouer avec les modes d'arrondi à l'aide de ce code simple (bien qu'il puisse être fait différemment - faible niveau ici):
enum ROUNDING_MODE
{
RM_TO_NEAREST = 0x00,
RM_TOWARD_MINF = 0x01,
RM_TOWARD_PINF = 0x02,
RM_TOWARD_ZERO = 0x03 // TRUNCATE
};
void set_round_mode(enum ROUNDING_MODE rm)
{
short csw;
short tmp = rm;
_asm
{
push ax
fstcw [csw]
mov ax, [csw]
and ax, ~(3<<10)
shl [tmp], 10
or ax, tmp
mov [csw], ax
fldcw [csw]
pop ax
}
}
Ok sympa, mais encore, comment est-ce lié à des valeurs entières? De la Patience ... pour comprendre pourquoi vous pourriez avoir besoin d'arrondi les modes impliqués dans int float conversion de vérifier le plus évident de la conversion de int en float - la troncature (pas par défaut) - qui peut ressembler à ceci:
- enregistrement signe
- annuler votre int si elle est inférieure à zéro
- trouver une position de gauche 1
- maj int vers la droite/gauche, de sorte que 1 se trouve au-dessus est placé sur le bit #23
- le record du nombre de quarts de travail pendant le processus de sorte que vous pouvez calculer l'exposant
Et le code de simuler ce comportement peut ressembler à ceci:
float int2float(int value)
{
// handles all values from [-2^24...2^24]
// outside this range only some integers may be represented exactly
// this method will use truncation 'rounding mode' during conversion
// we can safely reinterpret it as 0.0
if (value == 0) return 0.0;
if (value == (1U<<31)) // ie -2^31
{
// -(-2^31) = -2^31 so we'll not be able to handle it below - use const
value = 0xCF000000;
return *((float*)&value);
}
int sign = 0;
// handle negative values
if (value < 0)
{
sign = 1U << 31;
value = -value;
}
// although right shift of signed is undefined - all compilers (that I know) do
// arithmetic shift (copies sign into MSB) is what I prefer here
// hence using unsigned abs_value_copy for shift
unsigned int abs_value_copy = value;
// find leading one
int bit_num = 31;
int shift_count = 0;
for(; bit_num > 0; bit_num--)
{
if (abs_value_copy & (1U<<bit_num))
{
if (bit_num >= 23)
{
// need to shift right
shift_count = bit_num - 23;
abs_value_copy >>= shift_count;
}
else
{
// need to shift left
shift_count = 23 - bit_num;
abs_value_copy <<= shift_count;
}
break;
}
}
// exponent is biased by 127
int exp = bit_num + 127;
// clear leading 1 (bit #23) (it will implicitly be there but not stored)
int coeff = abs_value_copy & ~(1<<23);
// move exp to the right place
exp <<= 23;
int ret = sign | exp | coeff;
return *((float*)&ret);
}
Maintenant exemple troncature mode convertit 2147483583
de 2147483520
.
2147483583 = 01111111_11111111_11111111_10111111
Au cours de int->float conversion vous devez maj gauche 1 bit n ° 23. Aujourd'hui à la tête 1 est le bit n ° 30. Afin de le placer dans le bit n ° 23, vous devez effectuer la maj de droite en 7 positions. Pendant que vous perdez (ils ne rentre pas dans le format flottant 32 bits) 7 lsb bits à partir de la droite (vous tronquez/chop). Ils ont été:
01111111 = 63
Et 63 est ce numéro original perdu:
2147483583 -> 2147483520 + 63
La troncation est facile mais pas forcément ce que vous voulez et/ou qui est le meilleur pour tous les cas. Considérons l'exemple ci-dessous:
67108871 = 00000100_00000000_00000000_00000111
Au-dessus de la valeur ne peut pas être exactement représentée par flotteur, mais vérifiez ce que la troncature fait pour elle. Comme précédemment, nous devons changer de gauche 1 bit n ° 23. Cela nécessite de la valeur à être décalé à droite exactement 3 positions de perdre 3 bits LSB (à compter de maintenant, je vais écrire les nombres différemment montrant où implicite 24 bits float est et sera support explicite 23bits de significande):
00000001.[0000000_00000000_00000000] 111 * 2^26 (3 bits shifted out)
La troncature des côtelettes de 3 de fuite bits nous laissant avec 67108864
(67108864+7(3 haché bits)) = 67108871 (rappelez-vous bien que nous passons nous de compenser avec l'exposant de manipulation - omis ici).
Est-ce suffisant? Hey 67108872
est parfaitement représentable par 32bit float et devrait être beaucoup mieux que d' 67108864
droit? CORRECT et c'est là que vous voudrez parler d'arrondi lors de la conversion de int de 32 bits float.
Maintenant, nous allons voir comment défaut "d'arrondi au plus près, même en mode" fonctionne et quelles sont ses implications dans des OP cas. Considérons le même exemple une fois de plus.
67108871 = 00000100_00000000_00000000_00000111
Comme nous le savons, nous avons besoin de 3 droit des variations à la place la plus à gauche 1 dans le bit n ° 23:
00000000_1.[0000000_00000000_00000000] 111 * 2^26 (3 bits shifted out)
La procédure de "l'arrondi au plus proche même de" consiste à trouver 2 numéros que support de la valeur d'entrée 67108871
par le bas et au-dessus, aussi près que possible. Gardez à l'esprit que nous avons toujours fonctionner à l'intérieur de la FPU sur 80bits tellement bien que je montre quelques bits décalés qu'ils sont encore dans la FPU, reg, mais seront supprimées lors de l'opération d'arrondi lors du stockage de la valeur de sortie.
00000000_1.[0000000_00000000_00000000] 111 * 2^26 (3 bits shifted out)
2 valeurs qui sont étroitement support 00000000_1.[0000000_00000000_00000000] 111 * 2^26
sont:
de haut en bas:
00000000_1.[0000000_00000000_00000000] 111 * 2^26
+1
= 00000000_1.[0000000_00000000_00000001] * 2^26 = 67108872
et à partir de ci-dessous:
00000000_1.[0000000_00000000_00000000] * 2^26 = 67108864
Évidemment 67108872
est beaucoup plus proche d' 67108871
que 67108864
ainsi la conversion de 32 bits int valeur 67108871
donne 67108872
(en arrondi au plus proche, même mode).
Maintenant, OP numéros (toujours arrondi au plus proche même):
2147483583 = 01111111_11111111_11111111_10111111
= 00000000_1.[1111111_11111111_11111111] 0111111 * 2^30
support de valeurs:
haut:
00000000_1.[1111111_111111111_11111111] 0111111 * 2^30
+1
= 00000000_10.[0000000_00000000_00000000] * 2^30
= 00000000_1.[0000000_00000000_00000000] * 2^31 = 2147483648
en bas:
00000000_1.[1111111_111111111_11111111] * 2^30 = 2147483520
Gardez à l'esprit que même mot dans 'l'arrondi au plus proche, même à des" questions uniquement lorsque la valeur d'entrée est à mi-chemin entre le support de valeurs. Alors seulement, le mot même les questions et "décide" qui support la valeur doit être sélectionnée. Dans le cas ci-dessus , même n'a pas d'importance et nous devons simplement choisir la plus proche de la valeur, qui est - 2147483520
Dernière OP cas, montre que le problème où le même mot compte. :
2147483584 = 01111111_11111111_11111111_11000000
= 00000000_1.[1111111_11111111_11111111] 1000000 * 2^30
support de valeurs sont les mêmes que précédemment:
haut: 00000000_1.[0000000_00000000_00000000] * 2^31 = 2147483648
en bas: 00000000_1.[1111111_111111111_11111111] * 2^30 = 2147483520
Il n'y a pas plus proche de la valeur maintenant (2147483648-2147483584=64=2147483584-2147483520) donc, nous devons compter sur la même et sélectionnez haut (même) valeur 2147483648
.
Et ici, OP problème, c'est que Pascal a brièvement décrit. FPU ne fonctionne que sur des valeurs signées et 2147483648
ne peut pas être stockée comme signé int comme sa valeur max est de 2 147 483 647 par conséquent, des questions.
Preuve Simple (sans documentation guillemets) que FPU ne fonctionne que sur des valeurs signées ie. traite chaque valeur est signé par le débogage ceci:
unsigned int test = (1u << 31);
_asm
{
fild [test]
}
Bien qu'il ressemble à valeur de test doivent être traités comme des non signé, il sera chargé comme -231 comme il n'y a pas d'instructions pour le chargement des entiers signés et non signés valeurs dans la FPU. De même, vous n'allez pas trouver des instructions qui vous permettra de stocker de valeur non signée de la FPU, mem. Tout est une séquence de bits traités comme signé indépendamment de la façon dont vous pourriez avoir déclaré dans votre programme.
Été long mais j'espère que quelqu'un va apprendre quelque chose hors de lui.