Правила программирования на Си и Си++

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


Компьютеры — это арифметические инструменты, славные счетные машины. Они не знают математики. Поэтому даже такие простые выражения, как следующее, могут добавить вам хлопот:

int x = 32767;

x = (x * 2)/ 2;

(На 16-разрядной машине x

получится равным -1.32767 — это 0x7fff. Умножение на 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;

или равноценный ему.



Содержание раздела