Анимация
JavaScript
|
Главная Библионтека > Рекомендация Запомните, что вычисления с плавающей точкой - таинственная и странная вещь. Будьте внимательны при использовании чисел с плавающей точкой и избегайте преобразования типов с плавающей точкой. Почти все, что люди (как они думают) знают об арифметике, оказывается не верно применительно к вычислениям с плавающей точкой. Качественный компилятор предупредит вас, если вы попытаетесь сделать что-то с неопределенным поведением, а именно - поместить в переменную float величину типа double, которая меньше минимального или больше максимального представимого типом float значения. По-настоящему хороший компилятор позволяет включить предупреждения о попытках сделать что-то, что разрешено и определено в стандарте, но может вызвать потерю информации, а именно - поместить величину типа double в переменную типа float, находящуюся между минимальным и максимальным представимыми типом float значениями, но не имеющую точного представления в этом типе. Словом, будьте внимательны, избегайте полагаться на преобразования типов с плавающей точкой и не забудьте включить всю диагностику, на которую только способен ваш компилятор, - тогда у вас есть хоть какой-то шанс перебраться вброд через мутные воды арифметики с плавающей точкой. Задача 31. Сумеречное состояние... кода Сложность: 4 Иногда жизнь заставляет нас заниматься отладкой в ситуациях, которые иначе как таинственными, назвать сложно. Попробуйте свои силы и в этом. Сможете ли вы объяснить причину возникающих проблем? Вопрос ДЛЯ профессионала 1. Один программист написал следующий код. - file biology.h - ... необходимые заголовочные файлы и прочее ... class Animal { public: функции, работающие с данным объектом: virtual int Eat ( int ) { /*...*/ } vi rtual int Excrete ( int ) { /*...*/ } vi rtual int Sleep ( int ) { /*...*/ } vi rtual int wake ( int ) { /*...*/ } специально для животных, которые уже были в браке, и иногда не терпят своих бывших супругов: int EatEx С Animal* а ) { /*...*/ } int ExcreteEx ( Animal* a ) { /*...*/ } int SleepEx ( Animal* a ) { /*...*/ } int WakeEx ( Animal* a ) { /*...*/ } y." конкретные классы class cat : public Animal { /*...*/ } class Dog : public Animal { /*...*/ } class weevil : public Animal { /*...*/ } ... прочие животные ... Удобные, хотя и излишние, вспомогательные функции int Eat ( Animal* а ) { return a->Eat( 1 ); } int ExcreteC Animal* a ) { return a->Excrete( 1 ); } int Sieep ( Animal * a ) { return a->Sleep( 1 ); } int Wake ( Animal* a ) { return a->Wake( 1 ); } К сожалению, этот исходный текст не компилируется. Компилятор отклоняет определение как минимум одной из ...Ех-функций с сообщением о том, что функция уже определена. Чтобы обойти эту ошибку компиляции, программист закомментировал ...Ех-функции, после чего скомпилировал программу и начал тестирование. К сожалению, функция-член Animal::sleep не всегда работала корректно; однако когда программист вызывал ее непосредственно, все было в порядке. Однако при попытке вызвать се из функции-оболочки sleep, в которой не выполняется никаких иных действий, кроме вызова функции-члена, иногда не происходило ничего... не всегда, но такое случалось. И наконец, когда программист попытался разобраться в проблеме при помощи отладчика (или карты символов, сгенерированной компоновщиком), то он вообще не нашел и следа кода Animal :: Sleep. Это что, ошибка компилятора? Следует ли программисту писать гневное письмо разработчику компилятора? Искать хитрый вирус, заползший из недр Интернета? Списать все на проблему 2000 года? Выписать шамана из дебрей Амазонки для изгнания злых духов из системного блока? Что же все-таки произошло? Решение 1. Один программист написал следующий код. [...] Что же все-таки произошло? Если говорить коротко - такие симптомы могут быть вызваны разными заболеваниями, но их совокупность с высокой степенью вероятности говорит о том, что профаммист наступил на грабли непродуманного использования макросов. Мотивация Некоторые распространенные среды программирования С++ предоставляют макросы, преднамеренно созданные для изменения имен функций. Обычно это делается исходя из "хороших" или "невинных" побуждений, в частности - для обратной или прямой совместимости API. Например, если функция sleep в некоторой версии операционной системы заменена функцией sleepEx, производитель, поставляющий заголовочный файл, в котором объявлены эти функции, может решить "любезно" предоставить макроопределение, которое автоматически заменит имя sleep на SleepEx: #define sleep SleepEx Это - определенно Плохая Мысль. Макросы представляют собой антитезу инкапсуляции, поскольку их область действия невозможно проконтролировать - даже автору макроса. Макросам наплевать... Макросы по многим причинам - весьма неприятная вещь, которая может стать попросту опасной. В первую очередь это связано с тем, что макросы - средство замены текста, действующее во время обработки исходного текста препроцессором, до того как начнется какая-либо проверка синтаксиса и семантики. Далее перечислены некоторые из неприятностей, связанных с макросами. /. Макросы изменяют имена - слишком часто, чтобы это не приносило особого вреда. Будет преуменьшением сказать, что проблема только в том, что это мешает при отладке приложения. Такое переименование при помощи макросов означает, что когда вы думаете, что вызываете некоторую функцию, на самом деле ее вызов не происходит. Рассмотрим, например, нашу функцию sleep, не являющуюся членом. int sleepC Animal* а ) { return a->Sleep( 1 ); } Вы не в состоянии найти sleep ни в объектном коде, ни в карте, созданной компоновщиком, - просто потому что в действительности она называется SleepEx. Когда вы столкнетесь с отсутствием функции Sleep, вы можете решить, что компилятор встроил ее в код - это может объяснить, куда подевалась ваша функция и почему ее не видно в объектном коде. Если вы придете к такому заключению и напишете гневное письмо по поводу сверхагрессивной оптимизации разработчику компилятора, окажется, что вы обратились не по адресу, не в ту компанию (или, про крайней мере, не в тот ее отдел). Некоторые из читателей книги вполне могли сталкиваться с такими неприятностями в своей практике. Если вы, как и я, не удовлетворитесь первым попавшимся 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 [ 61 ] 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 |