PDA

View Full Version : [C++] Valore dello static_cast sbagliato


Dari
08-05-2010, 10:22
Ho un problema in un programma dove avrei bisogno di troncare un double per assegnare la parte intera ad un int. In pratica mi arriva un numero fra 0 e 1, che rappresenta una percentuale, io voglio scriverla "normalmente" come numero fra 0 e 100, per cui moltiplico il numero che arriva per 100 e lo converto in int con lo static_cast, in questo modo:

f(double percentualeDiPartenza_, double percentualeMassima_) {
int minimo, massimo;
double percentualeDiPartenzaInCentesimi = percentualeDiPartenza_*100.0, percentualeMassimaInCentesimi = percentualeMassima_*100.0;
minimo = static_cast<int>(percentualeDiPartenzaInCentesimi);
massimo = static_cast<int>(percentualeMassimaInCentesimi);
}


A questo punto succede quello che non capisco: se il numero che devo convertire come percentuale massima è 0.1, il risultato in massimo è 9 anziché 10, in più questo succede solo dentro questo programma, ho provato a "isolare" il pezzo di codice in un programma diverso e stampa 10:

#include <iostream>

using std::cout;
using std::endl;

int main() {
int numero;
double numero2 = 0.1;
numero = static_cast<int>(numero2*100.0);
cout << numero << ' ' << numero2 << endl;
}

L'output che ho è:

10 0.1


Non riesco a capire se ho il prosciutto sugli occhi io e c'è qualcosa che non vedo...se qualcuno riesce a capire cosa c'è che non va mi fa un grandissimo favore.

cionci
08-05-2010, 13:21
Prova con 0.1L al posto di 0.1 e 100.0L al posto di 100.0 ;)

Dari
08-05-2010, 13:48
Quindi è tipo una perdita di precisione? Ma 100.0 non viene convertito a double automaticamente? Ora provo comunque, grazie.

Aggiornamento: ho messo la L dopo tutte le costanti numeriche coinvolte, anche quelle nel codice che chiama la funzione, purtroppo non è cambiato nulla, altri suggerimenti?

p.s. ma c'è qualche differenza fra il codice del main che ho messo e quello della funzione f? Non riesco a capire come mai si comportino diversamente.

cionci
09-05-2010, 10:35
Sinceramente non mi ricordo, ma mi sembra che s enon specifichi il formato venga usato float di default.
Se metti L, la costante è un long double a 80 bit.
100.0 viene convertito automaticamente a double, il problema però non dovrebbe essere 100, che ha una rappresentazione esatta anche su un float, ma 0.1 ;)

Dari
09-05-2010, 12:51
Si ma il fatto è che 0.1 non da problemi, infatti il secondo pezzo di codice funziona, quello che non funziona è la funzione f quando al posto di uno dei due parametri arriva 0.1 (che viene calcolato da un'altra parte direttamente come double).

cionci
09-05-2010, 15:25
Bisogna allora vedere come è stato calcolato...

Dari
09-05-2010, 15:48
Il valore che passo alla funzione* è 1.0L meno un double x che può valere 0.9, 0.8, 0.7, 0.6, 0.5 (in input si seleziona uno fra dei valori fissi). Il problema sorge quando è 0.9, dei valori sono sicuro perché ho messo un cout per ogni variabile per vedere cosa succede. Se faccio stampare i valori coinvolti in:
percentualeDiPartenzaInCentesimi = percentualeDiPartenza_*100.0L ottengo 10 e 0.1, se faccio stampare minimo = static_cast<int>(percentualeDiPartenzaInCentesimi); ottengo 9, se c'è di mezzo qualche conversione a float non so proprio dove sia, il tipo float non lo uso da nessuna parte e ho messo la L dopo tutte le costanti reali.

*È in una funzione che ha solo un return 1.0L - x come codice.

cionci
09-05-2010, 15:59
Stampa percentualeDiPartenzaInCentesimi ed avrai la tua risposta... Comunque è un problema di rappresentazione.

1 - 0.9 non fa 0.1 in floating point o meglio...non fa esattamente 0.1, ma magari 0.0999999, con il risultato che se fai il troncamento ottieni 9.

Dari
09-05-2010, 16:25
Stampa 10 come ho scritto prima, e se stampo minimo invece stampa 9, mi sembra assurdo ma è così. E la cosa più assurda è che nel programma che ho scritto nell seconda parte del primo post lo stesso calcolo dà 10.

cionci
09-05-2010, 16:29
Mi posti tutto il codice, anche quello che usi per passare i valori alla funziona ?

Dari
09-05-2010, 16:44
È un po' un casino mettere tutto, cmq ecco:

double percentualeMassima_ = 0.5L - getParteNonDisponibile();
/*dentro la funzione getParteNonDisponibile() c'è return 1.0L - x, con x che può valere 0.9-0.5*/
f(0.0L, percentualeMassima_);

/* la f è quella di prima, il codice non è esattamente così perché è il costruttore di un oggetto, è troppo lungo
metterlo tutto e il resto non c'entra con il calcolo, le variabili in f vengono modificate solo nel codice che ho messo.*/

ps grazie mille, se non si può fare così vedrò di trovare un altro modo, però non so proprio come trattare le percentuali se non come double.

cionci
09-05-2010, 17:04
Una cosa del genre quindi ?

#include <iostream>


using namespace std;

void f(double percentualeDiPartenza_, double percentualeMassima_) {
int minimo, massimo;
double percentualeDiPartenzaInCentesimi = percentualeDiPartenza_*100.0L, percentualeMassimaInCentesimi = percentualeMassima_*100.0;
minimo = static_cast<int>(percentualeDiPartenzaInCentesimi);
massimo = static_cast<int>(percentualeMassimaInCentesimi);
cout << minimo << " " << massimo << endl;
}

int main()
{
double percentualeMassima_ = 0.5L - 0.4L;
f(0.0L, percentualeMassima_);
return 0;
}
A me stampa 10...

Dari
09-05-2010, 17:29
anche a me se lo metto in un programma separato (vedi primo post, il secondo pezzo di codice), boh, può dipendere da qualche opzione del compilatore? Ho -Wl,-O1 -L/usr/lib -lQtGui -lQtCore -lpthread (è un'applicazione Qt).

Aggiornamento: dopo cambio la rappresentazione interna dei valori in modo da minimizzare sottrazioni e altro.

cionci
09-05-2010, 17:35
Credo che dipenda tutto da cosa ritorna getParteNonDisponibile();

Dari
09-05-2010, 18:16
ParteNonDisponibile ha solo una sottrazione dentro, fra la costante 1.0L e un campo dati double con valori fissi perchè li inserisco interi da uno spinBox e li converto in double...volendo potrei cambiare la rappresentazione di tutte le percentuali da double a int ma sarebbe una porcheria, anche perchè i calcoli li devo fare in double lo stesso.

Adesso ho leggermente cambiato il codice, al posto di parteNonDisponibile() uso getParteDisponibile():

double getParteDisponibile() const {
return 0.5L - quotaAcquistata;
}

In pratica ho spostato li dentro la sottrazione che facevo.
Succede sempre la stessa cosa però.

cionci
09-05-2010, 18:25
Cambia poco, quotaAcquistata come lo calcoli ?

Dari
09-05-2010, 18:56
Ho risolto, il problema (non so perché) era nella lettura del valore di quotaAcquistata come int dallo spinBox, ho usato un doubleSpinBox (non avevo visto che c'era anche quello) e funziona tutto, grazie mille per l'assistenza!