|
Тема |
Re: Много интересно нещо [re: Lizard] |
|
Автор |
Eмил () |
|
Публикувано | 15.08.03 12:23 |
|
|
По прост пример:
var r:real; {или single или double или extended}
...
r:=0.975;
if r=0.975 then ShowMessage('yes') else ShowMessage('no');
...
Само за extended дава 'yes'.За другите разултата е 'no'
Extended типа се записва в 10 байта.
Всички останали в по-малко.
Single - 4 байта.
Double - 8 байта.
Real е еквивалентен на Double по подразбиране,
или в по старите версии на 6 байта.
Да речем, че r е double. Тогава присвояването
"r:= 0.975" ще заеме в паметта 8 байта.
На следващия ред имаме:
if r=0.975 then ...
Какво се сравнява?
Едното е променливата r (8 байта).
Другото което компилатора вижда е
константата 0.975
Какво се прави: 0.975 се представя като
extended (! 10 байта).
Понеже другото е double, то r се разширява
до 10 байта, НО това няма да е еквивалентно
на константата 0.975, защото r е било
с по-малко значащи цифри и се допълва с нули.
За по-голяма яснота нека не е 0.975, а числото пи.
На колко е равно?
P1=3.14 (2 знака точност след запетаята - примерно това е double)
или
P2=3.1415 (4 знака точност след запетаята - примерно това е extended )
Сега дали P1=P2 ?
За да си сравним трябва да ги приведем към един и същи тип
и то по-големия - extended.
P1 става: 3.14 -> 3.1400 (с нули)
P2 понеже си е вече extended то си остава 3.1415
Сега равни ли са 3.1400 и 3.1415 ?
Естествено не.
Може да има възражение: "да ама пи си е с много знаци
след запетаята, а 0.975 е само с 3 знака."
Отговора е: това е вярно само в десетична система,
а вътрешно числата се представат в двоична и
там това число не е с три знака а е безкрайна
периодична дроб и е въпрос на точност след кой
знак ще се отреже. Например 1/3=0.3333333...
не може да се запише с краен брой знаци, но
ако се използва не десетична, а троична
бройна система, то 1/3 се записва точно!
Сега конкретно за примера от постинга:
1 случай) r := r*100; r := round(r); r := r/100;
2 случай) r := round(r*100)/100;
Първо втория. Какво прави компилатора?
1. Вижда r*100. (в скобите)
Реакция:
- r от real става extended
- 100 се записва като extended
- Умножават се. РЕЗУЛТАТА Е EXTENDED
и с него се продължават следващите изчисления!
В пъврия случай ситуацията е различна.
Какво прави компилатора?
1. Вижда r := r*100;
Реакция:
- r от real става extended
- 100 се записва като extended
- Умножават се. резултата е extended както и преди.
- НО имаме r:=резултата, а r паметта е 8 байта
докато резултата е extended. Следствие:
резултата се реже(закръглява) до по-малка
точност и се записва в паметта определена
за променливата r.
Е сега следващите оператори (r := round(r); r := r/100;)
се изпълняват със стойността на r (double) от паметта,
а тя е РАЗЛИЧНА от стойността extended в предишния
случай "r := round(r*100)/100", просто в предишния
случай няма междинно записване в паметта, което
да "реже", а се изпълнява докрай с extended и
накрая се записва. Поради това е възможно
да се получат различни резултати.
Числото 0.975 в случая са подбрано така,
че ако се закръгли до double (D) или extended (E)
то в едното закръгляване D<0.975, а в другия E>0.975
Разликата може да е в 14 знак след запетаята,
но round КОРЕКТНО закръглява в различни посоки
в двата случая. От тук и различните резултати.
Цялата тази история може да се види, ако
се погледне машинния код, който се
генерира (CPU window).
Уф! престарах се. дано е било интересно на някой.
|
| |
|
|
|