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

1 2 3 4 5
Avatar
Samuel Krempp
le Monday 24 May 2004 18:57, écrivit :

Tous vos entierzs sont positifs, déclarez-les donc unsigned.


bof, moi je préfere choisir d'utiliser unsigned que si
numeric_limits<int>::max() ne me suffit pas ou que je compte utiliser
l'arithmétique modulo 2^n.

je ne crois pas qu'utiliser des unsigned dès qu'on a un nbre forcément
positif soit une convention usuelle en C++. en tout cas je lui trouve moins
d'avantage que d'inconvénients.

--
Sam

Avatar
kanze
Pierre Maurette wrote in message
news:...
"Rincevent" typa:

5) Pour la particules appartenant à la k-ème classe, sa couleur est
donc (canoniquement) donnée par (k/P)*N (k/P)*N n'est pas forcément
un entier, on prends donc la partie entière pour être sur...


Tous vos entiers sont positifs, déclarez-les donc unsigned.


Non. Gabi l'explique mieux que moi, mais en gros, l'abstraction
implémentée par les unsigned n'est pas un ensemble des nombres naturels.
Le type naturel pour les calculs entiers en C/C++, c'est int. On
n'utilise d'autres types que dans les cas exceptionnels. On n'utilise
donc unsigned que dans les cas où on a besoin d'un de ces
caractèristiques particuiliers :

- Des opérations bit-à-bit ne donne pas de résultats « bizarre », même
si on manipule le bit de poids fort. (C'est surtout important pour
les décalages à droits.)

- On a besoin d'un arithmétique modulo, par exemple dans le calcul
d'un code d'hachage.

- On a besoin de ce bit supplémentaire. Pour représenter ces niveaux
de gris, par exemple, je verrais bien un unsigned char, parce que
signé, il lui faudrait un short.

Enfin, les signé et les non signé ne font pas toujours bon mélange.
Donc, quand une interface externe impose des non-signés, il est souvent
préférable d'en faire pareil, simplement pour éviter le mélange.

Note aussi l'effet des promotions integrales. S'il stocke ses valeurs
dans des unsigned int, dès qu'il s'en sert dans une expression, elles
seront converties en int. Utiliser par ailleurs des unsigned int
reviendra à melanger signé et non signé. Je sais que ça semble bizarre,
mais pour éviter de melanger des signé et des non signé lorsqu'on a des
unsigned char, il faut se servir de int, et non de unsigned. (C'est
beau, le C, n'est-ce pas ?)

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.

que devrait vous générer le compilateur si vous avez déclaré N const.


*Si* les décalages sont plus rapide que les multiplications. Ce n'est
pas toujours le cas, et j'ai déjà travaillé sur des systèmes où le
compilateur, à titre d'optimisation, convertissait les décalages en
multiplications.

En tout cas, c'est le problème du compilateur, et non le tien.

--
James Kanze GABI Software
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


Avatar
kanze
Pierre Maurette wrote in message
news:...
"Rincevent" typa:

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)


Comment faites-vous pour mener ce calcul de tête ou avec papier-crayon
? Commencez-vous par diviser ? Avez vous besoin de flottants pour
traiter le problème ?
Si vous faites :
int resultat = (a*c)/b;
vous traitez exactement le problème tel qu'il est posé, c'est à dire
comme un problème d'entiers.


C'est souvent une bonne solution, mais il faut faire attention aux
débordements possibles.

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

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.

Il est aussi rapide et plus naturel de tester le signe (et
malheureusement je pense qu'il faudra également tester le résultat
exact, le cas ou a*c est divisible par b) pour ajuster le résultat.
Mais le résultat direct correspond à l'idée que je me fais d'une
partie entière.


--
James Kanze GABI Software
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


Avatar
Samuel Krempp
le Monday 24 May 2004 18:11, écrivit :
5) Pour la particules appartenant à la k-ème classe, sa couleur est donc
(canoniquement) donnée par (k/P)*N


de nos jours, la séparation "canonique" en classe serait plutôt basée sur
des percentiles.
par exple :
std::vector<int> valeurs; // on y met toutes les valeurs des pixels
sort(valeurs);
std::vector<int> bornes_classes;
for(int k=0; k<P; ++k) {
bornes_classes.push_back(valeurs[(k*N)/P]);
}

(tel quel ça pourrait donner les mêmes bornes à plusieurs classes, donc
produire moins que P classes, mais l'idée y est..)

au lieu d'avoir P classe bornées par des valeurs choisies sur une échelle
arbitraire, et parmi lesquelles la plupart peuvent très bien être vides,
on se retrouve avec P classes adaptées aux valeurs observées.

--
Sam

Avatar
Pierre Maurette
drkm typa:

Pierre Maurette writes:
[...]

Si vous faites :
int resultat = (a*c)/b;
vous traitez exactement le problème tel qu'il est posé, c'est à dire
comme un problème d'entiers.


Et qu'advient-il lorsque

a == std::numeric_limits< int >::max() ;
Oui. J'avais fait des hypothèses sur le problème qui n'étaient pas

mentionnées dans le message original (mais que le second message de
Rincevent rendent valables).
En fait, le message original est un peu étrange, je ne vois ce que
vient y faire le floor() puisque a, b et c me semblent devoir être
tous positifs.

? En fait, je suis en train de me demander s'il n'y a pas conversion
du résultat de « a * c » en un long int dans ce cas, mais je ne pense
pas ; et ce ne ferait que déplacer le problème. Tandis que

int result = static_cast< int >(
static_cast< double >( a ) / b * c
) ;

gère correctement le problème.
Je trouve dommage de passer par des double si le contexte rend cette

précaution inutile, et je mettrais ma main à couper que c'est ici le
cas. Ça dépend de l'étendue des valeurs admissibles pour a, b et c,
mais également de la fréquence du calcul. Il est clair que si le
calcul est fait une seule fois (avant une boucle par exemple), il est
inutile de trop se casser la tête. Surtout si l'on ne sait rien sur
a, b et c, auquel cas il faudrait faire quelques tests pour choisir
entre (a/b)*c, (c/b)*a et (a*c)/b, pas nécessairement plus lent que la
passage par double, mais un peu plus prise de tête, moins lisible,
etc...
Attention également, le passage par double donne un résultat qui peut
être différent (de celui exact donné par des int) si a*c est divisible
par b.
D'une façon plus générale, il me semble voir souvent des erreurs dues
à une utilisation abusive d'opérations "réelles" sur une problématique
d'entiers. Ce qui n'en disqualifie pas non plus l'usage modéré.

Pour ce qui est de la promotion en long int, je n'ai trouvé que
4.5/1. Je ne suis pas certain qu'il s'agit du bon paragraphe, mais si
c'est le cas, cette promotion en long int n'existe pas (ce que je
pense).
Il me semble également qu'il ne doit pas y avoir de promotion int ->

long int. Mais je travaille (c'est une erreur) avec en tête des int et
des long int équivalents. Si ce n'est pas le cas:
int result = static_cast<int>(static_cast<long int>(a)/b*c);
--
Pierre


Avatar
kanze
Pierre Maurette wrote in message
news:...
"Rincevent" typa:

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)


Comment faites-vous pour mener ce calcul de tête ou avec papier-crayon
? Commencez-vous par diviser ? Avez vous besoin de flottants pour
traiter le problème ?
Si vous faites :
int resultat = (a*c)/b;
vous traitez exactement le problème tel qu'il est posé, c'est à dire
comme un problème d'entiers.


C'est souvent une bonne solution, mais il faut faire attention aux
débordements possibles.

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

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.

Il est aussi rapide et plus naturel de tester le signe (et
malheureusement je pense qu'il faudra également tester le résultat
exact, le cas ou a*c est divisible par b) pour ajuster le résultat.
Mais le résultat direct correspond à l'idée que je me fais d'une
partie entière.


--
James Kanze GABI Software
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


Avatar
Pierre Maurette
typa:

Pierre Maurette wrote in message
news:...
"Rincevent" typa:

5) Pour la particules appartenant à la k-ème classe, sa couleur est
donc (canoniquement) donnée par (k/P)*N (k/P)*N n'est pas forcément
un entier, on prends donc la partie entière pour être sur...


Tous vos entiers sont positifs, déclarez-les donc unsigned.


Non. Gabi l'explique mieux que moi, mais en gros, l'abstraction
implémentée par les unsigned n'est pas un ensemble des nombres naturels.
Le type naturel pour les calculs entiers en C/C++, c'est int. On
n'utilise d'autres types que dans les cas exceptionnels. On n'utilise
donc unsigned que dans les cas où on a besoin d'un de ces
caractèristiques particuiliers :

- Des opérations bit-à-bit ne donne pas de résultats « bizarre », même
si on manipule le bit de poids fort. (C'est surtout important pour
les décalages à droits.)

