Dépassement de capacité ou pas ?

L'auteur de ce sujet a trouvé une solution à son problème.
Staff
Auteur du sujet

Bonsoir à tous,

En me repenchant sur les limites des types, je bute sur une question par rapport aux limites des types flottants : s'il ne fait pas de doute que l'affectation d'un nombre dépassant sa borne négative ou positive constitue un dépassement, quid d'un nombre trop proche de zéro pour être représenté ?

Autrement dit, le code ci-dessous réalise-t-il un dépassement de capacité ou bien s'agit-il d'un ajustement à la « valeur la plus proche représentable » (zéro étant situé dans les bornes du type) ? La seconde hypothèse me paraît la plus plausible, mais je n'ai aucune certitude sur ce point.

Des avis sur la question ? :)

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
#include <stdio.h>

int
main(void)
{
    float f = 1e-308; /* Dépassement ? */

    printf("%f\n", f); /* 0.000000 */
    return 0;
}

When a value of real floating type is converted to a real floating type, if the value being converted can be represented exactly in the new type, it is unchanged. If the value being converted is in the range of values that can be represented but cannot be represented exactly, the result is either the nearest higher or nearest lower representable value, chosen in an implementation-defined manner. If the value being converted is outside the range of values that can be represented, the behavior is undefined. Results of some implicit conversions may be represented in greater range and precision than that required by the new type (see 6.3.1.8 and 6.8.6.4).

ISO/IEC 9899:2011, doc. N1570, 6.3.1.5, Real floatting types, p. 52

Édité par Taurre

+0 -0
Staff

Cette réponse a aidé l'auteur du sujet

Salut,

Là, tu es bien dans l'intervalle représentable, mais le mode d'arrondi par défaut est « implementation-defined ». Donc dans l'absolu, tu ne sais pas si c'est au plus proche ou vers zéro…

Toi tu utilises un float, donc souvent, c'est du simple précision, et ton nombre est trèèès petit par rapport au plus petit non nul, c'est sûrement un vrai zéro. Si tu utilisais un double, le nombre serait juste dénormalisé (et non nul).

C'est un vaste sujet, et chaque implémentation fait un peu à sa sauce, en essayant de ressembler à IEEE-754. En vérité, tu ne peux pas vraiment savoir…

Édité par Aabu

+0 -0

FE_Exception ? http://en.cppreference.com/w/c/numeric/fenv (cf en particulier FE_UNDERFLOW)

Par contre, le cast a l'initialisation d'une litterale produit sur Clang (C++) des warnings et aucune exception FE. Ce n'est pas la meme chose en C ?

1
2
3
4
5
6
7
8
main.cpp:9:15: warning: implicit conversion loses floating-point precision: 'double' to 
'float' [-Wconversion]
    float f = 1e-308; /* Dépassement ? */    
          ~   ^~~~~~
main.cpp:18:20: warning: implicit conversion increases floating-point precision: 'float' to 
'double' [-Wdouble-promotion]
    printf("%f\n", f); /* 0.000000 */
    ~~~~~~         ^

J'ai une exception FE_UNDERFLOW uniquement sur un cast de variables.

1
2
double f = 1e-308;
float g = f; // FE_UNDERFLOW exception
+0 -0
Staff
Auteur du sujet

Merci pour vos réponses. :)

Là, tu es bien dans l'intervalle représentable, mais le mode d'arrondi par défaut est « implementation-defined ». Donc dans l'absolu, tu ne sais pas si c'est au plus proche ou vers zéro…

Aabu

Hmm… C'est donc bien ce que je souspsonais, c'est un arrondi et non un dépassement, il n'y aurait donc pas de comportement indéfini.

FE_Exception ? http://en.cppreference.com/w/c/numeric/fenv (cf en particulier FE_UNDERFLOW)

gbdivers

Ah ! Merci, j'avais effectivement oublié l'en-tête <fenv.h> ajouté en C99. Le code ci-dessous semble bien valider l'hypothèse d'un arrondi.

 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
#include <float.h>
#include <stdio.h>
#include <fenv.h>

#pragma STDC FENV_ACCESS ON

int
main(void)
{
    float f;
    double g = DBL_MIN;
    int e;

    feclearexcept(FE_ALL_EXCEPT);
    f = g;
    e = fetestexcept(FE_INEXACT | FE_INVALID | FE_OVERFLOW | FE_UNDERFLOW);

    if (e & FE_INEXACT)
        puts("FE_INEXACT");
    else if (e & FE_INVALID)
        puts("FE_INVALID");
    else if (e & FE_OVERFLOW)
        puts("FE_OVERFLOW");
    else if (e & FE_UNDERFLOW)
        puts("FE_UNDERFLOW");

    return 0;
}
1
FE_INEXACT

Édité par Taurre

+0 -0
Staff
Auteur du sujet

Par contre, le cast a l'initialisation d'une litterale produit sur Clang (C++) des warnings et aucune exception FE. Ce n'est pas la meme chose en C ?

gbdivers

Non, dans mon cas je n'ai aucun avertissement (en C, donc), ni de GCC, ni de Clang.

J'ai une exception FE_UNDERFLOW uniquement sur un cast de variables.

gbdivers

Par curiosité, obtiens-tu la même chose avec le code C que j'ai donné ?

+0 -0
Staff
Auteur du sujet

Arf ! J'avais oublié que plusieurs drapeaux pouvaient être levés en même temps. >_<
Ouais, du coup, pareil, j'ai les drapeaux FE_UNDERFLOW et FE_INEXACT qui sont levés… Dans ce cas, cela change la donne, le code induit un comportement indéfini puisqu'il y a dépassement.

Édité par Taurre

+0 -0
Vous devez être connecté pour pouvoir poster un message.
Connexion

Pas encore inscrit ?

Créez un compte en une minute pour profiter pleinement de toutes les fonctionnalités de Zeste de Savoir. Ici, tout est gratuit et sans publicité.
Créer un compte