OVH Cloud OVH Cloud

[Calcul] Problème étrange

69 réponses
Avatar
Rincevent
Bonjour à tous,
g un gros problème dans mon programme C++.
Voilà g 3 entier a,b et c
Je souhaite calculer la partie entière de (a/b)*c
J'utilise pour cela la fonction floor() de <cmath>
Or deux problèmes se présentent :

1) Le compilateur renvoie 0 lorsque je lui demande de calculer (a/b)*c (et
ce, alors que les valeurs de a,b,c ne devraient pas donner un tel résultat)

2) Le compilateur m'insulte lorsque j'essaie d'appliquer la fonction floor()
à (a/b)*c (motif : l'argument n'est pas un double...)


- Je suppose qu'il doit exister une fonction pour convertir des rapports
d'entier en double, non ?
- Et inversement pour passer d'un double codant un entier --> en type int ?
- floor() attend un double comme argument... Soit mais quel est le type duy
résultat ? un double ? un int ?
- comment résoudre cet épineux problème de façon élégante ??? ;-)

Quelqu'un peut-t-il m'aider ?
Merci d'avance !

Rincevent

10 réponses

Avatar
Gabriel Dos Reis
writes:

[...]

| Sérieusement, au moins dans la partie STL (et c'est bien là où on trouve
| l'introduction des size_t au bout de bras), c'est bien la troisième
| motivation ci-dessus qui a joué, au moins en partie. N'oublie pas que
| Stepanov a beaucoup développé sur un PC, à un époque où les PC étaient
| des machines à 16 bits (mais que les pointeurs avec 32 bits). Vouloir un
| vector<char> avec 40000 caractères (quand on peut avoir jusqu'à 640 Ko
| de mémoire totale) n'est pas si étrange que ça.

Je ne sais plus si c'est Stepanov qui a introduit ces size_type
non-signés. J'avais demandé à un grand expert qui a une horreur
incroyable de size_t comme type d'indice de tableau (dans la
bibliothèque), il m'a grosso modo répondu que c'était probablement
apparu un jour où il ne surveillait pas ce qui se passait dans le LWG...

Bon, il était midi, on allait déjeuner et les discussions de café n'ont
pas forcément le poids d'une réponse traditionelle, alors si jamais
quelqu'un me cite, faites attention que c'était une discussion de café :-)

[...]

| > Ceci dit, je crois que c'est un « erreur » qui est du même
| > niveau que les conversions implicites à perte de valeur :
|
| Pas du tout. Les conversions implicites à perte de valeur est une erreur
| de conception pûre et simple. Stroustrup n'avait pas le choix, à cause
| de la compatibilité C. En fait, je crois que l'« erreur » vient du fait
| que C dérivait au départ de B, qui lui n'avait pas de types du tout (et
| donc, pas de conversions). Mais le fait qu'il y a une excuse ne veut pas
| dire que ce n'est pas une erreur.

yep. Et en lisant le D&E et en faisant attention au champ lexical
qu'il utilise dans le chapitre « Overloading » (et si on connait en
général son ton réservé), on s'aperçoit qu'il n'apprécie vraiment pas
ces conversions chaotiques.

-- Gaby
Avatar
Gabriel Dos Reis
--=-=- Content-Type: text/plain; charset=iso-8859-15
Content-Transfer-Encoding: 8bit

Loïc Joly writes:

| Samuel Krempp wrote:
|
| > Dans le monde des unsigned int, "être positif" n'a pas de sens.
| > Et c'est vraiment dans la quintessence du type : le concept auquel
| > correspond le mieux les types entiers unsigned n'est pas pas celui des
| > nombres entiers positifs, mais celui de Z/nZ (i.e. arithmétique
| > modulo n -avec n puissance de 2
| > précisémént) dont le caractère cyclique s'oppose profondément à toute
| > relation d'ordre compatible avec l'addition.
|
| En quoi int est-il préférable ?

