Newsgroup: it.comp.lang.c
Titolo: Problemaccio assurdo (float in modalità debug e release)

Salve,
uso il Visual C/C++ v7.1.3088 (.NET 2003). Sto lavorando su di un
programma mezzo C mezzo C++.
A dire la verità è proprio una schifezza di codice, nel senso della
puluzia ecc...
Perciò non so quanti bug possano esserci. Tuttavia, ci lavoro da molto
tempo, e di debug ne ho fatto veramente molto.
Il programma assegna un valore float ad una matrice NxN.
Ho 4 matrici che, in teoria, dovrebbero avere lo stesso identico
coefficiente float.
Mentre in modalità release questo è vero (come prova, uso la printf()
e li stampo tutti e quattro), in modalità debug differiscono. Invece
di quattro volte 66.074173, ottengo due volte 66.074173 e due volte
66.074181, il che è *assolutamente* inaccettabile, sbagliato, errato,
incomprensibile.
Dove può annidarsi l'errore secondo voi???
Problemi di memoria??
A me un uso sbagliato di memoria, in questo caso, sembra strano che
possa produrre problemi ai calcoli in virgola mobile...mah
Cosa può essere secondo voi?
Help!!!!

Ho estrapolato il programma C. L'ho compilato e NON funziona, nel senso
suddetto (ma compila e parte ovviamente :)
Normalmente, non dovremmo aspettarci che i calcoli in virgola mobile
diano gli stessi risultati sia in modalità release che in modalità
debug ?!?!??!?!?!?
Questa cosa mi fa scervelare non poco....
La funzione noju() calcola, per ognuna delle quattro matrici
(scacchiere), il centro di massa e la varianza delle pedine rispetto al
centro di massa.
Provate a compilare il prog. con il vostro Visual C e ditemi se
ottenete risultati diversi tra la modalità debug e release.
Oppure ditemi xchè non sarebbe un bug....
Ciao
----------------------------------------
#include	<stdlib.h>
#include	<stdio.h>
#define		FABS(x)		((x) >= 0 ? (x) : (-(x)))
#define		SQR(x)		((x)*(x))
#define CASELLAOCCUPATA 1
#define CASELLAVUOTA -1
#define ROWS 15
#define COLS 15
int	scacchiera[ROWS][COLS];
//
//
//
int		ContaPedine()
{
	int		count = 0,i,j;
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			if (scacchiera[i][j] == CASELLAOCCUPATA)
				count++;
		}
	}
	return	count;
}
//
//
//
int		NoJu()
{
	int		h,k,i,j;
	int		pedine;
	float		var = 0.0f;
	float		com[2];
	for ( h =0 ; h < ROWS; h++)
	{
		for (k = 0; k < COLS; k++)
		{
			scacchiera[h][k] = CASELLAOCCUPATA;
		}
	}
	//scacchiera[ROWS/2][COLS/2] = CASELLAVUOTA;
	scacchiera[ROWS/2-1][COLS/2] = CASELLAVUOTA;
	scacchiera[ROWS/2-2][COLS/2] = CASELLAVUOTA;
         // conta il numero di caselle occupata
 	pedine = ContaPedine();
	//StampaScacchiera();
	//printf("\n");
	com[0] = 0.0f;
	com[1] = 0.0f;
	// Calcola in COM
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			com[0] += (float)j;
			com[1] += (float)i;
		}
	}
	com[0] /= (float)pedine;
	com[1] /= (float)pedine;
	// Calcola la varianza
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			if (scacchiera[i][j] == CASELLAOCCUPATA)
			{
				//var += sqr((fabs((float)j - com[0]) + fabs((float)i - com[1])));
				var += SQR((FABS((float)j - com[0]) + FABS((float)i - com[1])));
			}
		}
	}
	var /= (float)(pedine - 1);
	printf("var [%f]\n", var);
	scacchiera[ROWS/2-1][COLS/2] = CASELLAOCCUPATA;
	scacchiera[ROWS/2-2][COLS/2] = CASELLAOCCUPATA;
	scacchiera[ROWS/2][COLS/2-1] = CASELLAVUOTA;
	scacchiera[ROWS/2][COLS/2-2] = CASELLAVUOTA;
	pedine = ContaPedine();
	//StampaScacchiera();
	//printf("\n");
	var = 0.0f;
	com[0] = 0.0f;
	com[1] = 0.0f;
	// Calcola in COM
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			com[0] += (float)j;
			com[1] += (float)i;
		}
	}
	com[0] /= (float)pedine;
	com[1] /= (float)pedine;
	// Calcola la varianza
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			if (scacchiera[i][j] == CASELLAOCCUPATA)
			{
				//var += sqr((fabs((float)j - com[0]) + fabs((float)i - com[1])));
				var += SQR((FABS((float)j - com[0]) + FABS((float)i - com[1])));
			}
		}
	}
	var /= (float)(pedine - 1);
	printf("var [%f]\n", var);
	scacchiera[ROWS/2][COLS/2-1] = CASELLAOCCUPATA;
	scacchiera[ROWS/2][COLS/2-2] = CASELLAOCCUPATA;
	scacchiera[ROWS/2][COLS/2+1] = CASELLAVUOTA;
	scacchiera[ROWS/2][COLS/2+2] = CASELLAVUOTA;
	pedine = ContaPedine();
	//StampaScacchiera();
	//printf("\n");
	var = 0.0f;
	com[0] = 0.0f;
	com[1] = 0.0f;
	// Calcola in COM
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			com[0] += (float)j;
			com[1] += (float)i;
		}
	}
	com[0] /= (float)pedine;
	com[1] /= (float)pedine;
	// Calcola la varianza
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			if (scacchiera[i][j] == CASELLAOCCUPATA)
			{
				//var += sqr((fabs((float)j - com[0]) + fabs((float)i - com[1])));
				var += SQR((FABS((float)j - com[0]) + FABS((float)i - com[1])));
			}
		}
	}
	var /= (float)(pedine - 1);
	printf("var [%f]\n", var);
	scacchiera[ROWS/2][COLS/2+1] = CASELLAOCCUPATA;
	scacchiera[ROWS/2][COLS/2+2] = CASELLAOCCUPATA;
	scacchiera[ROWS/2+1][COLS/2] = CASELLAVUOTA;
	scacchiera[ROWS/2+2][COLS/2] = CASELLAVUOTA;
	pedine = ContaPedine();
	//StampaScacchiera();
	//printf("\n");
	var = 0.0f;
	com[0] = 0.0f;
	com[1] = 0.0f;
	// Calcola in COM
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			com[0] += (float)j;
			com[1] += (float)i;
		}
	}
	com[0] /= (float)pedine;
	com[1] /= (float)pedine;
	// Calcola la varianza
	for (i = 0; i < ROWS; i++)
	{
		for (j = 0; j < COLS; j++)
		{
			if (scacchiera[i][j] == CASELLAOCCUPATA)
			{
				//var += sqr((fabs((float)j - com[0]) + fabs((float)i - com[1])));
				var += SQR((FABS((float)j - com[0]) + FABS((float)i - com[1])));
			}
		}
	}
	var /= (float)(pedine - 1);
	printf("var [%f]\n", var);
	getc(stdin);
	return	0;
}
//
//
//
int		main()
{
	NoJu();
	return	0;
}

 incomprensibile.
