Je lis un livre en C, parlant de plages de virgule flottante, l'auteur a donné le tableau:
Type Smallest Positive Value Largest value Precision
==== ======================= ============= =========
float 1.17549 x 10^-38 3.40282 x 10^38 6 digits
double 2.22507 x 10^-308 1.79769 x 10^308 15 digits
Je ne sais pas d'où viennent les nombres dans les colonnes Plus petite valeur positive et Plus grande valeur.
Un nombre à virgule flottante de 32 bits a 23 + 1 bits de mantisse et un exposant de 8 bits (cependant -126 à 127 est utilisé), donc le plus grand nombre que vous pouvez représenter est:
(1 + 1 / 2 + ... 1 / (2 ^ 23)) * (2 ^ 127) =
(2 ^ 23 + 2 ^ 23 + .... 1) * (2 ^ (127 - 23)) =
(2 ^ 24 - 1) * (2 ^ 104) ~= 3.4e38
Les valeurs pour le type de données float proviennent du fait d'avoir 32 bits au total pour représenter le nombre qui est alloué comme ceci:
1 bit: bit de signe
8 bits: exposant p
23 bits: mantisse
L'exposant est stocké sous la forme p + BIAS
où le BIAS est 127, la mantisse a 23 bits et un 24ème bit caché qui est supposé 1. Ce bit caché est le bit le plus significatif (MSB) de la mantisse et l'exposant doit être choisi pour qu'il soit 1.
Cela signifie que le plus petit nombre que vous pouvez représenter est 01000000000000000000000000000000
lequel est 1x2^-126 = 1.17549435E-38
.
La plus grande valeur est 011111111111111111111111111111111
, la mantisse est 2 * (1 - 1/65536) et l'exposant est 127 ce qui donne (1 - 1 / 65536) * 2 ^ 128 = 3.40277175E38
.
Les mêmes principes s'appliquent à la double précision sauf que les bits sont:
1 bit: bit de signe
11 bits: bits d'exposant
52 bits: bits de mantisse
BIAS: 1023
Donc, techniquement, les limites proviennent de la norme IEEE-754 pour représenter les nombres à virgule flottante et ce qui précède est de savoir comment ces limites se produisent
Comme dasblinkenlight a déjà répondu, les nombres proviennent de la façon dont les nombres à virgule flottante sont représentés dans IEEE-754, et Andreas a une belle répartition des mathématiques.
Cependant - veillez à ce que la précision des nombres à virgule flottante ne soit pas exactement 6 ou 15 chiffres décimaux significatifs comme le suggère le tableau, car la précision des nombres IEEE-754 dépend du nombre de chiffres binaires significatifs.
float
a 24 chiffres binaires significatifs - ce qui, selon le nombre représenté, se traduit par 6 à 8 chiffres décimaux de précision.
double
a 53 chiffres binaires significatifs, soit environ 15 chiffres décimaux.
ne autre réponse à moi a plus d'explications si vous êtes intéressé.
Infini, NaN et sous-normales
Ce sont des mises en garde importantes qu'aucune autre réponse n'a mentionnées jusqu'à présent.
Lisez d'abord cette introduction à l'IEEE 754 et aux nombres subnormaux: Qu'est-ce qu'un nombre à virgule flottante subnormal?
Ensuite, pour les flottants simple précision (32 bits):
IEEE 754 dit que si l'exposant est tout (0xFF == 255
), il représente alors NaN ou Infinity.
C'est pourquoi le plus grand nombre non infini a un exposant 0xFE == 254
et pas 0xFF
.
Puis avec le biais, ça devient:
254 - 127 == 127
FLT_MIN
est le plus petit nombre normal. Mais il y en a de plus petits sous-normaux! Ceux qui prennent le -127
emplacement d'exposant.
Toutes les assertions du programme suivant passent sur Ubuntu 18.04 AMD64:
#include <assert.h>
#include <float.h>
#include <inttypes.h>
#include <math.h>
#include <stdlib.h>
#include <stdio.h>
float float_from_bytes(
uint32_t sign,
uint32_t exponent,
uint32_t fraction
) {
uint32_t bytes;
bytes = 0;
bytes |= sign;
bytes <<= 8;
bytes |= exponent;
bytes <<= 23;
bytes |= fraction;
return *(float*)&bytes;
}
int main(void) {
/* All 1 exponent and non-0 fraction means NaN.
* There are of course many possible representations,
* and some have special semantics such as signalling vs not.
*/
assert(isnan(float_from_bytes(0, 0xFF, 1)));
assert(isnan(NAN));
printf("nan = %e\n", NAN);
/* All 1 exponent and 0 fraction means infinity. */
assert(INFINITY == float_from_bytes(0, 0xFF, 0));
assert(isinf(INFINITY));
printf("infinity = %e\n", INFINITY);
/* ANSI C defines FLT_MAX as the largest non-infinite number. */
assert(FLT_MAX == 0x1.FFFFFEp127f);
/* Not 0xFF because that is infinite. */
assert(FLT_MAX == float_from_bytes(0, 0xFE, 0x7FFFFF));
assert(!isinf(FLT_MAX));
assert(FLT_MAX < INFINITY);
printf("largest non infinite = %e\n", FLT_MAX);
/* ANSI C defines FLT_MIN as the smallest non-subnormal number. */
assert(FLT_MIN == 0x1.0p-126f);
assert(FLT_MIN == float_from_bytes(0, 1, 0));
assert(isnormal(FLT_MIN));
printf("smallest normal = %e\n", FLT_MIN);
/* The smallest non-zero subnormal number. */
float smallest_subnormal = float_from_bytes(0, 0, 1);
assert(smallest_subnormal == 0x0.000002p-126f);
assert(0.0f < smallest_subnormal);
assert(!isnormal(smallest_subnormal));
printf("smallest subnormal = %e\n", smallest_subnormal);
return EXIT_SUCCESS;
}
Compilez et exécutez avec:
gcc -ggdb3 -O0 -std=c11 -Wall -Wextra -Wpedantic -Werror -o subnormal.out subnormal.c
./subnormal.out
Sortie:
nan = nan
infinity = inf
largest non infinite = 3.402823e+38
smallest normal = 1.175494e-38
smallest subnormal = 1.401298e-45
C'est une conséquence de la taille de la partie exposant du type, comme dans IEEE 754 par exemple. Vous pouvez examiner les tailles avec FLT_MAX, FLT_MIN, DBL_MAX, DBL_MIN dans float.h.