web-dev-qa-db-fra.com

Plages de type de données à virgule flottante en C?

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.

33
ipkiss

Ces nombres proviennent de la norme IEEE-754 , qui définit la représentation standard des nombres à virgule flottante. Article Wikipedia sur le lien explique comment arriver à ces plages en connaissant le nombre de bits utilisés pour les signes, la mantisse et l'exposant.

18
dasblinkenlight

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
28
Andreas Brinck

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

7
SirGuy

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é.

2
Timothy Jones

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;
}

GitHub en amont .

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.

1
foo