--=-=- Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit


En C (et en C++), le type naturel du type d'un indice de tableau,
c'est int. J'estime que c'est suffisamment une bonne raison.
C'est ce que faisait Dennis Ritchie, c'est ce que faisait Bjane
Stroustrup. Un jour, quelqu'un dans le comité C, a pensé que c'était
une brillante idée d'utiliser size_t comme entier non signé, et depuis
c'est le foutoir.

--=-=- Content-Type: text/plain; charset=iso-8859-15
Content-Transfer-Encoding: 8bit


| On a aussi les phénomènes de cyclicité,

--=-=- Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit


No, on a un phénomène de débordement. techniquement, cela peut par
exemple lever une exception.

--=-=- Content-Type: text/plain; charset=iso-8859-15
Content-Transfer-Encoding: 8bit


| mais en plus, ils ne sont pas spécifiés par la norme.

--=-=- Content-Type: text/plain; charset=iso-8859-1
Content-Transfer-Encoding: 8bit


Non, c'est undefined behaviour, voulant dire que c'est laissé au choix
des implémentations de définir ce qu'elles en pensent. Mais visiblement,
le débordement des entiers est le dernier des soucis des auteurs de
compilateurs et en général ils ont l'air de s'en foutre. Il revient à
l'utilisateur de leur faire connaire leurs opinions.

-- Gaby


--=-=-=--
Avatar
Pierre Maurette
typa:

Pierre Maurette wrote in message
news:...

[...]
N est une constante, apparemment, ainsi que P pendant le
calcul. Votre problème esr ainsi simplifié :

int resultat = (k*N)/P; //c'est tout
ou même
int resultat = (k<<8)/P;


Jamais. Il veut multiplier, non décaler.


Surtout que je déclare resultat en int (et non unsigned int). Laisser
faire le compilateur, en déclarant toutefois N const si c'est le cas.

Question subsidiaire: (je me situe pour l'exemple dans une
architecture où la multiplication serait très pénalisante par rapport
au décalage). Si N est constant dans une boucle, faut-il faire
confiance au compilateur ou est-il préférable de l'orienter par un
cast ou autre méthode ? Si oui, laquelle (en C et en C++) ?


Je ne sais pas exactement ce que tu veux dire par « constant dans une
boucle ». S'il ne s'agit pas d'une constante au moment de la
compilation, il n'y a pas grand chose que ni toi ni le compilateur peut
faire. Et si N est une constante au moment de la compilation, c'est
égale où il est déclaré -- pour le compilateur, une constante est une
constante.
OK. Je m'étais un peu emballé. En fait, si N prend une série de

valeurs constantes connues à la compilation (c'était le cas dans mes
tests), il me faut bêtement et logiquement déclarer une série de
constantes !
De toutes façons, il est à priori anormal (sauf pour clarté de
l'écriture, mais alors utiliser des constantes) de coder quelque chose
dont la sortie peut être calculée avant compilation. Je fais mumuse
avec le compilateur de Microsoft qui ne loupe pas ce genre de chose.
C'est même parfois difficile de lui faire générer quelque chose pour
faire des tests (merci volatile).
--
Pierre




Avatar
Pierre Maurette
typa:
[...]
Malheureusement, C/C++ n'a pas des types à sous-étendu (subrange
types). On peut les implémenter au moyens des classes, mais le résultat
est assez lourd. Alors, en général, on s'en passe. (Peut-être à tort, je
ne sais pas. Un template Subrange< typename T, T, T > ne doit pas poser
de grands problèmes.)
Peut-être y a-t-il un problème de livre et/ou d'enseignement à vouloir