sinceramente non ci vedo proprio niente di strano!
Il risultato e' perfettamente accettabile e corretto,
anche se ovviamente, non essendo disponibile il codice non posso
escludere che tu non ci abbia messo qualche bug.
I probabili ingredienti del pasticcio sono almeno 2:
1) come dovresti sapere, i float hanno una precisione che fa schifo.
(In particolare secondo me non dovrebbe usarli nessuno,
se non per motivi di spazio, tipo se devi memorizzarne 100milioni, e 
solo dopo aver passato un esame di Calcolo Numerico :-) ).
Hanno una precisione di circa 1 parte su 10^7.
Per l'appunto 66.074181/(66.074181-66.074173) fa circa 8.26*10^6,
quindi e' probabile che i due numeri di cui sopra differiscano
per 1 bit. Quindi di fatto sono praticamente uguali.
2)  Perche' due numeri calcolati
nello stesso modo possono differire tra debug e release?
Non conosco nello specifico il VC, ma
tipicamente tra debug e release verranno introdotte
delle ottimizzazioni nella compilazione.
Per motivi suoi (ottimizzazione della gestione
dei registri, per esempio) il compilatore
puo' decidere di cambiare l'ordine di certe
operazioni. Tieni presente che in aritmetica di
macchina (quella usata dal calcolatore) non valgono
molte delle proprieta' che invece valgono nei calcoli
"esatti" tra numeri reali. Per esempio, la proprieta'
associativa non vale. Cioe' non e' affatto detto che
dati 3 numeri float o double a,b,c, valga
(a+b)+c == a+(b+c).
Per capirlo in modo macroscopico, senza andare a vedere l'ultimo bit,
basta prendere 3 float
che valgono
a =  1*10^(-9)
b =  1.0
c = -1.0
In un caso la somma fara' 0 e nell'altro 1*10^(-9).
Io, in un codice che mi fece penare non poco, calcolavo
in due punti del programma mi sembra il valore (x-y)^2+(w-z)^2, con
la stessa formula e con gli stessi valori (le stesse
variabili!): i due valori differivano....
Altri ti potranno dire eventuali altre cose relative al VC.
Io, se sei interessato a conoscere gioie e dolori di float e compagnia, 
ti consiglio di leggere questo documento:
http://www.physics.ohio-state.edu/~dws/grouplinks/floating_point_math.pdf
ciao,
g.

Ciao, tutto quello che hai scritto è verissimo. Infatti, modificando
un opzione del compilatore, ottengo finalmente gli stessi risultati,
sia in modalità release, che in modalità debug.
Io avrei sottolineato questa cosa con un bel warning però...
In ogni caso, l'uso dei double eliminava il problema a priori...
Sapevo i problemi dell'aritmetica finita, ma non avevo pensato al fatto
che il codice macchina fosse riodinato...errore mio!!!
Grazie