简体   繁体   中英

Precision of calculations in Python 3

In Python I get an unexpected result of a simple calculation:

100 - 123.123

Returns:
-23.123000000000005

I expected -23.123 as output.

So I tried:

import numpy as np
np.float64(100) - np.float64(123.123)

Returns:
-23.123000000000005

I expected -23.123 as output.

Any suggestions how to get more precise / accurate results?

You have the definition of this effect in the python doc:

https://docs.python.org/2/tutorial/floatingpoint.html

you can format your result at the print to have the 3 digits you want:

print("%.3f" % np.float64(100) - np.float64(123.123))

Any suggestions how to get more precise / accurate results ?

Sure, several options ready for re-use.

The problem is not in how a number looks, but how it is technically represented "inside" the process of computing. IEEE-standard specification for floating point number-representations are technically quite "short" as soon visible in some just mildly deep iterative number (re-)processing and thus python , as well as other languages, has extended precision number representation + processing tools ( arbitrary precision mathematics ) - take o look :

decimal module : my beloved one

a few years ago I did some indeed extended precision computing ( beyond 1000 decimal places, due to very ugly conditioned problem, using pure-decimal functions with making even both the numpy arrays ( as a storage-class ) + scipy.optimize.fmin_l_bfgs() solvers keep working using these and also to remain within these extended-precisions ... cool )

import decimal

decX = np.asarray( ( decimal.Decimal(  3.4 ), decimal.Decimal(  3.5 ), decimal.Decimal(  3.7 ), decimal.Decimal(   4.3 ), ) )
decY = np.asarray( ( decimal.Decimal( 65   ), decimal.Decimal( 85   ), decimal.Decimal( 97   ), decimal.Decimal( 100   ), ) )

def pure_dec_LSQ_5DoF( decCTX,                                                Xopt,                                                decX_measured,                            decY_measured ):                            # [PERF] ~ 2400 [us] @ .prec =   14
    return decCTX.add( decCTX.add( decCTX.power( decCTX.subtract( decCTX.fma( Xopt[0], decCTX.power( Xopt[4], decCTX.fma( Xopt[1], decX_measured[0], Xopt[2] ) ), Xopt[3] ), decY_measured[0] ), decimal.Decimal( 2 ) ), #        ~ 2800 [us] @ .prec =   28
                                   decCTX.power( decCTX.subtract( decCTX.fma( Xopt[0], decCTX.power( Xopt[4], decCTX.fma( Xopt[1], decX_measured[1], Xopt[2] ) ), Xopt[3] ), decY_measured[1] ), decimal.Decimal( 2 ) )  #        ~ 7700 [us] @ .prec =  100
                                   ),                                                                                                                                                                                    #        ~ 1340 [ms] @ .prec = 1000
                       decCTX.add( decCTX.power( decCTX.subtract( decCTX.fma( Xopt[0], decCTX.power( Xopt[4], decCTX.fma( Xopt[1], decX_measured[2], Xopt[2] ) ), Xopt[3] ), decY_measured[2] ), decimal.Decimal( 2 ) ), #
                                   decCTX.power( decCTX.subtract( decCTX.fma( Xopt[0], decCTX.power( Xopt[4], decCTX.fma( Xopt[1], decX_measured[3], Xopt[2] ) ), Xopt[3] ), decY_measured[3] ), decimal.Decimal( 2 ) )  #
                                   )
                       )

Sure, 1000 decimal places are a lot, yet one pays with time to let the numerical process be executed down to these depths, without lossing precision.