présenter immédiatement les applications lourdes des classes, à partir
d'exemples (les chats, les avions) dont la traduction informatique est
peu convaincante. Bon, d'autres exemples sont quand même mieux
choisis, et certains livres n'ont pas ce défaut.
J'aime bien la première phrase sur les classes dans un référence IBM :
"A class is a mechanism for creating user-defined data types."
Question:
Le fait qu'il y ait en C++ des types qui ne sont pas des classes
(contrairement à Java par exemple) permet-il des choses impossibles
dans le cas contraire ? Je suppose que si la réponse est "oui", c'est
vers les applications bas niveau que ça se situera. Mais je ne pense
pas, c'est plus la notion de pointeur et l'opérateur adresse & qui
rend le langage si universel.
--
Pierre

Avatar
drkm
writes:

Pierre Maurette wrote in message
news:...

Je vous laisse, par rapport à ce que vous souhaitez, juger de
l'oportunité d'un ajustement par floor(). Personnellement, je ne
l'utiliserais pas, puisque c'est une fonction double.


Si les valeurs sont signées, il a intérêt à passer en flottant de toute
façon. La division avec des résultats négatifs n'est pas bien définie en
C++.


Pourrais-tu être un peu plus précis sur ce qui n'est pas bien
défini, stp ? Je n'arrive pas à voir ce dont tu parles.

--drkm


Avatar
Alain Naigeon
"Samuel Krempp" a écrit dans le
message news: 40b3e824$0$6343$
le Tuesday 25 May 2004 23:38, écrivit :
Pour les classes on conseille de s'inspirer de "l'univers du problème" ;
serait-ce une tare de faire de même pour d'autres types, encore
plus basiques ? Quand une variable représente un entier qui n'a
aucun sens autre que positif, cela me révulse absolument qu'il
ne soit pas unsigned.


en cherchant un exple, il me vient :
for(unsigned int n = 25; n>= 0; --n) ;
c'est pour moi une illustration assez simple du fait que les unsigned int
sont mal adaptés pour modéliser un sous ensemble des nombres entiers
positifs.

Dans le monde des unsigned int, "être positif" n'a pas de sens.


[...]

Je te remercie de ta longue réponse, à laquelle, malheureusement,
j'avoue n'avoir pas compris grand-chose.

Simplement, à la dernière phrase j'objecterais que, dans ce
cas, le langage ne doit pas autoriser un test de positivité
sur quelque chose qui ne connaît pas cette propriété.
(même "positivité" me paraît ambigu ; par contre,
"non nul" est clair, et a parfaitement un sens pour un
entier non signé).

Ce n'est certainement pas une raison, à mes yeux, pour
violer grossièrement le bon sens en permettant que le
type d'un nombre d'habitants puisse être négatif. L'idée
de bricoler des gardes-fous par des tests à l'exécution
n'est qu'un pis aller, vraiment pas séduisante quand il
existe un type qui colle avec le domaine de définition
de ce qu'on veut représenter.
Un nombre d'habitants est, par nature, non signé. Je
choisis donc le type "unsigned int", qui signifie, si je
ne m'abuse : "non signé". Donc, si quelque chose
cloche par après, c'est la faute du langage, pas la
mienne !

(en plus, ce n'est pas en me disant que ça vient du C
qu'on va me convaincre, mais alors là pas du tout ;
ça ne m'étonne pas, chaque fois qu'un problème
conceptuellement clair reçoit une réponse en termes
d'implémentation, machine, etc, c'est une démarche
typiquement C-iste. Finalement, ils ont peut-être
raison de penser que les classes et tout ce bazar est
lourd pour ce qu'il apporte ; je crois que je vais
revenir à la bonne vieille méthode de mettre un
pointeur en premier argument de toutes mes
focntions, et par nostalgie je vais même l'appeler
"this", pourquoi pas, hein ? :-) )

--

Français *==> "Musique renaissance" <==* English
midi - facsimiles - ligatures - mensuration
http://anaigeon.free.fr | http://www.medieval.org/emfaq/anaigeon/
Alain Naigeon - - Strasbourg, France


Avatar
Michel Michaud
a écrit dans le message de
news:
Le problème, c'est ce qu'on entend par « division ». Si tu
démandes à un non-informaticien quelle est la valeur de 1/3, il
va te dire un tiers. Et non 0. Dans la mésure où l'opérateur ne


Mais est-ce qu'on veut C++ (ou les autres langages) fait pour
des non informaticiens ? Demande à un non informaticiens ce que
signifie 3*5 et il va dire : ???. Et il y a tellement d'autres
cas semblables...

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/

Avatar
Alain Naigeon
a écrit dans le message news:

"Michel Michaud" wrote in message
news:<0_Jsc.27867$...
Dans news:,
Pierre Maurette wrote in message
news:...
Mais bon, personnellement, je trouve regrettable l'utilisation de /
pour la division entière, j'aime bien le couple div mod du Pascal.


Tout à fait d'accord. Mais C/C++ ne sont pas seuls dans leur choix.


Tiens vous êtes bizarres : au contraire, j'ai toujours trouvé que les
langages où les opérations classiques (+-*/) sont dans le type des
données SAUF la division étaient plutôt bizarres. Il me semble que,
la plupart du temps, si on a des entiers pour les calculs, on veut un
entier comme résultat et la division réelle ne fait qu'apporter des
conversions inutiles.


Le problème, c'est ce qu'on entend par « division ». Si tu démandes à un
non-informaticien quelle est la valeur de 1/3, il va te dire un
tiers. Et non 0. Dans la mésure où l'opérateur ne donne pas 1/3, ou
quelque chose de proche, pour 1/3, je ne trouve pas que l'opérateur /
est bien choisi.


C'est peut-être un problème d'éducation pour nous tous. On n'insiste
pas assez, au départ, sur le fait qu'un opérateur fonctionne autrement
avec chaque type. C'est ainsi que la rencontre d'une multiplication
non commutative nous a tous plongés, un jour, stupéfiés.
Donc, comme c'est un phénomène général, je ne suis pas sûr que
la solution soit de modifier le symbole opératoire, vu qu'on verra
assez vite qu'il n'en faut pas deux, mais trois, quatre, etc.
Donc gardons le signe, en sachant qu'il agit sur des êtres différents.
Parce, ton idée, elle conduit à distinguer aussi sqrt(int) de sqrt(float),
etc. Donc, il faudait les appeler différemment aussi ?
Et dans un langage qui implémenterait les matrices et leurs
opérations comme type de base, il faudrait aussi un autre symbole
pour leur multiplication ? Ca commence à faire beaucoup.

--

Français *==> "Musique renaissance" <==* English
midi - facsimiles - ligatures - mensuration
http://anaigeon.free.fr | http://www.medieval.org/emfaq/anaigeon/
Alain Naigeon - - Strasbourg, France




Avatar
Michel Michaud
a écrit dans le message de
news:
"Michel Michaud" wrote in message
news:<EJJsc.27786$...
C'est à peu près ce que je pense aussi, mais les nombreuses
discussions (sur clc++m par exemple) montrent bien que ce n'est
pas complètement noir ou blanc. En particulier, le fait que la
bibliothèque standard regorge de unsigned (pour aucune des
raisons citées plus haut) indique qu'au moins quelques grands
experts ont, pendant un certain temps, pensé autrement.


Ou que la bibliothèque n'est pas le produit des grands
experts:-).

Sérieusement, au moins dans la partie STL (et c'est bien là où on
trouve l'introduction des size_t au bout de bras), c'est bien la
troisième motivation ci-dessus qui a joué, au moins en partie.
[... i.e. un bit de plus ...]

N'oublie pas que Stepanov a beaucoup développé sur un PC, à un
époque où les PC étaient des machines à 16 bits (mais que les
pointeurs avec 32 bits). Vouloir un vector<char> avec 40000
caractères (quand on peut avoir jusqu'à 640 Ko de mémoire totale)
n'est pas si étrange que ça.


Hum. Et pourquoi pas simplement prendre un long (minimum 32 bits
tout le temps) pour pouvoir avoir un vecteur de 80 000 éléments
ou plus... Avec 640K, on peut aller bien plus aussi. Je ne vois
pas pourquoi 40 000 semblerait plus utile que 80 000...

Par ailleurs, si je me rappelle bien, à cause des types pour
les distances entre les éléments (ou quelque chose du genre), on
ne peut pas avoir plus du nombre d'éléments du type signé (mais
je peux me tromper...).

[...]
Ceci dit, je crois que c'est un « erreur » qui est du même
niveau que les conversions implicites à perte de valeur :


Pas du tout. Les conversions implicites à perte de valeur est une
erreur de conception pûre et simple. Stroustrup n'avait pas le
choix, à cause de la compatibilité C. En fait, je crois que l'
« erreur » vient du fait que C dérivait au départ de B, qui lui
n'avait pas de types du tout (et donc, pas de conversions). Mais
le fait qu'il y a une excuse ne veut pas dire que ce n'est pas
une erreur.


Exactement. C'est une « erreur » (note les guillemets). Je n'ai
pas dit qu'elle venait de Stroustrup...

[...]
Moi aussi, je préfère ne pas avoir des avertissements. Mais
parfois, il y a des compilateurs qui en émettent des vraiments
bêtes.


On désactive ceux-là seulement :-)

--
Michel Michaud
http://www.gdzid.com
FAQ de fr.comp.lang.c++ :
http://www.cmla.ens-cachan.fr/~dosreis/C++/FAQ/


Avatar
Alain Naigeon
a écrit dans le message news:


Tu veux dire que ça te révulse qu'il puisse représenter les valeurs en
dehors du domaine. Moi aussi. Par exemple, si je veux un type qui
représente un dé, je ne veux pas non plus qu'il puisse représenter un 0,
ou un 100.

Malheureusement, C/C++ n'a pas des types à sous-étendu (subrange
types).


Certes je regrette beaucoup le type intervalle du Pascal. Ceci dit,
je ne suis réaliste, travaillons avec ce qu'offre le langage. Ce que je dis
simplement, c'est que s'il existe un type, alors c'est vraiment bizare
de s'en passer. A moins que le langage ne nous trompe ; si les
"unsigned int" et leurs opérations autorisées (par le langage !) ne
se comportent pas comme des entiers sans signe, alors ce n'est
plus de café qu'il faut parler, mais de pousse-café :-)
Etre averti des problèmes d'implémentation numérique (et toi
tu l'es !), c'est bien. Mais prenons garde à ne pas penser les
nombres à partir de leur implémentation. Un entier sans signe,
cela a un sens. Si C++ a des problèmes pour les représenter
(en interdisant pas < 0 par exemple), cela ne change rien à
la pertinence du concept entier sans signe.
De toute façon il y a un problème déjà de confondre test de
signe avec <. < est parfaitement correct, simplement <0 ne
sera jamais vrai. Q'un entier sans signe ne puisse faire l'objet
d'un test de signe, cela tombe sous le sens. Par contre, le
fait un entier négatif soit < à ceux positifs ou nuls, c'est
déjà un théorème - je veux dire : < et test de signe sont
deux chose différentes, et en les confondant au départ,
on se retrove tôt ou tard avec des problèmes, ce n'est
que justice.

Sur "bassement technique" : j'ai fait de l'assembleur, et
j'adorais ça. Mais se donner des outils plus abstraits,
et s'occuper en permanence de leur implémentation,
est-ce bien logique ? Dans ce cas, le C te gardera
plus près de ça en permanence. Là je suis sérieux, ce
n'est pas polémique.

--

Français *==> "Musique renaissance" <==* English
midi - facsimiles - ligatures - mensuration
http://anaigeon.free.fr | http://www.medieval.org/emfaq/anaigeon/
Alain Naigeon - - Strasbourg, France