Entrée hexadécimale de nombres signés

Le
Michael DOUBEZ
Bonjour,

Je n'arrive pas à comprendre pourquoi les entrées hexadécimales signées
ratent pour ffff. Je suppose que la conversion hexa dans num_get<>() se
fait d'abord en non-signé puis il y a un check pour savoir si il y a
overflow dans le type signé (ce qui est le cas pour "ffff" en short).

Example: le programme suivant affiche "KO" sous gcc 3.4.3:
#include <iostream>
#include <sstream>

int main()
{
std::istringstream is("ffff");
short value;

if(!(is>>value))std::cerr<<"KO"<<std::endl;
}

Est ce une contrainte de formatage: le type nombre non signé a un nombre
limité de bytes à affiché alors que le type signé devrait garantir la
taille tu type ex: "fffffffe" pour -2 ?
Je ne vois que ça.

Est ce qu'il y une référence à cela dans le standard (§27.6.1.2?) ?

--
Michael
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
Sylvain Togni
Le #18634561
Michael DOUBEZ a écrit :

Je n'arrive pas à comprendre pourquoi les entrées hexadécimales signées
ratent pour ffff.



Pour la même raison que 65535 échouerait en décimal.

Je suppose que la conversion hexa dans num_get<>() se
fait d'abord en non-signé puis il y a un check pour savoir si il y a
overflow dans le type signé (ce qui est le cas pour "ffff" en short).

Example: le programme suivant affiche "KO" sous gcc 3.4.3:
#include <iostream>
#include <sstream>

int main()
{
std::istringstream is("ffff");
short value;

if(!(is>>value))std::cerr<<"KO"<<std::endl;
}

Est ce une contrainte de formatage: le type nombre non signé a un nombre
limité de bytes à affiché alors que le type signé devrait garantir la
taille tu type ex: "ffffff...fe" pour -2 ?



Non, -2 en base 16 ne s'écrit pas ffffff...fe mais ... -2 ! (ou -0x2)

La base 16 n'est pas traitée différemment des autres bases, les
nombres négatifs doivent commencer par le signe moins.

--
Sylvain Togni
Michael DOUBEZ
Le #18635051
Sylvain Togni wrote:
Michael DOUBEZ a écrit :

Je n'arrive pas à comprendre pourquoi les entrées hexadécimales
signées ratent pour ffff.



Pour la même raison que 65535 échouerait en décimal.

Je suppose que la conversion hexa dans num_get<>() se fait d'abord en
non-signé puis il y a un check pour savoir si il y a overflow dans le
type signé (ce qui est le cas pour "ffff" en short).

Example: le programme suivant affiche "KO" sous gcc 3.4.3:
#include <iostream>
#include <sstream>

int main()
{
std::istringstream is("ffff");
short value;

if(!(is>>value))std::cerr<<"KO"<<std::endl;




Je veux dire
if(!(is>>std::hex>>value))std::cerr<<"KO"<<std::endl;
}

Est ce une contrainte de formatage: le type nombre non signé a un
nombre limité de bytes à affiché alors que le type signé devrait
garantir la taille tu type ex: "ffffff...fe" pour -2 ?



Non, -2 en base 16 ne s'écrit pas ffffff...fe mais ... -2 ! (ou -0x2)



Pourtant:
short d=-1;
std::cout<<std::hex<<d<<std::endl;

écrit:
ffff

et non pas -1

La base 16 n'est pas traitée différemment des autres bases, les
nombres négatifs doivent commencer par le signe moins.



Sauf que les opérations ne sont pas réversibles, ce qui n'est pas très
intuitif.

Une référence dans le standard (je suppose que c'est hérité du C - strtod) ?

--
Michael
Jean-Marc Bourguet
Le #18635041
Michael DOUBEZ
Pourtant:
short d=-1;
std::cout<<std::hex<<d<<std::endl;

écrit:
ffff

et non pas -1



hex implique que l'argument est converti en unsigned (et oui, c'est defini
en fonction de "%x" de printf et "%x" prend un argument unsigned).

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
James Kanze
Le #18640681
On Feb 10, 1:37 pm, Michael DOUBEZ
Je n'arrive pas à comprendre pourquoi les entrées
hexadécimales signées ratent pour ffff. Je suppose que la
conversion hexa dans num_get<>() se fait d'abord en non-signé
puis il y a un check pour savoir si il y a overflow dans le
type signé (ce qui est le cas pour "ffff" en short).



