15. Լողացող կետի թվաբանություն. խնդիրներ և սահմանափակումներ

Լողացող կետով թվերը համակարգչային տեխնիկայում ներկայացված են որպես բազային 2 (երկուական) կոտորակներ: Օրինակ՝ տասնորդական կոտորակը

ունի 1/10 + 2/100 + 5/1000 արժեք, և նույն կերպ երկուական կոտորակը

ունի 0/2 + 0/4 + 1/8 արժեք։ Այս երկու կոտորակներն ունեն միանման արժեքներ, միակ իրական տարբերությունն այն է, որ առաջինը գրված է 10 կոտորակային նշումով, իսկ երկրորդը՝ 2 հիմքով:

Ցավոք սրտի, տասնորդական կոտորակների մեծ մասը չի կարող ճիշտ ներկայացվել որպես երկուական կոտորակներ: Հետևանքն այն է, որ, ընդհանուր առմամբ, տասնորդական լողացող կետային թվերը, որոնք դուք մուտքագրում եք, մոտավոր են միայն մեքենայում իրականում պահվող երկուական լողացող թվերով:

Խնդիրը սկզբում ավելի հեշտ է հասկանալ 10 հիմքում: Դիտարկենք 1/3 կոտորակը: Դուք կարող եք մոտավորել այն որպես բազային 10 կոտորակ.

կամ, ավելի լավ,

կամ, ավելի լավ,

եւ այլն։ Անկախ նրանից, թե քանի թվանշան եք պատրաստ գրի առնել, արդյունքը երբեք չի լինի ճիշտ 1/3, այլ կլինի ավելի լավ մոտավոր 1/3:

Նույն կերպ, անկախ նրանից, թե քանի բազային նիշ եք ցանկանում օգտագործել, տասնորդական 2 արժեքը չի կարող ներկայացվել հենց որպես բազային 0.1 կոտորակ: 2-րդ հիմքում 2/1-ը անվերջ կրկնվող կոտորակն է

Կանգնեք ցանկացած վերջավոր թվով բիթերի վրա, և դուք ստանում եք մոտավորություն: Այսօրվա մեքենաների մեծ մասում լողացողները մոտավոր են՝ օգտագործելով երկուական կոտորակը, որի համարիչը օգտագործում է առաջին 53 բիթը՝ սկսած ամենակարևոր բիթից և հայտարարը որպես երկու ուժ: 1/10-ի դեպքում երկուական կոտորակն է 3602879701896397 / 2 ** 55 որը մոտ է, բայց ոչ ճիշտ հավասար 1/10-ի իրական արժեքին:

Շատ օգտատերեր տեղյակ չեն մոտավորության մասին արժեքների ցուցադրման ձևի պատճառով: Python-ը տպում է միայն մեքենայի կողմից պահվող երկուական մոտարկման իրական տասնորդական արժեքի տասնորդական մոտարկումը: Մեքենաների մեծ մասում, եթե Python-ը տպեր 0.1-ի համար պահվող երկուական մոտավորության իրական տասնորդական արժեքը, այն պետք է ցուցադրեր

>>>

Դա ավելի շատ թվեր է, քան մարդկանց մեծամասնությունը համարում է օգտակար, ուստի Python-ը կառավարելի է պահում թվերի քանակը՝ փոխարենը ցուցադրելով կլորացված արժեք:

>>>

Պարզապես հիշեք, թեև տպված արդյունքը նման է 1/10-ի ճշգրիտ արժեքին, իրական պահված արժեքը ամենամոտ ներկայացվող երկուական կոտորակն է:

Հետաքրքիր է, որ կան բազմաթիվ տարբեր տասնորդական թվեր, որոնք կիսում են նույն մոտավոր մոտավոր երկուական կոտորակը: Օրինակ՝ թվերը 0.1 և 0.10000000000000001 և 0.1000000000000000055511151231257827021181583404541015625 բոլորը մոտավոր են 3602879701896397 / 2 ** 55. Քանի որ այս բոլոր տասնորդական արժեքներն ունեն նույն մոտավորությունը, դրանցից որևէ մեկը կարող է ցուցադրվել՝ պահպանելով անփոփոխությունը: eval(repr(x)) == x.

Պատմականորեն, Python-ի հուշումը և ներկառուցվածը repr() ֆունկցիան կընտրի 17 նշանակալի թվանշան ունեցողը, 0.10000000000000001. Սկսած Python 3.1-ից, Python-ը (համակարգերի մեծ մասում) այժմ կարող է ընտրել դրանցից ամենակարճը և պարզապես ցուցադրել 0.1.

Նկատի ունեցեք, որ սա երկուական լողացող կետի էության մեջ է. սա Python-ի վրիպակ չէ և ձեր կոդում նույնպես սխալ չէ: Դուք կտեսնեք նույն բանը բոլոր լեզուներում, որոնք աջակցում են ձեր սարքաշարի լողացող կետի թվաբանությունը (չնայած որոշ լեզուներ կարող են ոչ ցուցադրել տարբերությունը լռելյայն կամ բոլոր ելքային ռեժիմներում):

