... это не g++, это minGW.
MinGW (англ. Minimalist GNU for Windows), ранее mingw32, — нативный программный порт GNU Compiler Collection (GCC) под Microsoft Windows, вместе с набором свободно распространяемых библиотек импорта и заголовочных файлов для Windows API. (Википедия).
Я плохо понимаю, как можно портировать на другую платформу приложение с открытым кодом, при этом наделать кучу ошибок, которые, казалось бы, случайно не сделаешь, при этом стать популярным. Но вот как-то так и вышло.
Вот, например, что вас может ждать, если вы будете использовать компилятор, который на этом сайте гордо зовется g++. Попробуем запустить вот такой код:
#include <iostream>
#include <cmath>
#include <algorithm>
#include <vector>
#include <iomanip>
using namespace std;
bool fail1=0, fail2=0;
bool lsw0(int a, int b) {
// В исходном массиве нет нулей,
// компаратор не может запуститься от нулевого аргумента
if (a==0 || b==0) if (!fail1) {fail1 = 1; cout << "Fail #1" << endl;}
// Арктангенс от положительного числа и нуля согласно man pages
// равен pi/2 независимо от первого аргумента
if (atan2(a,0) < atan2(b,0)) if (!fail2) {fail2 = 1; cout << "Fail #2" << endl;}
// Это отношение транзитивно, если арктангенс зависит только от входных данных
return atan2(max(a,1),0) < atan2(max(b,1),0);
}
int main () {
#ifdef __MINGW32__
cout << "I'm running on MinGW 32" << endl;
#endif
vector<int> perm;
for (int j=1;j<250;j++) {
perm.push_back(j);
}
sort(perm.begin(),perm.end(),lsw0);
}
На нормальных компиляторах этот код отработает без ошибок и ничего не выведет на экран. Но не на MinGW! Фейл первый: если запустить сортировку сортировать массив (1,2,...,249), может ли компаратор запуститься от нулевых аргументов? Как правило, функцию сортировки совершенно не волнует отношение порядка для элементов вне массива. Но MinGW оказался очень любопытным компилятором и таки заглянул туда. Фейл второй: как вы думаете, что больше: угол от OX до вектора (0;1) или угол от OX до вектора (0,2). Вот MinGW искренне думает, что они вполне себе могут отличаться. Ну и наконец, пишем более-менее стандартный компаратор, который отработает верно независимо от реализации atan2, лишь бы сам atan2 не рухнул. Но и здесь нас ждет провал, сортировка падает в RE.
Вывод: если вы не желаете экстремального поиска багов компилятора во время контеста, используйте компилятор Visual C++. Также, наверное, стоит сменить название компилятора в системе с g++ на MinGW, чтобы новички не удивлялись, почему в системе код падает, а у них на локальном компьютере все работает.
UPD. SkyHawk подсказывает, что g++ может вызывать fail #1, если компаратор не является strict weak ordering. Однако, MinGW умеет так делать даже при верном компараторе.
UPD2. Делаю вывод, что все эти фейлы порождаются одной и той же ошибкой: в MinGW strict weak ordering не выполняется даже для double.
UPD3. Ссылка от DAle про причины подобных ошибок при операциях с плавающей точкой.
На счет Fail #1 — в случае ошибки в компараторе программа, откомпилированная в VS или gcc, тоже может вызвать компаратор от отсутствующего значения.
Я считаю, что ты выбрал слишком яркий и обманчивый заголовок. Как ты сам признаешь, не GCC виноват, а MinGW.
Да, я имел в виду немного другой заголовок. Так лучше?
Я вот как раз хотел узнать на онсайте КРОКа, но потом забыл.. По каким причинам используются c одной стороны MinGW, а с другой Mono?
На самом деле у тебя плохой компаратор т.к. ты не учитываешь, что значения вычисляются с погрешностью. При чем этот код с радостью падает и в VS2005. Если же добавить корректную обработку погрешностей, то в обоих случаях код работает адекватно.
А при чем тут погрешности? Во-первых, должно получиться ровно pi/2. Во-вторых, даже если не ровно, должно от запуска к запуску получаться одно и то же число, тогда получается, что мы как бы сортим числа atan2(x,0) по возрастанию. Тогда компаратор все равно корректен.
Добавь в код
cout << lsw0(1, 32) << lsw0(32, 1);
и ты обнаружишь, что у тебя компаратор оба раза вернёт значение 1. Что мягко говоря не совсем то, чего ты ожидаешь увидеть.
Мой компилятор выдает 0 0.
UPD. Как и компилятор ideone.
Хм. На CF 2005я студия тоже выдаёт 0 0, правда Fail #2 всё равно вывело. У меня же 1 1. (видимо опции компиляции на CF немного другие).
Ну стандартом я самоуверенно обозвал man pages по atan2. Он, естественно, неприменим к MS Visual C++.
Стоп! А у вас какой компилятор? Вам не кажется ненормальным, что
atan2(1,0) < atan2(32,0)
, и одновременноatan2(32,0) < atan2(1,0)
. Кажется, я нашел fail#3 :).Еще интересный факт:
atan2
отличается отstd::atan2
. Код:У меня выводит
А вот это неожиданность. Казалось бы, нафига так делать? Никакой пользы, только хорошо запрятанный источник ошибок.
Не знаю, но еще мораль: нефиг писать
using namespace std;
, а то можно-таки напороться. Ну или EPS использовать.Мораль в том, что не надо пользоваться C-шными функциями, только std::...
Хотя на олимпиадах с этим конечно сложно.
Как раз их и использовал. Не спасло.
Интересная штука. Расследование привело к следующему.
std::atan2
в GNU C++ определён как шаблон:В данном примере
_Tp
=int
и_Up
=int
.__enable_if
просто делает проверку и возвращает тот же тип. А вот__promote_2
— это, где так же
_Tp
=int
и_Up
=int
. Результат вызова__promote2
есть тип результатаstd::atan2
. Так как сумма двух интов тожеint
, то получается, чтоstd::atan2<int, int>()
возвращаетint
!В самом деле, если в данном примере добавить касты или явно вызвать
std::atan2<double, double>
, то вывод одинаковый.UPD: OK, всё-таки
std::tr1::is_same<__typeof__(std::atan2(a, 0)), double>::value
=true
, так что проблема где-то в другом месте. Однако вышеприведённые хаки всё равно работают.UPD2: Проглядел самое главное:
Так что весь пост ни о чём.
VS2005 и g++ 4.6.1 (MinGW).
что за мода пошла — гнать на компиляторы
Компиляторы не виноваты. Но знать о том, с чем можешь столкнуться полезно. Вот я поехал на финал КРОК, и, вместо того, чтобы решать задачки, два часа вылавливал вот эти самые баги компилятора.
Раз
пошла такая пьянказашёл вопрос про компиляторы, спрошу:В C++ я не особо силён, и не так давно у меня возникла проблема: один и тот же код на С++, используя компилятор GNU C++, получает Accepted, а при использовании MS C++ — неправильный ответ на тесте 8, причём дома я использую Visual Studio Express 2010, там всё работает как надо.
Код: GNU C++ (1644554) и MS C++ (1644555).
В чём причина?
Похоже, что у тебя где-то происходит переполнение при преобразовании слишком длинной строки в int. Получается неопределенное поведение, и тут уже каждый компилятор вправе поступать, как он хочет.
Компиляторы по-разному реагируют на попытку считать число, не помещающееся в int. MinGW превращает его в 231 - 1. В MS C++ происходит обычное переполнение. В данном случае поведение MinGW крайне нелогично. Так что Accepted на нем можно считать ошибкой. Надо сначала проверять длину строки, а потом уже переводить в число.
На самом деле, мораль, скорее, такова: всегда используйте сравнение с EPS (ну или MSVC), вещественные числа в MinGW — странная вещь.
Очень странная, если честно, мораль. В компараторах обычно не так просто использовать сравнение с EPS, чтобы не нарушить его транзитивность.
Мне не приходилось сортировать числа, которые расположены в цепочку с шагом <EPS. Так что обычно ничего страшного, но помнить об этом, конечно, стоит.
А мне приходилось :( Это на самом деле очень большая проблема, которая приводит к непредсказуемым последствиям.
Ну и вообще, вычисления с плавающей запятой -- это тот еще фан, надо всегда держать ухо в остро. Вот хороший пример непредсказуемого поведения, https://github.com/Kontakter/Cpp-examples/blob/master/trouble_with_double/main.cpp. Кстати на моем текущем ноутбуке работает правильно, а вот на паре удаленных машинок нет :)
Если компилировать без оптимизаций, сюрприза никакого нет, правда? ;-) На доступных мне компах по крайней мере не проявилось. По-моему, это все-таки спецэффект оптимизатора.
Да, без оптимизации скорее всего проблем не будет. Спецэффект состоит в том (как я его понимаю), что большинство FPU производит вычисления в 128-битном регистре, а выгружает оттуда только первые 64 бита. Компилятор все оптимизирует так, что ничего не выгружает, а сразу печатает результат, поэтому мы получаем в итоге единицу :)
Спецэффект с
atan2
иstd::atan2
проявляется и при-O0
Кстати, да. Ведь сравнения сами по себе выполняются с погрешностью. Значит, что бы мы не делали, все равно будут строгие сравнения, которые верны в обе стороны. Мне вот интересно, а MinGW тьюринг-полный?
А причем тут вообще это?
Возможно ли, что глюков в MinGW достаточно, что в нем нельзя реализовать любую программу, которую можно реализовать на машине Тьюринга?
Ну brainfuck на нём, наверное, всё же будет работать, что как-бы намекает ;)
Конечно, не Тьюринг полный. Доступная для адресации лента конечна. Какая тут полнота по Тьюрингу может быть...
Не соглашусь.
Сейчас все строго объясню:
Что значит, что погрешность вычислений не влияет на верность ответа? Это значит, что одинаковые числа, даже с учетом погрешности, мы способны понимать, как одинаковые, а разные, как разные.
Математически это можно записать так: такой что
|x - y| > 2·ε означает неравество
|x - y| < ε означает равенство.
Чисел таких, что ε ≤ |x - y| ≤ 2·ε не существует.
Если формулировать сравнение через ε так, как я описал выше, то сортировка всегда работает. А иначе можно сделать вывод, что погрешность настолько велика, что у нас начались более серьезные проблемы.
Я согласен, при таких условиях все отлично будет.
По идее сравнение идет на аппаратном уровне и от компилятора ну никак не может зависеть
На визуалке не проявляется почему-то.
Может быть просто повезло? Например, возможно, что алгоритм сортировки слегка отличается и на этом конкретном тесте бага не вылазит
Да вот у меня есть и примеры без сортировки вовсе.
Сколько будет
(atan2(1,0) < atan2(1,0))
? Правильный ответ —0
, и это нормально. Сколько будет(atan2(1,0) < atan2(1,0))+(atan2(1,0) < atan2(1,0))
? Правильный ответ —2
, и это странно...Ну как бы.. http://www.parashift.com/c++-faq-lite/newbie.html#faq-29.18
Ну конкретно от того, что регистры длиннее хранимых в памяти значений, а компилятор любит оптимизировать, поможет сделать переменные volatile.
Ещё в том же могут помочь соответствующие опции/прагмы компиляторов, например, -ffloat-store в GNU C, /fp:strict в MSVC. В Java для этого есть ключевое слово strictfp.
Вот смотрю я на очередную подобную тему, и задаюсь вопросом: а сталкиваются ли с подобными проблемами (баги компилятора) программисты на Java? Конечно, баги есть везде. Но хочется по факту.
P.S.: так же я замечал, что во многих случаях реализация STL в g++ немного быстрее, нежели в MS. Так же, константа увеличения вектора при перераспределении памяти в g++ меньше (AFAIK золотое сечение против удвоения в MS). Бывали случаи, когда некоторое решение на g++ заходило, а на компиляторе MS — давало TL/ML. Так что ответ на вопрос "слать в g++ или в MSVC++" imho достаточно неоднозначен...
Сталкиваются... Почитайте обсуждение.
Посылка 1 Посылка 2 Вот два одинаковых кода, разница лишь в типе массива, в одном это Integer[], в другом int[], с int[] проходит 65 тестов, с Integer всего 30. Буду благодарен если какой-нибудь знаток Java скажет мне в чём проблема.
Например, == для Integer работает не так, как вы думаете.
== сравнивает указатели на объекты, а они одинаковы не всегда. для небольших чисел, вроде бы в пределах байта, указатели совпадают даже если числа получены в разных местах программы, просто потому что для них некий буфер сделан, чтобы памяти кучу не жрать.
Т.е. если я использую Integer, то нужно использовать свой компаратор, где вместо == будет equals, так я понимаю?
UPD не о том подумал, всё ясно уже, спасибо.
используйте == для сравнения указателей, для сравнения объектов есть equals, CompareTo и др. по вкусу, и в зависимости от того что вам нужно
В g++ вектор растет в два раза -- только что проверил. В VS в презентации STL говорил, что у них растет в 1.5. Студии проверить нету.
Я сам хоть локально и пишу на g++, отправляю всегда в студии, потому что I64d.
… это не g++, это minGW.
Возможно, на Codeforces еще и Windows стоит
CF тестят 12 серверов. Почему бы 3-4 из них не сделать линуксовыми?
Зачем?
Производительность понизиться сильно. Сабмиты под MS VC++ не смогут тестироваться на линуксовых серверах и наоборот.
Я думаю, несмотря на то, что Codeforces написан на Java, проблем с переносом всего на linux будет очень много, да и времени это потребует.
Какой именно дистрибутив выбрать?
Ты не ответил на главный вопрос. Зачем это делать вообще? Тогда уже нужны и FreeBSD, Mac OS X сервера.
Я и не говорю, что в этом особо много смысла. Просто если уж писать g++ 4.6.*, то это должно работать также, как мой g++ той же версии, как мне кажется.
Кстати, в питоне может быть разница между виндой и линуксом. Под 64-битными никсами будет работать 64-битная арифметика на интах, а под виндой — все равно длинка. Подозреваю что в Java тоже приколы есть.
Например, для того, чтобы не было таких долгоиграющих фейспалмов.
http://bit.ly/Ik3B5w
Не понимаю. Как еще вывести long long в С++ через printf на Windows? Ну то есть тогда будет так "Выводите через %lld, если ваш сабмит достанется инвокеру на линуксе и %I64d если винде".
А при чём тут windows вообще?
Если будут Linux-сервера, то на них пусть идут сабмиты по gcc.
Я не понимаю почему упомянутая фраза про "%I64d" является фейспалмом. Это наоборот, хорошо. А то если на каком-нибудь соревновании забыть проверить это на пробном туре, можно получить лишнию посылку.
Она является фейспалмом потому, что в случае посылки на нормальный gcc она имеет мало смысла. Если решение тестируется мной локально на gcc, то и удалённо от него необходимо ожидать такого же поведения. Имхо.
По "не забыть на пробном туре": так может просто ко всем задачам уже приписывать разные нюансы компиляторов, систем и прочего? Чтобы не забыть проверить на пробном туре.
http://codeforces.me/problemset/customtest
И?
А вообще, не могу понять, зачем вы так усердно защищаете процветание и поддержку костылей?
Если реализация gcc под Windows крива, это значит, что тестироваться решения должны на тех системах, где реализация адекватна.
Я, наоборот, абсолютно против костылей.
По-моему, переводить часть серверов Codeforces на linux — это костыли.
А баги есть во всех компиляторах. Может быть, описанного здесь бага нет в gcc, но наверняка есть другой, которого нет в MinGW.
Еще раз. Я не предлагаю переводить часть CF серверов на linux. Я просто не понимаю, зачем мне надо наколоться пару раз, а потом извращаться с дефайнами и запуском просто для того, чтобы узнать настоящее название компилятора?
%lld естественно. Компиляторами Microsoft, Intel, etc. При чем тут Windows или не Windows вообще. Тут вся проблема в кривом MinGW.
Причём и сам MinGW уже давно понимает
%lld
.Давно — это с какой версии, если не секрет?
Судя по списку изменений файла, обрабатывающего вызовы
printf
и др., то уже с июля 2008 года (в самой первой ревизии уже есть обработка случаяll
). Это соответствует версии mingw-rt >= 3.15.А то есть все настолько плохо. Есть ветка g++ которая принимает одно, а есть которая принимает другое.
MinGW которое поставляется с msys например не принемает lld
MinGW не поставляется с MSYS, это MSYS поставляется с MinGW.
Имеется в виду
i686-pc-msys-gcc
(версия которого аж 3.4.4)? Я попробовал и он тоже правильно выводит числа типа123456789123456789LL
или18446744073709551615ULL
с помощью%lld
/%llu
.У меня версия 4.6.1 выводит верно, но выдаёт warning, что некорректный placeholder
И тестить на рандомном инвокере?
Интересным вариантом для *никсоидов была бы кросс-компиляция *никсовым портом mingw32 и последующий запуск под wine ("костыль на костыле и костылем погоняет", так сказать). Попробую, как прийду домой.
А кто-нибудь знает, на mingw ровно реализован fseek? Codeblocks при наведении на fseek выдает странный define. И вот это настораживает...