Pull to refresh

Comments 25

В маркетинге бывает наоборот — попытка собрать информацию ломает коммерческий процесс. Или она так его меняет, что собранная информация обесценивается, полностью или частично.
В приведенном примере видно, что ошибка кроется в прямом сравнении чисел с плавающей точкой, которое нельзя так делать. И этот баг скорее всего можно назвать борбагом.
А нужно как-то так:
bool is_not_equal(double a, double b) {
	return std::fabs(a - b) > std::numeric_limits<double>::epsilon();
}

Можно написать ещё проще:


//current = round(current * 1000.0) / 1000.0;
//cout << current << endl; //<-- COMMENT BACK IN TO "FIX" BUG
} while (fabs(current - last) >= 1e-3);

А round не нужен. Это источник потенциальных ошибок. Например, если результат будет последовательно меняться: 1.0005 + eps, 1.0005 — eps,…, тогда даже если eps будет очень мало, мы никогда не остановимся.

Как минимум, эпсилон ещё нужно масштабировать на величину наибольшего значения, иначе на больших числах такое сравнение мало поможет.


std::fabs(a - b) <= std::numeric_limits<T>::epsilon() * std::max(std::abs(a), std::abs(b)) * units_in_the_last_place;
epsilon, обчно величина порядка 1E-7...1E-8 для float, это отклонение МАНТИССЫ числа, а не самого числа.
для применения epsilon нужно сначала отделить порядок от мантиссы, иначе у вас 1E-30 будет равен 1E-20

Продолжая квантовую тему, предлагаю ввести Эверетт-баг: когда код выполняется в какой-то "параллельной Вселенной" и его результаты абсолютно не соотносятся с исходниками. Обычно происходят по причине неправильного билда, либо ввиду внутренних багов используемых библиотек.

Сталкивался :-( ужасные впечатления, после 100500 тестов хочется просто проснуться и чтобы такое больше никогда не снилось.

Ха! Вот только за последние три месяца (специфично для Java):


  1. Пустой catch. Вредитель, писавший бизнес-логику верхнего уровня, замолчал исключение и поставил try с пустым catch. В итоге мы думали, что сбой ниже — в протоколе передачи: данные-то на экране показывались совсем левые, а исключение "типа" не выбрасывалось.
  2. Невозможное поведение на сертификационном вокрбенче. Тестовая группа клиента отправляет нам багрепорты, которые уже давно были пофикшены и на тестах не возникали. Две недели компостировали нам мозги: делаем в коде различные "подпорки" для гипотетических (и невозможных) случаев, добавляем больше логов. В итоге требую доступ к сертификационной машине и вижу — о чудо — определена переменная CLASSPATH и в нее добавлены старые .jar-ы из другой директории...
  3. Пустой-не пустой список. Функция фильтровала значения в списке, но на выход выдавала всегда пустой. Причем на все 100% верная и локально все тесты проходит. А на сервере не выдает. После разделывания кода оказался баг в библиотеке ORM, который выдавал "бажной" IndirectList: https://bugs.eclipse.org/bugs/show_bug.cgi?id=433075
  4. В задеплоенном приложении тупо не работали периодические процессы, а ошибки в логах не было. В результате головной боли обнаружилось, что клиент использовал кривой sftp, который почему-то обрезал концовку файла. А возникавший NoClassDefFoundError не перехватывался catch(Exception e) и поэтому в лог не выводился.
Пункт 1) это вообще какая-то хитроумная диверсия, по-моему. Пункт 2) — ситуация странным образом довольно типичная, полагаю, на нечто подобное попадались (в том или ином варианте) почти все. Пункты 3) и 4) — ну это просто злой рок, никто не застрахован.

Да и ошибки в компиляторах никто не отменял. Уже 2 раза с таким сталкивался.

