2015-06-23 11 views
21

Capisco in che modo le operazioni matematiche matematicamente equivalenti possono produrre risultati diversi a causa di errori numerici (ad esempio, sommare i float in ordini diversi).Comportamento strano numpy.sum quando si aggiungono gli zeri

Tuttavia, mi sorprende che l'aggiunta di zeri a sum può modificare il risultato. Ho pensato che questo vale sempre per i galleggianti, non importa quale: x + 0. == x.

Ecco un esempio. Mi aspettavo che tutte le linee fossero esattamente a zero. Qualcuno può spiegare perché questo accade?

M = 4 # number of random values 
Z = 4 # number of additional zeros 
for i in range(20): 
    a = np.random.rand(M) 
    b = np.zeros(M+Z) 
    b[:M] = a 
    print a.sum() - b.sum() 

-4.4408920985e-16 
0.0 
0.0 
0.0 
4.4408920985e-16 
0.0 
-4.4408920985e-16 
0.0 
0.0 
0.0 
0.0 
0.0 
0.0 
0.0 
0.0 
2.22044604925e-16 
0.0 
4.4408920985e-16 
4.4408920985e-16 
0.0 

Sembra non accada per i valori minori di M e Z.

Mi sono anche assicurato che a.dtype==b.dtype.

Ecco un altro esempio, che dimostra anche incorporate sum si comporta di pitone come previsto:

a = np.array([0.1,  1.0/3,  1.0/7,  1.0/13, 1.0/23]) 
b = np.array([0.1, 0.0, 1.0/3, 0.0, 1.0/7, 0.0, 1.0/13, 1.0/23]) 
print a.sum() - b.sum() 
=> -1.11022302463e-16 
print sum(a) - sum(b) 
=> 0.0 

sto usando v1.9.2 NumPy.

+5

Posso riprodurre con 1.9.2, ma non con 1.6.1. La mia ipotesi sarebbe che l'array più lungo in qualche modo fa sì che gli elementi vengano aggiunti in un ordine diverso, ad esempio per facilitare SMID. – amaurea

+1

Anche con '' math.fsum() '', che è diverso da '' sum() '', sembra non accadere (almeno in 2.7.x). –

+3

Mi azzarderei a indovinare che è la sommatoria pairwise, https://github.com/numpy/numpy/pull/3685 –

risposta

7

Risposta breve: Stai visualizzando la differenza tra

a + b + c + d 

e

(a + b) + (c + d) 

che a causa di imprecisioni in virgola mobile non è la stessa.

Risposta lunga: Numpy implementa la sommatoria in coppia come ottimizzazione di entrambe le velocità (consente una più facile vettorizzazione) e errore di arrotondamento.

L'implementazione di somma numpica può essere trovata here (funzione [email protected]@). In pratica fa quanto segue:

  1. Se la lunghezza dell'array è inferiore a 8, viene eseguita una sommatoria di ciclo regolare. Questo è il motivo per cui lo strano risultato non viene osservato se W < 4 nel tuo caso - la stessa sommazione for-loop verrà utilizzata in entrambi i casi.
  2. Se la lunghezza è compresa tra 8 e 128, accumula le somme in 8 scomparti r[0]-r[7] quindi li somma ((r[0] + r[1]) + (r[2] + r[3])) + ((r[4] + r[5]) + (r[6] + r[7])).
  3. In caso contrario, somma in modo ricorsivo due metà dell'array.

Pertanto, nel primo caso si ottiene a.sum() = a[0] + a[1] + a[2] + a[3] e nel secondo caso b.sum() = (a[0] + a[1]) + (a[2] + a[3]) che porta a a.sum() - b.sum() != 0.