Domanda Dove sono le inesattezze in math.sqrt () e math.pow () provenienti da grandi numeri? [duplicare]


Questa domanda ha già una risposta qui:

Se prendi un numero, prendi la sua radice quadrata, rilascia il decimale e poi lo alzi alla seconda potenza, il risultato dovrebbe sempre essere inferiore o uguale al numero originale.

Questo sembra essere vero in python fino a quando non lo provi 99999999999999975425 per qualche ragione.

import math

def check(n):
    assert math.pow(math.floor(math.sqrt(n)), 2) <= n

check(99999999999999975424)  # No exception.
check(99999999999999975425)  # Throws AssertionError.

Sembra math.pow(math.floor(math.sqrt(99999999999999975425)), 2) ritorna 1e+20.

Presumo che questo abbia qualcosa a che fare con il modo in cui memorizziamo i valori in python ... qualcosa relativo all'aritmetica in virgola mobile, ma non posso ragionare su come specificamente ciò influisce su questo caso.


11
2018-01-22 01:21


origine


risposte:


Stai supervolmente complicando la tua risposta. Il problema non è in realtà sqrt o pow, il problema è che stai usando numeri più grandi di quelli in virgola mobile che possono rappresentare precisamente. L'aritmetica in virgola mobile IEEE a 64 bit standard non può rappresentare ogni valore intero oltre i 52 bit (più un bit di segno).

Prova a convertire i tuoi input in float e di nuovo:

>>> int(float(99999999999999975424))
99999999999999967232
>>> int(float(99999999999999975425))
99999999999999983616

Come puoi vedere, il valore rappresentativo è saltato nel 16384. Il primo passo in math.sqrt sta convertendo in float (C double), e in quel momento, il tuo valore è aumentato del necessario per rovinare il risultato finale.

Versione breve: float non può rappresentare precisamente interi di grandi dimensioni. Uso decimal se hai bisogno di maggiore precisione.


21
2018-01-22 01:34



A differenza delle richieste di risposta di Evan Rose (ora cancellate), ciò non è dovuto a un valore epsilon nell'algoritmo sqrt.

Maggior parte math le funzioni del modulo assegnano i loro input a float, e math.sqrt è uno di loro.

99999999999999975425 non può essere rappresentato come un float. Per questo input, il cast produce un float con un valore numerico esatto 99999999999999983616, che repr mostra come 9.999999999999998e+19:

>>> float(99999999999999975425)
9.999999999999998e+19
>>> int(_)
99999999999999983616L

Il galleggiante più vicino alla radice quadrata di questo numero è 10000000000.0e questo è quello che math.sqrt ritorna.


8
2018-01-22 01:33