На PHP сплошь и рядом бывает во всяких нечасто используемых расширениях Особенно, если писать под виндой, а потом разворачивать на Linux-сервере (хотя и с разными версиями Linux случаются нестыковки). Из того что помню — функции для работы с сокетами сильно платформозависимы.
UFO just landed and posted this here
UFO just landed and posted this here
О даа, «повезло» пару раз словить :( Ну, зато теперь я знаю как это называется :)
Я словил механический гейзенбаг.
Не запускалась стиральная машинка: вместо стирки гудит и выключается. Через неделю работает. А потом нет. Нет стабильности.
Оказалось, в нее вода не поступает. Или поступает — как получится. Тройник между холодной водой для раковины и стиральной машинкой был сделан на основе крана (с крутящейся ручкой), и вода поступает в обе системы только в некоторых положениях крана.
Вот вам костыль в сантехнике.
Ах-ха, как раз сейчас ловим баг в серверном приложении, который дичайше зависит от тайминга и расположения кода. С произвольной перезаписью произвольной памяти.
Буквально втыкаешь немного логгирования — все, либо ошибки нет, либо она проявляется совсем по-другому. И делай что хошь.
Тоже ловил подобную багу (раз уж делимся опытом). В дебаге работает, в релизе иногда клиент падает, если сервер перезапускается, но в релизе с прицеплённым отладчиком всё идеально. Прицепление отладчика после падения приводит к различным местам, связанным с криптографией. Куча времени потрачено на синхронизацию, чистку объектов, а оказалось, что в нативном вызове чуть поменялась структура данных, и она портит память, но так, что стреляет только при таких условиях и совершенно в несвяазанном коде.
И как нашли, кто нашкодил? Это ж самое сложное. Ну вот, упал у тебя поток Х, а данные его попортил поток Y сто лет назад :( Пока ввели искусственные guard page в тех местах, где была порча.
Просто уже предположили, что в таком виде очень похоже на порчу памяти, начали вспоминать, что обновляли (может просто у них глюк?), а там, при вдумчивой проверке структур и обнаружили что поменялись структуры при вызове.
Мы сегодня нашли свою проблему, ура.
Она была в коде 10 лет, и стала проявляться только теперь.
UFO just landed and posted this here
Ловил типичный гейзенбаг в начале 90-х, на MSC 6.0 (ещё без плюсов, именно С). Short (16-битный) в стеке параметров занимал стандартные 32 бита. При debug сборке параметр укладывался в одну половину слова, а в release — в другую. Читался при этом он в обоих случаях из одной половины, так что в дебаге всё работало правильно, а в релизе — нет. Ох, долго же я это отлавливал…

О-о-о, было же ещё круче! 5" дискеты. Был 40-дорожечный дисковод, поставили 80-дорожечные. Но у клиентов (за полярным кругом, между прочим), был 40-дорожечный, так что я форматировал под 40 дорожек. И — не работает. То есть там, у них — не работает. У меня — работает. Дискету возят самолётом, программа запускается именно с неё, наличие ложной копии на харде исключено — не было никаких хардов. Привозили, я записывал очередную версию, быстро на следующий самолёт, и — туда. Не работает :-)
… оказалось, что, поскольку «мой» дисковод был 80-дорожечным, в режиме 40 дорожек он писал только на половину ширины дорожки. И у пользователей считывалась старая версия, записанная, ещё когда у меня стоял 40-дорожечный дисковод. Ну, и не работала с новыми данными, ессно.
Разрешилось только, когда я, во-первых, слетал туда в командировку, во-вторых, личную дискету пустил в дело (тогда это было и дорого и дефицитно) — всё заработало сразу. И появилась возможность не спеша анализировать.
Да, история про дискеты просто-таки ностальгичная, несмотря на суровую драматичность сюжета. Какие были времена… (пошёл рыться в шкафу в поисках пятидюймовок с целью выпить водки и над ними всплакнуть).
double a, b;
if (a!=b)
эквивалентно
if( true)
по определению.
Я такие вещи пишу только
if (abs(a-b)<1e-x)
где x — целое больше нуля
Sign up to leave a comment.