- On a besoin d'un arithmétique modulo, par exemple dans le calcul
d'un code d'hachage.

- On a besoin de ce bit supplémentaire. Pour représenter ces niveaux
de gris, par exemple, je verrais bien un unsigned char, parce que
signé, il lui faudrait un short.

Enfin, les signé et les non signé ne font pas toujours bon mélange.
Donc, quand une interface externe impose des non-signés, il est souvent
préférable d'en faire pareil, simplement pour éviter le mélange.
OK. En fait, j'utilise souvent (par défaut) le int, en l'absence

d'autre contrainte, quand c'est un "simple" entier dont j'ai besoin.
Quand je choisis autre chose, c'est soit pour respecter un prototype
imposé, soit quand la taille a une importance. C'est dans ce dernier
cas que je choisis entre signed et unsigned en fonction de ce que la
variable représente (et c'est donc parfois une erreur). J'utilise
alors le plus souvent un typedef (ou des types de l'implémentation du
genre _uint8, _sint8, etc). En particulier, je réserve char (dont le
signed/unsigned n'est pas défini par la norme) exclusivement aux
caractères.


Note aussi l'effet des promotions integrales. S'il stocke ses valeurs
dans des unsigned int, dès qu'il s'en sert dans une expression, elles
seront converties en int. Utiliser par ailleurs des unsigned int
reviendra à melanger signé et non signé. Je sais que ça semble bizarre,
mais pour éviter de melanger des signé et des non signé lorsqu'on a des
unsigned char, il faut se servir de int, et non de unsigned. (C'est
beau, le C, n'est-ce pas ?)
Effectivement, c'est bien dans C++4.5.1, mais certaines conséquences

m'en avaient échappé.



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++) ?


que devrait vous générer le compilateur si vous avez déclaré N const.


*Si* les décalages sont plus rapide que les multiplications. Ce n'est
pas toujours le cas, et j'ai déjà travaillé sur des systèmes où le
compilateur, à titre d'optimisation, convertissait les décalages en
multiplications.

En tout cas, c'est le problème du compilateur, et non le tien.
Absolument.

--
Pierre



Avatar
Michel Michaud
Dans news:,
Non. Gabi l'explique mieux que moi, mais en gros, l'abstraction
implémentée par les unsigned n'est pas un ensemble des nombres
naturels. Le type naturel pour les calculs entiers en C/C++,
c'est int. On n'utilise d'autres types que dans les cas
exceptionnels. On n'utilise donc unsigned que dans les cas où
on a besoin d'un de ces caractèristiques particuiliers :

- Des opérations bit-à-bit ne donne pas de résultats «
bizarre », même si on manipule le bit de poids fort. (C'est
surtout important pour les décalages à droits.)

- On a besoin d'un arithmétique modulo, par exemple dans le
calcul d'un code d'hachage.

- On a besoin de ce bit supplémentaire. Pour représenter ces
niveaux de gris, par exemple, je verrais bien un unsigned
char, parce que signé, il lui faudrait un short.


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.

Ceci dit, je crois que c'est un « erreur » qui est du même
niveau que les conversions implicites à perte de valeur : on
sent bien que ces « experts » mettent les compilateurs en mode
« pas d'avertissement pour ces petits détails » alors qu'au
contraire plusieurs personnes, dont moi, aiment que les
programmes passent sans avertissement, même au niveau le plus
élevé...

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

Avatar
Michel Michaud
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.

Demandez à quelqu'un qui fait du VB s'il fait attention
d'utiliser la division entière quand il en a besoin (au moins
en Pascal, il me semble qu'il fallait convertir explicitement
le résultat de la division pour le ravoir en entier).

Je comprends que le programmeur peut faire des erreurs s'il ne
fait pas attention à l'ordre des opérations en calcul entier,
mais il faut apprendre car il y a aussi des problèmes avec
l'ordre des opérations dans les calculs réels, même si la
plupart des programmeurs ne semblent pas s'en préoccuper.

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


Avatar
Michel Michaud
Dans news:, Pierre
D'une façon plus générale, il me semble voir souvent des
erreurs dues à une utilisation abusive d'opérations "réelles"
sur une problématique d'entiers. Ce qui n'en disqualifie pas
non plus l'usage modéré.


Mais qui demande que le programmeur comprenne bien ce qu'il
fait et, dans ce cas, il ne sentira pas le besoin de passer
par des réels si ce n'est pas nécessaire.

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

1 2 3 4 5