Qual è il modo migliore per confrontare galleggianti per quasi-parità in Python?

È ben noto che il confronto di carri allegorici per la parità è un po ‘ complicato a causa dell’arrotondamento e precisione i problemi.

Per esempio:
https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/

Qual è il modo consigliato di fare con questo in Python?

Sicuramente c’è una funzione di libreria per questo da qualche parte?

Dal momento che le applicazioni e i vostri dati e il vostro dominio del problema-ed è solo una riga di codice, perché ci sarebbe una “libreria standard funzione”?
all, any, max, min sono ogni fondamentalmente one-liners, e non sono solo fornito in una libreria, sono builtin funzioni. Così il BDFL ragioni non sono che. La riga di codice che la maggior parte delle persone di scrittura è abbastanza semplice e spesso non funziona, che è un forte motivo per offrire qualcosa di meglio. Naturalmente qualsiasi modulo che altre strategie dovrebbe anche fornire avvertimenti che descrive quando sono adeguate, e, cosa più importante, quando non sono. Analisi numerica è difficile, non è una grande vergogna che i progettisti di linguaggi di solito non tentare di strumenti per aiutare con esso.
Jessop. Quelli di raccolta di funzioni orientate non hanno l’applicazione, i dati e il dominio del problema delle dipendenze float-point. Così il “one-liner” chiaramente non è così importante come i veri motivi. Analisi numerica è difficile, e non può essere una prima classe parte di un general-purpose biblioteca di lingua.
Probabilmente sarei d’accordo se la distribuzione di Python standard non è venuto con il più moduli per le interfacce XML. Chiaramente il fatto che le diverse applicazioni che hanno bisogno di fare qualcosa di diverso è alcuna barra di tutto per mettere i moduli in base a che fare, in un modo o in un altro. Certamente ci sono i trucchi per confrontare i carri che vengono re-utilizzato molto, più base di un determinato numero di ulps. Così sono d’accordo solo in parte – il problema è che l’analisi numerica è difficile. Python in linea di principio, fornire gli strumenti per rendere un po ‘ più facile, per qualche tempo. Credo che nessuno ha volontariamente.
Inoltre, “si riduce a un hard-to-linea di design di codice” – se è ancora un one-liner, una volta che si sta facendo correttamente, penso che il tuo monitor è più ampia di quella del mio ;-). Comunque, credo che l’intera zona è abbastanza specializzato, nel senso che più programmatori (me compreso) molto raramente utilizzata. Combinato con l’essere duro, non si sta andando a colpire la parte superiore del “most wanted” la lista per le librerie di base nella maggior parte delle lingue.

OriginaleL’autore Gordon Wrigley | 2011-04-08

12 risposte

  1. 197

    Python 3.5 aggiunge il matematica.inoltre vicino e cmath.inoltre vicino funzioni come descritto in PEP 485.

    Se si sta utilizzando una versione precedente di Python, la funzione equivalente è dato in documentazione.

    def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
        return abs(a-b) <= max(rel_tol * max(abs(a), abs(b)), abs_tol)

    rel_tol è una relativa tolleranza, è moltiplicato per il maggiore tra le grandezze delle due argomenti, come i valori di ottenere più grande, non così il permesso di differenza tra loro pur considerando loro pari.

    abs_tol è un’assoluta tolleranza che viene applicato così com’è in tutti i casi. Se la differenza è meno di uno di tali tolleranze, i valori sono considerati uguali.

    nota quando a o b è un numpy array, numpy.isclose funziona.
    è un relativa tolleranza, è moltiplicato per il maggiore tra le grandezze delle due argomenti, come i valori di ottenere più grande, non così il permesso di differenza tra loro pur considerando loro pari. abs_tol è un tolleranza assoluta che è applicato così com’è in tutti i casi. Se la differenza è meno di uno di tali tolleranze, i valori sono considerati uguali.
    Non per sminuire il valore di questa risposta, penso che sia una buona), vale la pena notare che la documentazione dice anche: “Modulo di controllo di errore, ecc, la funzione restituirà il risultato di…”, In altre parole, il isclose funzione (sopra) non è un completa di attuazione.
    Ci scusiamo per la riscoperta di un vecchio thread, ma mi sembrava che vale la pena sottolineare che isclose aderisce sempre alla meno conservativo criterio. Dico solo che il comportamento è contro di me. Io per specificare due criteri, vorrei aspetto sempre minore tolleranza al punto di sostituire la maggiore.
    hai diritto alla tua opinione, naturalmente, ma questo comportamento aveva perfettamente senso per me. Con la tua definizione, nulla potrebbe mai essere “vicino a” zero, a causa di una relativa tolleranza moltiplicato per zero è sempre zero.

    OriginaleL’autore Mark Ransom

  2. 53

    È qualcosa di così semplice come la seguente non è abbastanza buono?

    return abs(f1 - f2) <= allowed_error
    Come il link che ho fornito punti, sottraendo funziona solo se si conosce la approssimativo di grandezza dei numeri in anticipo.
    Questo è esattamente il motivo per cui non esiste una funzione di libreria. È sempre dipendente dalla effettiva del problema a portata di mano.
    non è una soluzione perfetta per tutte le circostanze, non è un buon motivo per non fornire una soluzione ragionevole per la maggior parte delle circostanze. Senza una tale soluzione, gli utenti finali sono di sinistra per utilizzare qualsiasi merda che sogno da loro stessi, come abs(f1 – f2) < allowed_error, che fallisce miseramente quando le grandezze di f1 e f2 non sapere in anticipo.
    si prega di non chiamare le risposte qui “merda.” Questo è offensivo e non risolve nulla. Presentare il vostro argomento e andare avanti.
    Nella mia esperienza, il metodo migliore per confrontare i carri è: abs(f1-f2) < tol*max(abs(f1),abs(f2)). Questo tipo di relativa tolleranza è l’unico modo sensato per confrontare i carri in generale, come sono di solito colpiti da errore di arrotondamento, in piccole cifre decimali.

    OriginaleL’autore Andrew White

  3. 29

    Direi che sono d’accordo che Gareth risposta è probabilmente il più appropriato, come una leggera funzione/soluzione.

    Ma ho pensato che sarebbe utile notare che se si utilizza NumPy o stanno prendendo in considerazione, c’è una confezionato funzione per questo.

    numpy.isclose(a, b, rtol=1e-05, atol=1e-08, equal_nan=False)

    Un po ‘ di disclaimer, però: l’installazione di NumPy può essere non banale esperienza a seconda della piattaforma.

    numpy.allclose (…) farà il trucco, bene.
    “L’installazione di numpy può essere non banale esperienza a seconda della piattaforma.”…um Cosa? Per quali piattaforme è “non banale” per installare numpy? Che cosa esattamente di fatto non banale?
    difficile ottenere un binario a 64 bit per Windows. Difficile ottenere numpy via pip su Windows.
    Lo faccio, ma alcuni dei miei studenti di usare Windows, per cui devo avere a che fare con questa roba.
    Se devi installare open data science piattaforma alimentato da Python, il modo migliore è Anaconda continuum.io/download (panda, numpy e più out of the box)

    OriginaleL’autore J.Makela

  4. 12

    Uso di Python decimale modulo, che prevede la Decimal classe.

    Dai commenti:

    Vale la pena notare che se sei
    matematica-lavoro pesante e non
    serve assolutamente la precisione dal
    decimale, questo può davvero bog cose
    giù. I carri sono modo più veloce per
    che fare con, ma imprecisa. I decimali sono
    estremamente preciso ma lento.

    OriginaleL’autore jathanism

  5. 11

    Io non sono a conoscenza di nulla in Python standard library (o altrove) che implementa Dawson’s AlmostEqual2sComplement funzione. Se questo è il tipo di comportamento che si desidera, sarà necessario implementare da soli. (Nel qual caso, piuttosto che utilizzando Dawson intelligente bit per bit hack si sarebbe probabilmente meglio utilizzare più convenzionali test del modulo if abs(a-b) <= eps1*(abs(a)+abs(b)) + eps2 o simili. Per ottenere Dawson comportamento si potrebbe dire qualcosa di simile if abs(a-b) <= eps*max(EPS,abs(a),abs(b)) per qualche piccolo fisso EPS; questo non è esattamente lo stesso come Dawson, ma è simile nello spirito.

    Non riesco a seguire molto quello che stai facendo qui, ma è interessante. Qual è la differenza tra eps, eps1, eps2 e EPS?
    eps1 e eps2 definire un parente e un’assoluta tolleranza: siete disposti a consentire a e b differire da circa eps1 volte quanto sono grandi più eps2. eps è un singolo di tolleranza; siete disposti a consentire a e b differire da circa eps volte quanto sono grandi, con la precisazione che nulla di dimensione EPS o più piccoli si presume essere di dimensioni EPS. Se si prende EPS di essere il più piccolo non denormal valore del tipo a virgola mobile, questo è molto simile a Dawson’s comparatore (tranne che per un fattore 2^#bit perché Dawson misure di tolleranza in ulps).
    Per inciso, sono d’accordo con S. Lott che la Cosa Giusta è sempre andando a dipendere sulla sua effettiva applicazione, che è il motivo per cui non c’è una funzione della libreria standard per tutti i tuoi virgola mobile a confronto le esigenze.
    Come si fa a determinare il “minimo non denormal valore del tipo a virgola mobile” per python?
    Questa pagina docs.python.org/tutorial/floatingpoint.html dice quasi tutti python implementazioni standard IEEE 754 precisione doppia carri allegorici e questa pagina en.wikipedia.org/wiki/IEEE_754-1985 dice che i numeri normalizzati più vicino a zero sono ±2**-1022.

    OriginaleL’autore Gareth McCaughan

  6. 8

    Comune di saggezza che i numeri a virgola mobile non può essere paragonato per la parità è impreciso. I numeri a virgola mobile non sono diversi da numeri interi: Se si valuta “a == b”, si aprirà vero, se sono identici numeri e false altrimenti (con la consapevolezza che due NaNs sono, naturalmente, non numeri identici).

    Il vero problema è questo: Se ho fatto alcuni calcoli e non sono sicuro che i due numeri devo confrontare sono esattamente corretto, allora che cosa? Questo problema è lo stesso per la virgola mobile, come per i numeri interi. Se si valuta l’intero espressione “7/3*3”, non risultano uguali “7*3/3”.

    Quindi suppongo che abbiamo chiesto “Come faccio a confrontare I numeri interi per la parità?” in una tale situazione. Non esiste un’unica risposta; cosa si dovrebbe fare in una determinata situazione, in particolare che tipo di errori che si ha e ciò che si vuole raggiungere.

    Qui sono alcune possibili scelte.

    Se si vuole ottenere un risultato “vero” se matematicamente esatta numeri sono uguali, allora si potrebbe provare a utilizzare le proprietà dei calcoli eseguiti per dimostrare che si ottiene gli stessi errori in due numeri. Se è fattibile, e si confronta due numeri che risultano dalle espressioni che potrebbero dare un numero uguale se calcolato esattamente, allora si otterrà il “vero” dal confronto. Un altro approccio è che si potrebbe analizzare le proprietà dei calcoli e dimostrare che l’errore non supera una certa cifra, forse un importo assoluto o di un importo relativo ad uno degli ingressi, o una delle uscite. In questo caso, si può chiedere se i due numeri calcolati differiscono al massimo di tale importo, e ritorna “vero” se sono all’interno dell’intervallo. Se non è possibile dimostrare un errore legati, si può immaginare e sperare per il meglio. Un modo di indovinare è quello di valutare molti campioni casuali e vedere che tipo di distribuzione che si ottiene nei risultati.

    Naturalmente, dal momento che solo impostare il requisito che si ottiene “true” se matematicamente esatta risultati sono uguali, abbiamo lasciato aperta la possibilità che si ottiene “vero”, anche se non sono uguali. (In realtà, siamo in grado di soddisfare il requisito, restituendo sempre “vero”. Questo rende il calcolo è semplice, ma è auspicabile, quindi mi limiterò a discutere per migliorare la situazione di seguito.)

    Se si desidera ottenere un “falso” risultato se matematicamente numeri esatti sarebbe impari, è necessario dimostrare che la valutazione dei numeri rendimenti numeri diversi se matematicamente numeri esatti sarebbe impari. Questo può essere impossibile per fini pratici, in molte situazioni comuni. Cerchiamo quindi di prendere in considerazione un’alternativa.

    Un utile requisito potrebbe essere che abbiamo un “falso” risultato se matematicamente esatta numeri differiscono di più di una certa quantità. Per esempio, forse stiamo andando a calcolare dove una palla gettata in un gioco per computer viaggiato, e vogliamo sapere se si ha colpito con una mazza. In questo caso, certamente si vuole ottenere “true” se la palla colpisce la mazza, e vogliamo essere “false” se la palla è lontana dalla bat, e siamo in grado di accettare un’errata risposta “true” se la palla in modo matematicamente esatta simulazione perdere mazza, ma è all’interno di un millimetro di colpire la mazza. In questo caso, abbiamo bisogno di dimostrare (o indovinare/stima) che il nostro calcolo della palla e la mazza la posizione di un errore combinato di più di un millimetro (per tutte le posizioni di interesse). Questo ci avrebbe permesso di tornare sempre “false” se la palla e mazza sono più di un millimetro di distanza, di tornare a “true” se toccano, e di tornare a “true” se sono abbastanza vicino per essere accettabile.

    Così, come si decide cosa per tornare quando si confrontano i numeri a virgola mobile dipende molto dalla situazione specifica.

    Come si va su di dimostrare limiti dell’errore di calcolo, che può essere un argomento complicato. Qualsiasi implementazione in virgola mobile che utilizza lo standard IEEE 754 in giro-a-vicino modalità restituisce il numero a virgola mobile più vicino al risultato esatto per qualsiasi operazione di base (in particolare, moltiplicazione, divisione, addizione, sottrazione, radice quadrata). (In caso di pareggio, giro in modo che il basso bit è ancora.) (Prestare particolare attenzione radice quadrata e la divisione; il linguaggio di implementazione potrebbe utilizzare metodi che non sono conformi allo standard IEEE 754 per quelli.) A causa di questo requisito, sappiamo che l’errore in un unico risultato è non più di 1/2 del valore del bit meno significativo. (Se fosse di più, l’arrotondamento si sarebbe andato ad un altro numero che è all’interno di 1/2 del valore).

    Andando via di là, diventa sostanzialmente più complicato; il passo successivo è l’esecuzione di un’operazione in cui uno degli ingressi ha già qualche errore. Espressioni semplici, questi errori possono essere seguiti attraverso il calcolo di un limite sul finale di errore. In pratica, questo è fatto solo in alcune situazioni, come ad esempio lavorare su un di alta qualità libreria matematica. E, naturalmente, è necessario un controllo preciso sopra esattamente quali operazioni vengono eseguite. Linguaggi di alto livello spesso il compilatore molto lento, si potrebbe non sapere l’ordine in cui le operazioni vengono eseguite.

    C’è molto di più che potrebbe essere (e lo è) scritto su questo argomento, ma mi devo fermare lì. In sintesi, la risposta è: non C’è nessuna libreria di routine per questo confronto perché non esiste un’unica soluzione che si adatta alle più svariate esigenze che vale la pena di mettere in una libreria di routine. (Se confrontando con un parente o un errore assoluto intervallo sufficiente per voi, potete farlo semplicemente senza una routine di libreria.)

    Dalla discussione di cui sopra con Gareth McCaughan, correttamente il confronto con un errore relativo essenzialmente ammonta a “abs(a-b) <= epsmax(2*-1022,abs(a),abs(b))”, che non è qualcosa che vorrei descrivere come semplice e certamente non è qualcosa che avrei lavorato da me. Anche Steve Jessop punti è di analoga complessità di max, min, qualsiasi e tutti, che sono tutti i comandi incorporati. Così fornendo un errore relativo a confronto con standard di matematica modulo sembra una buona idea.
    (7/3*3 == 7*3/3) restituisce True in python.
    Ho appena eseguito il Python 2.7.2 su OS X 10.8.3 e inserito (7/3*3 == 7*3/3). Stampate False.
    Probabilmente si è dimenticato di tipo from __future__ import division. Se non lo fai, non ci sono i numeri in virgola mobile, e il confronto è tra due numeri interi.
    Questa è una discussione importante, ma non incredibilmente utile.

    OriginaleL’autore Eric Postpischil

  7. 6

    Se si desidera utilizzare in testing/TDD contesto, direi che questo è un modo standard:

    from nose.tools import assert_almost_equals
    
    assert_almost_equals(x, y, places=7) #default is 7

    OriginaleL’autore volodymyr

  8. 4

    per la matematica.inoltre vicino() è stato aggiunto a Python 3.5 (il codice sorgente). Qui è un porto di Python 2. E ‘ differenza da un liner di Marco Riscatto è che è in grado di gestire “inf” e “-inf” correttamente.

    def isclose(a, b, rel_tol=1e-09, abs_tol=0.0):
        '''
        Python 2 implementation of Python 3.5 math.isclose()
        https://hg.python.org/cpython/file/tip/Modules/mathmodule.c#l1993
        '''
        # sanity check on the inputs
        if rel_tol < 0 or abs_tol < 0:
            raise ValueError("tolerances must be non-negative")
    
        # short circuit exact equality -- needed to catch two infinities of
        # the same sign. And perhaps speeds things up a bit sometimes.
        if a == b:
            return True
    
        # This catches the case of two infinities of opposite sign, or
        # one infinity and one finite number. Two infinities of opposite
        # sign would otherwise have an infinite relative tolerance.
        # Two infinities of the same sign are caught by the equality check
        # above.
        if math.isinf(a) or math.isinf(b):
            return False
    
        # now do the regular computation
        # this is essentially the "weak" test from the Boost library
        diff = math.fabs(b - a)
        result = (((diff <= math.fabs(rel_tol * b)) or
                   (diff <= math.fabs(rel_tol * a))) or
                  (diff <= abs_tol))
        return result

    OriginaleL’autore user2745509

  9. 4

    Ho trovato il seguente confronto utile:

    str(f1) == str(f2)
    è interessante, ma non molto pratico a causa di str(.1 + .2) == .3
    str(.1 + .2) == str(.3) restituisce True
    Come è diverso dalla f1 = f2 = — se entrambi sono vicini, ma ancora diverso a causa di precisione, le rappresentazioni di stringa sarà anche disuguali.
    .1 + .2 == .3 restituisce False, mentre str(.1 + .2) == str(.3) restituisce True

    OriginaleL’autore Kresimir

  10. 1

    Per alcuni casi in cui si può influenzare il numero di rappresentazione, si può rappresentare come frazioni invece dei carri allegorici, che utilizza intero numeratore e denominatore. In questo modo si può avere esattamente i confronti.

    Vedere Frazione da frazioni modulo per i dettagli.

    OriginaleL’autore eis

  11. 1

    Mi è piaciuto @Sesquipedal ‘s suggerimento, ma con la modifica (un caso d’uso quando entrambi i valori sono a 0 restituisce False). Nel mio caso ero in Python 2.7 e solo usato una semplice funzione:

    if f1 ==0 and f2 == 0:
        return True
    else:
        return abs(f1-f2) < tol*max(abs(f1),abs(f2))

    OriginaleL’autore IronYeti

  12. 0

    Questo forse è un po ‘brutto trucco, ma funziona abbastanza bene quando non ti serve più il valore predefinito di precisione” float (circa 11 decimali). Funziona bene su python 2.7.

    Il round_to funzione utilizza il formato metodo built-in str classe per arrotondare il galleggiante su una stringa che rappresenta il galleggiante con il numero di decimali necessari, e quindi si applica il eval built-in funzione arrotondati float string per tornare a galleggiante tipo numerico.

    Il is_close funzione applica solo una semplice condizionale arrotondati fino a galleggiante.

    def round_to(float_num, decimal_precision):
        return eval("'{:." + str(int(decimal_precision)) + "f}'.format(" + str(float_num) + ")")
    
    def is_close(float_a, float_b, decimal_precision):
        if round_to(float_a, decimal_precision) == round_to(float_b, decimal_precision):
            return True
        return False
    
    a = 10.0 / 3
    # Result: 3.3333333333333335
    b = 10.0001 / 3
    # Result: 3.3333666666666666
    
    print is_close(a, b, decimal_precision=4)
    # Result: False
    
    print is_close(a, b, decimal_precision=3)
    # Result: True

    OriginaleL’autore Albert Alomar

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *