Sia N
un intero senza segno di tempo di compilazione.moltiplicazione in virgola mobile contro aggiunta ripetuta
GCC può ottimizzare
unsigned sum = 0;
for(unsigned i=0; i<N; i++) sum += a; // a is an unsigned integer
semplicemente a*N
. Questo può essere compreso dal momento che l'aritmetica modulare dice (a%k + b%k)%k = (a+b)%k
.
Tuttavia GCC non ottimizzerà
float sum = 0;
for(unsigned i=0; i<N; i++) sum += a; // a is a float
al a*(float)N
.
Ma utilizzando la matematica associativa con ad es. -Ofast
Ho scoperto che GCC può ridurre questo in ordine ai passi log2(N)
. E.g per N=8
può fare la somma in tre aggiunte.
sum = a + a
sum = sum + sum // (a + a) + (a + a)
sum = sum + sum // ((a + a) + (a + a)) + ((a + a) + (a + a))
Anche se un certo punto dopo N=16
GCC torna a fare N-1
somme.
La mia domanda è perché GCC non fa a*(float)N
con -Ofast
?
Invece di essere O(N)
o O(Log(N))
potrebbe essere semplicemente O(1)
. Poiché lo N
è noto al momento della compilazione, è possibile determinare se N
si adatta a un valore float. E anche se N
è troppo grande per un float, potrebbe fare sum =a*(float)(N & 0x0000ffff) + a*(float)(N & ffff0000)
. In effetti, ho fatto un piccolo test per verificare la precisione e lo a*(float)N
è più preciso in ogni caso (vedi il codice e i risultati sotto).
//gcc -O3 foo.c
//don't use -Ofast or -ffast-math or -fassociative-math
#include <stdio.h>
float sumf(float a, int n)
{
float sum = 0;
for(int i=0; i<n; i++) sum += a;
return sum;
}
float sumf_kahan(float a, int n)
{
float sum = 0;
float c = 0;
for(int i=0; i<n; i++) {
float y = a - c;
float t = sum + y;
c = (t -sum) - y;
sum = t;
}
return sum;
}
float mulf(float a, int n)
{
return a*n;
}
int main(void)
{
int n = 1<<24;
float a = 3.14159;
float t1 = sumf(a,n);
float t2 = sumf_kahan(a,n);
float t3 = mulf(a,n);
printf("%f %f %f\n",t1,t2,t3);
}
Il risultato è 61848396.000000 52707136.000000 52707136.000000
che dimostra che la moltiplicazione e la Kahan summation hanno lo stesso risultato che ritengo mostra che la moltiplicazione è più accurata rispetto alla semplice somma.
Avete considerato che tre operazioni di aggiunta possono essere più veloci di un multiplo in virgola mobile? Ciò sarebbe coerente con il ritorno a un multiplo di FP a N = 16. – Ian
Non esegue l'ottimizzazione perché l'ottimizzazione non è valida; in generale produce un risultato diverso. L'aritmetica in virgola mobile non obbedisce alle normali proprietà aritmetiche che ci si aspetta come associatività o legge distributiva. La moltiplicazione non è un'aggiunta ripetuta. –
@R ..: Lui (implicitamente) usa '-ffast-math' che * fa * consente quel tipo di ottimizzazioni. http://stackoverflow.com/questions/7420665/what-does-gccs-ffast-math-actually-do –