Ավելի հաճելի արդյունք ստանալու համար դուք կարող եք օգտագործել լարային ձևաչափում՝ սահմանափակ թվով նշանակալի թվանշաններ արտադրելու համար.

>>>

Կարևոր է գիտակցել, որ սա իրական իմաստով պատրանք է. դուք պարզապես կլորացնում եք ցուցադրել մեքենայի իրական արժեքից:

Մի պատրանքը կարող է ծնել մյուսը: Օրինակ, քանի որ 0.1-ը հենց 1/10-ը չէ, 0.1-ի երեք արժեքների գումարումը չի կարող ճշգրիտ 0.3 ստանալ:

>>>

Բացի այդ, քանի որ 0.1-ը չի կարող մոտենալ 1/10-ի ճշգրիտ արժեքին, իսկ 0.3-ը չի կարող մոտենալ 3/10-ի ճշգրիտ արժեքին, ապա նախապես կլորացնելով. round() գործառույթը չի կարող օգնել.

>>>

Թեև թվերը չեն կարող ավելի մոտ լինել իրենց նախատեսված ճշգրիտ արժեքներին, round() ֆունկցիան կարող է օգտակար լինել հետկլորացման համար, որպեսզի ոչ ճշգրիտ արժեքներով արդյունքները համեմատելի լինեն միմյանց հետ.

>>>

Երկուական լողացող կետով թվաբանությունը նման շատ անակնկալներ է պարունակում: «0.1»-ի խնդիրը մանրամասնորեն բացատրվում է ստորև՝ «Ներկայացման սխալ» բաժնում: Տեսնել Լողացող կետի վտանգները այլ սովորական անակնկալների ավելի ամբողջական նկարագրության համար:

Ինչպես ասվում է մոտ ավարտին, «հեշտ պատասխաններ չկան»: Այդուհանդերձ, անտեղի մի զգուշացեք լողացող կետից: Python float գործողությունների սխալները ժառանգվում են լողացող կետով սարքաշարից, և մեքենաների մեծ մասում ոչ ավելի, քան 1 մաս 2**53 յուրաքանչյուր գործողության համար: Դա ավելի քան բավարար է առաջադրանքների մեծ մասի համար, բայց դուք պետք է հիշեք, որ դա տասնորդական թվաբանություն չէ, և որ յուրաքանչյուր float գործողություն կարող է ենթարկվել նոր կլորացման սխալի:

Չնայած պաթոլոգիական դեպքերը գոյություն ունեն, լողացող կետային թվաբանության սովորական օգտագործման դեպքում դուք կտեսնեք այն արդյունքը, որը դուք ակնկալում եք, ի վերջո, եթե պարզապես կլորացնեք ձեր վերջնական արդյունքների ցուցադրումը մինչև ձեր սպասվող տասնորդական թվանշանները: str() սովորաբար բավական է, իսկ ավելի նուրբ վերահսկողության համար տե՛ս str.format() մեթոդի ձևաչափի սպեցիֆիկատորները Ձևաչափել լարային շարահյուսությունը.

Օգտագործման դեպքերի համար, որոնք պահանջում են ճշգրիտ տասնորդական ներկայացում, փորձեք օգտագործել decimal մոդուլ, որն իրականացնում է տասնորդական թվաբանություն, որը հարմար է հաշվապահական ծրագրերի և բարձր ճշգրտության ծրագրերի համար:

Ճշգրիտ թվաբանության մեկ այլ ձև աջակցում է fractions մոդուլ, որն իրականացնում է թվաբանություն՝ հիմնված ռացիոնալ թվերի վրա (այսպես, 1/3-ի նման թվերը կարող են ճշգրիտ ներկայացված լինել):

Եթե ​​դուք լողացող կետով գործառնությունների ծանրակշիռ օգտվող եք, ապա պետք է նայեք NumPy փաթեթին և մաթեմատիկական և վիճակագրական գործողությունների բազմաթիվ այլ փաթեթներին, որոնք տրամադրվում են SciPy նախագծի կողմից: Տեսնելhttps://scipy.org>.

Python-ը տրամադրում է գործիքներ, որոնք կարող են օգնել այն հազվադեպ դեպքերում, երբ դուք իսկապես do ուզում եմ իմանալ լողացողի ճշգրիտ արժեքը: Այն float.as_integer_ratio() մեթոդը արտահայտում է float-ի արժեքը որպես կոտորակ.

>>>

Քանի որ հարաբերակցությունը ճշգրիտ է, այն կարող է օգտագործվել սկզբնական արժեքը անկորուստ վերստեղծելու համար.

>>>

The float.hex() մեթոդը արտահայտում է լողացող վեցանկյուն (հիմք 16), կրկին տալով ձեր համակարգչի կողմից պահպանված ճշգրիտ արժեքը.

>>>

Այս ճշգրիտ տասնվեցական պատկերը կարող է օգտագործվել լողացող արժեքը ճշգրտորեն վերակառուցելու համար.

>>>

Քանի որ ներկայացումը ճշգրիտ է, այն օգտակար է Python-ի տարբեր տարբերակներում արժեքները հուսալիորեն տեղափոխելու համար (պլատֆորմի անկախություն) և տվյալների փոխանակման այլ լեզուների հետ, որոնք աջակցում են նույն ձևաչափին (օրինակ՝ Java-ն և C99-ը):

Մեկ այլ օգտակար գործիք է math.fsum() ֆունկցիա, որն օգնում է նվազեցնել ճշգրիտության կորուստը գումարման ժամանակ: Այն հետևում է «կորցրած թվերին», քանի որ արժեքները ավելացվում են ընթացիկ ընդհանուրի վրա: Դա կարող է փոխել ընդհանուր ճշգրտությունը, որպեսզի սխալները չկուտակվեն այն կետում, որտեղ նրանք ազդեն վերջնական ընդհանուրի վրա.

>>>

15.1: Ներկայացման սխալ

Այս բաժինը մանրամասն բացատրում է «0.1» օրինակը և ցույց է տալիս, թե ինչպես կարող եք ինքներդ կատարել նման դեպքերի ճշգրիտ վերլուծություն: Ենթադրվում է երկուական լողացող կետի ներկայացման հիմնական ծանոթությունը:

Ներկայացման սխալ վերաբերում է այն փաստին, որ որոշ (առավել, իրականում) տասնորդական կոտորակներ չեն կարող ներկայացվել ճիշտ որպես երկուական (հիմնական 2) կոտորակներ: Սա է հիմնական պատճառը, որ Python-ը (կամ Perl, C, C++, Java, Fortran և շատ ուրիշներ) հաճախ չի ցուցադրում ճշգրիտ տասնորդական թիվը, որը դուք ակնկալում եք:

Ինչո՞ւ է այդպես։ 1/10-ը ճիշտ չի ներկայացվում որպես երկուական կոտորակ: Գրեթե բոլոր մեքենաներն այսօր (նոյեմբեր 2000) օգտագործում են IEEE-754 լողացող կետի թվաբանություն, և գրեթե բոլոր հարթակներում Python-ի լողացողները քարտեզագրում են IEEE-754 «կրկնակի ճշգրտությամբ»: 754 կրկնակիները պարունակում են 53 բիթ ճշգրտություն, ուստի մուտքագրման ժամանակ համակարգիչը ձգտում է 0.1-ը փոխարկել իր ձևի ամենամոտ կոտորակին: J/2**N որտեղ J ուղիղ 53 բիթ պարունակող ամբողջ թիվ է։ Վերաշարադրում

as

և հիշելով դա J ունի ուղիղ 53 բիթ (է >= 2**52 սակայն < 2**53), լավագույն արժեքը N 56 է:

>>>

Այսինքն, 56-ը միակ արժեքն է N որ հեռանում է J ուղիղ 53 բիթով: Լավագույն հնարավոր արժեքը համար J ապա այդ գործակիցը կլորացվում է.

>>>

Քանի որ մնացորդը 10-ի կեսից ավելին է, լավագույն մոտարկումը ստացվում է՝ կլորացնելով.

>>>

Հետևաբար 1 կրկնակի ճշգրտությամբ 10/754-ի լավագույն հնարավոր մոտարկումը հետևյալն է.

Ե՛վ համարիչը, և՛ հայտարարը երկուսի բաժանելով կոտորակը դառնում է.

Նկատի ունեցեք, որ քանի որ մենք կլորացրինք, սա իրականում մի փոքր ավելի մեծ է, քան 1/10; եթե չկլորացնեինք, գործակիցը 1/10-ից մի փոքր փոքր կլիներ: Բայց ոչ մի դեպքում չի կարող լինել ճիշտ 1 / 10.

Այսպիսով, համակարգիչը երբեք չի «տեսնում» 1/10-ը. այն, ինչ նա տեսնում է, վերը նշված ճշգրիտ կոտորակն է, լավագույն 754 կրկնակի մոտարկումը, որը նա կարող է ստանալ.

>>>

Եթե ​​այդ կոտորակը բազմապատկենք 10**55-ով, կարող ենք տեսնել 55 տասնորդական նիշի արժեքը.

>>>

նշանակում է, որ համակարգչում պահվող ճշգրիտ թիվը հավասար է 0.1000000000000000055511151231257827021181583404541015625 տասնորդական արժեքին: Ամբողջական տասնորդական արժեքը ցուցադրելու փոխարեն, շատ լեզուներ (ներառյալ Python-ի հին տարբերակները) արդյունքը կլորացնում են մինչև 17 նշանակալի թվանշան.

>>>

The fractions և decimal մոդուլները հեշտացնում են այս հաշվարկները.

>>>

ArmenianEnglish