>>> with decimal.localcontext() as locCTX:
       ...      for aPREC in range( 20, 31 ):
       ...          locCTX.prec = aPREC
       ...          ( pure_dec_LSQ_5DoF( locCTX,
                                         dec_fmin_x0_SEARCH_TRIM_TO_BE_PRECISE,
                                         decX,
                                         decY
                                         ),
                      pure_dec_RESi(     locCTX,
                                         dec_fmin_x0_SEARCH_TRIM_TO_BE_PRECISE,
                                         decX,
                                         decY
                                         )
                      )
       ...
       (Decimal('0.038471115298826195147'),           (Decimal('0.023589050081780503'),           Decimal('-0.082605913918299990'),           Decimal('0.150647690402532134'),           Decimal('-0.091630826566012630')))
       (Decimal('0.0384711152988261953165'),          (Decimal('0.0235890500817804889'),          Decimal('-0.0826059139182999933'),          Decimal('0.1506476904025321349'),          Decimal('-0.0916308265660126301')))
       (Decimal('0.03847111529882619531420'),         (Decimal('0.02358905008178048823'),         Decimal('-0.08260591391829999331'),         Decimal('0.15064769040253213501'),         Decimal('-0.09163082656601263007')))
       (Decimal('0.038471115298826195324048'),        (Decimal('0.023589050081780488368'),        Decimal('-0.082605913918299993309'),        Decimal('0.150647690402532135021'),        Decimal('-0.091630826566012630071')))
       (Decimal('0.0384711152988261953231489'),       (Decimal('0.0235890500817804883582'),       Decimal('-0.0826059139182999933087'),       Decimal('0.1506476904025321350199'),       Decimal('-0.0916308265660126300707')))
       (Decimal('0.03847111529882619532322276'),      (Decimal('0.02358905008178048835950'),      Decimal('-0.08260591391829999330863'),      Decimal('0.15064769040253213501998'),      Decimal('-0.09163082656601263007070')))
       (Decimal('0.038471115298826195323213788'),     (Decimal('0.023589050081780488359358'),     Decimal('-0.082605913918299993308625'),     Decimal('0.150647690402532135019974'),     Decimal('-0.091630826566012630070702')))
       (Decimal('0.0384711152988261953232136753'),    (Decimal('0.0235890500817804883593541'),    Decimal('-0.0826059139182999933086251'),    Decimal('0.1506476904025321350199740'),    Decimal('-0.0916308265660126300707023')))
       (Decimal('0.03847111529882619532321367314'),   (Decimal('0.02358905008178048835935336'),   Decimal('-0.08260591391829999330862505'),   Decimal('0.15064769040253213501997413'),   Decimal('-0.09163082656601263007070231')))
       (Decimal('0.038471115298826195323213665675'),  (Decimal('0.023589050081780488359353229'),  Decimal('-0.082605913918299993308625043'),  Decimal('0.150647690402532135019974132'),  Decimal('-0.091630826566012630070702306')))
       (Decimal('0.0384711152988261953232136649869'), (Decimal('0.0235890500817804883593532187'), Decimal('-0.0826059139182999933086250437'), Decimal('0.1506476904025321350199741307'), Decimal('-0.0916308265660126300707023064')))

Was able to evolve the search process to a minimum like this:

dec_fmin_x0_SEARCH_ADAPTIVE = np.asarray( (                     decimal.Decimal( -101000000010553.05594055493064099456356276561617988943684402001075635                                                                                                                                                                                        ),
                                                                decimal.Decimal(               -8.660605201193546246                                                                                                                                                                                                                           ),
                                                                decimal.Decimal(                0.00021842459768549                                                                                                                                                                                                                            ),
                                                                decimal.Decimal(               99.9259163119085989057939988625810620201012857893012816197730189907743792931209843327426339987914746365315172977942868845721827684076717423116961495794648319380554868846324870276029626886129186998300662535940937605435069739237317269895772  ),
                                                                decimal.Decimal(                2.64971757369295002249999999827154484100152060917026952223212241653783649669777780217778380697777777796977777777969777777777969777777779697777777796977777805877778058777780587777777800577777780057777778005777777800577777774817774778285740 ),
                                                                )
                                           )

mpmath module :

import mpmath

def fmp_5DoF( Xopt, matX_measured , matY_measured ):
    return ( ( ( Xopt[0] * mpmath.power( Xopt[4], ( Xopt[1] * matX_measured[0] + Xopt[2] ) ) + Xopt[3] ) - matY_measured[0] )**2
           + ( ( Xopt[0] * mpmath.power( Xopt[4], ( Xopt[1] * matX_measured[1] + Xopt[2] ) ) + Xopt[3] ) - matY_measured[1] )**2
           + ( ( Xopt[0] * mpmath.power( Xopt[4], ( Xopt[1] * matX_measured[2] + Xopt[2] ) ) + Xopt[3] ) - matY_measured[2] )**2
           + ( ( Xopt[0] * mpmath.power( Xopt[4], ( Xopt[1] * matX_measured[3] + Xopt[2] ) ) + Xopt[3] ) - matY_measured[3] )**2
             )
>>> fmp_5DoF( [ -1.01e+14, -8.44017374, -7.84602606e-4, 9.99261072e+1, 2.71792286 ], matX, matY )
mpf('0.038471245878352295')

>>> mpmath.mp.prec
53

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM