is_реклам:

пошук, категорії та ін. показати ▼

Комп’ютери не знають математики

Комп’ютери не знають математики
автор опубліковано

В новій підкатегорії Архітектура категорії Програмування поговоримо про те, яку роль грає знання архітектури комп'ютера, на якому Ви пишете код, типів даних, зображення чисел в пам'яті, механізмів виконання елементарних арифметичних і логічних операцій, при виконанні Ваших програм.

За більш детальною інформацією звертайтесь до чудової книжки Алена И. Голуба "Веревка достаточной длинны, чтобы выстрелить себе в ногу" .

Припускайте, що ситуація може змінитися в гірший бік

Один із кращих прикладів цієї проблеми пов'язаний зі "знаковим розширенням". Більшість комп'ютерів використовують так звану арифметику "двійкового доповнення". Крайній лівий біт від'ємного числа в цій арифметиці завжди містить 1. Наприклад, восьмибітовий тип char зі знаком, що містить число -10 , буде зображений в машині з двійковим доповненням як 11110110 (або 0xf6 ). Те ж саме число в 16-бітовому типі int передставлено як 0xfff6 . Якщо Ви перетворите 8-бітовий char в int явно за допомогою оператора переведення типів або неявно, просто використовуючи його в арифметичному виразі, де другий оперант має тип int , то компілятор перетворить char в int , добавляючи другий байт і дублюючи знаковий біт (крайній зліва) char в кожному біті добавленого байта. Це і є знакове розширення.

Існує два види операцій зсуву вправо на рівні мови асемблера: арифметичний зсув дає Вам знакове розширення (те значення, яке було в крайньому лівому біті до зсуву, буде в ньому і після зсуву); логічний зсув заповнює лівий біт нулем. Це правило, незалежно від того, арифметичний чи логічний зсув у Вас, коли Ви використовуєте оператор зсуву С/С++, звучить дуже просто: якщо Вам потрібне знакове розширення, то припустіть, що у Вас отримається заповнення нулями. А якщо Вам потрібне заповнення нулями, то уявіть, що у Вас отрималось знакове розширення. Будьте уважні і слідкуйте за цим!

Іншим хорошим прикладом є коди помилок, що повертаються. Дивовижна кількість програмістів не турбується про перевірку того, чи не повернула функція malloc() значення NULL при нестачі вільної пам'яті. Може бути, що вони сподіваються, що є безмежний об'єм віртуальної пам'яті, але очевидно, що помилка може з легкістю викликати резервування усієї пам'яті, яка є в наявності, і Ви ніколи не побачите цього, якщо не будете перевіряти коди помилок. Якщо функція може визначити стан помилки, то Ви маєте припустити, що помилка відбудеться, щонайменше, один раз за час життя програми.

Комп'ютери на знають математики

Комп'ютери - це арифметичні інструменти, добрі обчислювальні машини. Вони не знають математики. Тому навіть такі прості вирази, як наступні, можуть добавити Вам проблем:

int x = 32767;
x = (x * 2) / 2;

На 16-розрядній машині х отримається рівним -1.32767 - це 0х7fff . Множення на 2 - насправді зсув вліво на один біт, дає в результаті 0xfffe - від'ємне число. Ділення на два є арифметичним зсувом вправо з гарантованим знаковим розширенням, і так Ви отримаєте тепер 0xffff або -1). Тому важливо кожен раз при виконанні арифметичних обчислень враховувати обмеження комп'ютерної системи. Якщо Ви виконуєте множення перед діленням, то при цьому ризикуєте вийти за межі розрядності при збереженні результату; якщо Ви спочатку ділите, то ризикуєте випадково заокруглити результат до нуля; і так далі. Чисельним методам для комп'ютерів присвячені цілі книжки, і Вам потрібно прочитати хоча б одну із них, якщо у Ваших програмах багато математики.

Ви також маєте знати дрібні "гріхи" своєї мови програмування. В С, наприклад, перетворення типів виконується за принципом "оператор за оператором". Думаю, знадобиться не одна хвилина, щоб розібратися, чому наступний код нічого не робить:

long x;
x &= 0xffff;		// очистити все, крім молодших 16-ти бітів
			// 32-бітного типу long

Припустимо, що комп'ютер має 16-бітовий тип int і 32-бітовий тип long . Константа 0xffff типу int з арифметичним значенням -1. Компілятор в С при трансляції &= знаходить різні типи операндів і тому перетворює int в long . -1 в типі long зображується як 0xffffffff , тому логічна операція І не має ефекту. Це якраз той спосіб, відповідно якому і має працювати дана мова програмування. Але це приходить в голову не з першого разу!

Зверніть увагу, що Ви не можете виправити цю ситуацію, використавши переведенням типів. Все, що робить наступний код, це замінює неявне перетворення типу явним. Але, тим не менш, відбувається те ж саме:

x &= (long)0xffff;

Єдиним методом вирішення цієї проблеми є:

x &= 0xffffUL;

або рівноцінний йому.

схоже за тегами

Залишити коментар:

Яндекс цитирования UA TOP Bloggers