Example: le programme suivant affiche "KO" sous gcc 3.4.3:
#include <iostream>
#include <sstream>



int main()
{
std::istringstream is("ffff");
short value;

if(!(is>>value))std::cerr<<"KO"<<std::endl;
}



Est ce une contrainte de formatage: le type nombre non signé a
un nombre limité de bytes à affiché alors que le type signé
devrait garantir la taille tu type ex: "ffffff...fe" pour -2 ?
Je ne vois que ça.



Comme d'autres ont déjà dit, 0xFFFF a une valeur positive qui ne
tient pas dans un short.

Est ce qu'il y une référence à cela dans le standard
(§27.6.1.2?) ?



§27.6.1.2 renvoie à §22.2.2.1 (la facette num_put), qui lui
renvoie à fscanf de la norme C. Qui dit (d'une façon globale,
pour toutes les conversions) « [...], the result of the
conversion is placed in the object pointed to by the first
argument following the format argument that has not already
received a conversion result. If this object does not have an
appropriate type, or if the result of the conversion cannot be
represented in the object, the behavior is undefined. »

La brouillon de comité pour C++0x a modifié §22.2.2.1 pour
renvoyer à strtoll (qui a un comportement défini quelque soit
les entrées), avec une gestion d'erreur précisée ; dans le cas
de débordement (comme ici), il doit stocker la valeur maximum
du type (ou valeur minimum, si la valeur lue est négative) et
positionner le failbit (et donc, l'entrée échoue). Ce qui
correspond plus ou moins au comportement des bonnes
implémentations déjà. (Je ne suis pas sûr en ce qui concerne le
stockage de la valeur -- il y a aussi une tradition qu'en cas
d'erreur, l'objet qu'on lit n'est pas modifié.)

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Sylvain Togni
Le #18641071
Jean-Marc Bourguet a écrit :

Michael DOUBEZ
Pourtant:
short d=-1;
std::cout<<std::hex<<d<<std::endl;

écrit:
ffff

et non pas -1



hex implique que l'argument est converti en unsigned (et oui, c'est defini
en fonction de "%x" de printf et "%x" prend un argument unsigned).



Et comment ça se fait qu'en entrée c'est l'inverse : ffff ne fonctionne
pas mais -1 oui ? L'incohérence est-elle au niveau ostream/istream ou
printf/scanf ?

(Je comprend mieux le problème de Michael maintenant.)

--
Sylvain Togni
Michael DOUBEZ
Le #18641061
James Kanze wrote:
On Feb 10, 1:37 pm, Michael DOUBEZ
Je n'arrive pas à comprendre pourquoi les entrées
hexadécimales signées ratent pour ffff. Je suppose que la
conversion hexa dans num_get<>() se fait d'abord en non-signé
puis il y a un check pour savoir si il y a overflow dans le
type signé (ce qui est le cas pour "ffff" en short).




[snip]

Comme d'autres ont déjà dit, 0xFFFF a une valeur positive qui ne
tient pas dans un short.



Mon erreur consistait a considérer le contenu bit de -1 en short et pas
la valeur.

Est ce qu'il y une référence à cela dans le standard
(§27.6.1.2?) ?



§27.6.1.2 renvoie à §22.2.2.1 (la facette num_put), qui lui
renvoie à fscanf de la norme C. Qui dit (d'une façon globale,
pour toutes les conversions) « [...], the result of the
conversion is placed in the object pointed to by the first
argument following the format argument that has not already
received a conversion result. If this object does not have an
appropriate type, or if the result of the conversion cannot be
represented in the object, the behavior is undefined. »



Dans ce cas, pour fscanf, c'est transparent puisque le passage de
paramètre perd l'information de type des pointeurs.
Je peux donc faire:
short val;
scanf("%hx",&val);

Je suppose que c'est formellement un comportement indéfini mais je vois
peu de différence avec:
short val;
unsigned short val_input;
scanf("%hx",&val_input);
val=unsigned short(val_input)

si je sais que la représentation bit correspond.

J'ai regardé dans 22.2.2.1.2, et si je comprend bien, ça veux dire que
toutes les opération font une conversion implicite en non-signé dès que
basefield n'est plus 0.

La brouillon de comité pour C++0x a modifié §22.2.2.1 pour
renvoyer à strtoll (qui a un comportement défini quelque soit
les entrées), avec une gestion d'erreur précisée ; dans le cas
de débordement (comme ici), il doit stocker la valeur maximum
du type (ou valeur minimum, si la valeur lue est négative) et
positionner le failbit (et donc, l'entrée échoue). Ce qui
correspond plus ou moins au comportement des bonnes
implémentations déjà. (Je ne suis pas sûr en ce qui concerne le
stockage de la valeur -- il y a aussi une tradition qu'en cas
d'erreur, l'objet qu'on lit n'est pas modifié.)



Sous gcc3.4.3 le paramètre n'est pas modifié et failbit est positionné.

Dès que j'ai mis le basefield de ma stream à ios::hex, mes tests
unitaires se sont mis à m'insulter. J'utilisais des
(i/o)stream_iterator<short>() et l'absence de réversibilité m'a surpris.
J'utilise donc des itérateurs <unsigned short> à la place. Mais, dans un
cas de static_assert du value_type de mes itérateurs ou de
metaparamètre, cette conversion serait énervante.

--
Michael
Jean-Marc Bourguet
Le #18642331
Sylvain Togni
Jean-Marc Bourguet a écrit :

> Michael DOUBEZ >
>> Pourtant:
>> short d=-1;
>> std::cout<<std::hex<<d<<std::endl;
>>
>> écrit:
>> ffff
>>
>> et non pas -1
> hex implique que l'argument est converti en unsigned (et oui, c'est defini
> en fonction de "%x" de printf et "%x" prend un argument unsigned).

Et comment ça se fait qu'en entrée c'est l'inverse : ffff ne fonctionne
pas mais -1 oui ? L'incohérence est-elle au niveau ostream/istream ou
printf/scanf ?



Le comportement me semble indefini en entree en C++03 si on rentre un
unsigned hors de l'intervalle valide pour le type signe correspondant
(3.9.1/3 indique que c'est bon pour les valeurs representable par les deux)
-- se prendre une erreur est alors pas mal. En C++0X, la description a
change et j'ai pas le temps de regarde -- comme je n'ai pas eu le temps de
cherche si il y a eu des DR concernant ca non integre dans C++03.

A+

--
Jean-Marc
FAQ de fclc++: http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ
C++ FAQ Lite en VF: http://www.ifrance.com/jlecomte/c++/c++-faq-lite/index.html
Site de usenet-fr: http://www.usenet-fr.news.eu.org
James Kanze
Le #18649821
On Feb 11, 10:23 am, Michael DOUBEZ
James Kanze wrote:
> On Feb 10, 1:37 pm, Michael DOUBEZ


>> Je n'arrive pas à comprendre pourquoi les entrées
>> hexadécimales signées ratent pour ffff. Je suppose que la
>> conversion hexa dans num_get<>() se fait d'abord en non-signé
>> puis il y a un check pour savoir si il y a overflow dans le
>> type signé (ce qui est le cas pour "ffff" en short).
[snip]



> Comme d'autres ont déjà dit, 0xFFFF a une valeur positive
> qui ne tient pas dans un short.



Mon erreur consistait a considérer le contenu bit de -1 en
short et pas la valeur.



>> Est ce qu'il y une référence à cela dans le standard
>> (§27.6.1.2?) ?



> §27.6.1.2 renvoie à §22.2.2.1 (la facette num_put), qui lui
> renvoie à fscanf de la norme C. Qui dit (d'une façon globale,
> pour toutes les conversions) « [...], the result of the
> conversion is placed in the object pointed to by the first
> argument following the format argument that has not already
> received a conversion result. If this object does not have an
> appropriate type, or if the result of the conversion cannot be
> represented in the object, the behavior is undefined. »



Dans ce cas, pour fscanf, c'est transparent puisque le passage
de paramètre perd l'information de type des pointeurs.



Je peux donc faire:
short val;
scanf("%hx",&val);



Non. La norme C est claire : « [concernant %u) The
corresponding argument shall be a pointer to unsigned
integer. ». Sinon, tu as un comportement indéfini.

Dans la pratique, évidemment... la norme C exige par ailleurs
(je crois) que les pointeurs vers un type signé et le type
correspondant non-signé aient la même taille et la même
représentation. Ce qui veut dire que si l'implémentation
n'effectue pas de vérification réele des types dans va_arg (et
je n'en connais aucune qui le fait), scanf va bien pouvoir
écrire l'image de bits de l'unsigned à travers le pointeur. Et
das le cas d'une machine complément à deux, tu es plus ou moins
garantie qu'une valeur avec tous les bits à un donne -1. (Pas
100%, évidemment, puisqu'une implémentation légale des unsigned,
c'est d'utiliser les signed, en forçant le bit de signe à 0.
Mais ça ne marche pas pour des short de 16 bits.)

Je suppose que c'est formellement un comportement indéfini
mais je vois peu de différence avec:
short val;
unsigned short val_input;
scanf("%hx",&val_input);
val=unsigned short(val_input)



Il y a une différence énorme. Dans ce cas-ci, tu as une
conversion explicite -- où, selon la norme C « either the
result is implementation-defined or an implementation-defined
signal is raised. »

si je sais que la représentation bit correspond.



D'abord, évidemment, tu ne le sais pas en général. Ensuite, du
moment que la valeur n'est pas représentable, la norme donne à
l'implémentation toutes les libertés.

J'ai regardé dans 22.2.2.1.2, et si je comprend bien, ça veux
dire que toutes les opération font une conversion implicite en
non-signé dès que basefield n'est plus 0.



Non. D'abord, évidemment, si basefield est dec (le défaut), la
spécification de conversion dépend de si le type est signé ou
non ; s'il est signé, on utilise %d (qui exige un pointeur vers
signé), non-signé %u (qui exige un pointeur vers non-signé).
Pour les autres bases, c'est pas clair : « A sequence of chars
has been accumulated in stage 2 that is converted (according to
the rules of scanf) to a value of the type of val. » Or, pour
%x, si val n'est pas non-signé, on a un comportement indéfini.
Lire du hex vers un objet de type signé a un comportement
indéfini, selon C++03. (Selon le brouillon de C++0x, les règles
de stdtoll ou de stdtoull s'applique, selon que le type de val
est signé ou non.)

> La brouillon de comité pour C++0x a modifié §22.2.2.1 pour
> renvoyer à strtoll (qui a un comportement défini quelque soit
> les entrées), avec une gestion d'erreur précisée ; dans le cas
> de débordement (comme ici), il doit stocker la valeur maximum
> du type (ou valeur minimum, si la valeur lue est négative) et
> positionner le failbit (et donc, l'entrée échoue). Ce qui
> correspond plus ou moins au comportement des bonnes
> implémentations déjà. (Je ne suis pas sûr en ce qui concerne le
> stockage de la valeur -- il y a aussi une tradition qu'en cas
> d'erreur, l'objet qu'on lit n'est pas modifié.)



Sous gcc3.4.3 le paramètre n'est pas modifié et failbit est
positionné.



C'est ce que j'aurais fait aussi, avant d'avoir lu le brouillon.
À mon avis, ça correspond plus à la tradition (failbit
positionné implique pas d'écriture dans le type cible). Mais il
y avait déjà des exceptions à cette tradition (istream::read(),
par exemple), et la solution adoptée par C++0x permet de
distinguer entre un débordement et l'absence complet d'un
nombre.

Dès que j'ai mis le basefield de ma stream à ios::hex, mes
tests unitaires se sont mis à m'insulter. J'utilisais des
(i/o)stream_iterator<short>() et l'absence de réversibilité
m'a surpris. J'utilise donc des itérateurs <unsigned short> à
la place. Mais, dans un cas de static_assert du value_type de
mes itérateurs ou de metaparamètre, cette conversion serait
énervante.



Les iostream sont conçus pour les entrées/sorties des êtres
humains. En général, tu ne peux pas supposer la
réversibilité : il n'est pas présente ni pour les virgule
flottants (au moins d'insister sur la précision), ni sur les
chaînes de caractères. Ni, à vrai dire, pour les entiers :
output << i << j ;
ne peut pas être rélu par :
input >> i >> j ;
Tout au plus, on peut se servir des iostream pour implémenter
un format text qui garantit la réversibilité, en y ajoutant des
séparateurs, forçant la présision des flottants, et avec un
traitement non trivial des chaînes de caractères. (À cet égard,
voir ParsableString dans ma bibliothèque.)

--
James Kanze (GABI Software) email:
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Publicité
Poster une réponse
Anonyme