Блог пользователя -XraY-

Автор -XraY-, 14 лет назад, По-русски

Добрый день!

Сейчас, прорешивая задачу http://codeforces.me/contest/257/problem/C, я получил сомнительный рантайм. Вот этот код получает Re1:

http://pastebin.com/gUPnDVpQ

Получается, что функция atan2, вызываемая от одного и того же набора аргументов, может выдавать различные значения. Почему?!

  • Проголосовать: нравится
  • +6
  • Проголосовать: не нравится

»
14 лет назад, скрыть # |
← Rev. 3  
Проголосовать: нравится 0 Проголосовать: не нравится

Не знаю, не знаю. У меня такой код получил АС.

UPD: одна из ваших попыток до сих пор висит на первом тесте)

  • »
    »
    14 лет назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится

    Еще раз, сортировка структур, как сортировка вида atan2(p1.y, p1.x) < atan2(p2.y, p2.x) падает на сервере по внутреннему рантайму, а если сначала запихнуть значения atan2 в некое поле структуры, то это получает AC. Проблема именно в том, что atan2 работает странно на сервере и выдает различные значения при одинаковых параметрах. У меня на компе все ок.

  • »
    »
    14 лет назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится

    Ты похоже не понял о чем речь. Суть в том, что в коде есть такие 2 строчки

    ps[i].value = ps[i].val();
    assert(ps[i].value == ps[i].val());
    

    RE 1 Вылетает из-за того, что ps[i].value присваивается atan2 (ps[i].y, ps[i].x), но потом вызывается assert (ps[i].value, atan2 (ps[i].y, ps[i].x)), то есть 2 идентичных вызова atan2 дают разные значения. В этом-то и заключается вопрос : Почему?

»
14 лет назад, скрыть # |
← Rev. 4  
Проголосовать: нравится +8 Проголосовать: не нравится

Ну потому что даблы плюс оптимизатор. Можно считать, что любая функция возвращает ans+e, где e — погрешность с матожиданием ноль.

Т.е. ничего точно посчитаться не может (кроме, возможно, целых/полуцелых, которые хранятся точно). Вообще, жизнь так устроена :)

UPD: продолжение истории.

»
14 лет назад, скрыть # |
← Rev. 2  
Проголосовать: нравится +21 Проголосовать: не нравится

Я подозреваю, что это связано с тем, что у процессоров архитектуры x86/x64 внутренняя разрядность вещественных регистров 80 бит. В то время как разрядность дабла 64 бита. Тоесть сохраняя результат вычисления мы теряем 16 бит. Наверняка у тебя в assert сравнивается сохранённое 64 битное значение с только что полученным 80 битным и последнее более точное.

Попробуй эксперимент:

double tmp[N];
for (int i = 0; i < n; i++)
 tmp[i] = ps[i].val();
for (int i = 0; i < n; i ++)
 assert(ps[i].value == tmp[i]);
  • »
    »
    14 лет назад, скрыть # ^ |
     
    Проголосовать: нравится 0 Проголосовать: не нравится

    А почему std::sort тогда падает?

    • »
      »
      »
      14 лет назад, скрыть # ^ |
      ← Rev. 2  
      Проголосовать: нравится +8 Проголосовать: не нравится

      Я подозреваю что atan(x) < atan(x) первый раз запишет атан в память и обрежет, а второй возьмёт из регистра. В результате первый получиться меньше второго, что вернёт тру, что нарушит целостность сорта.

      Используй только сохранённые значения. Зачем тебе тангенс прямо в предикате считать?

      • »
        »
        »
        »
        14 лет назад, скрыть # ^ |
         
        Проголосовать: нравится 0 Проголосовать: не нравится

        Да, круто, понял.

        Чтобы память лишнюю не использовать. Здесь я так и сделал.

        На самом деле, меня больше волнует, можно ли писать компаратор: "векторное произведение < 0". Ведь легко может оказаться, что a < a. Значит, сорт опять упадет по рантайму. А это совсем не то, о чем хочется думать на контесте. Писать "... < -EPS"? Но тогда придется выбирать EPS, а какой, не ясно.

        Да и на месте векторного произведения может оказаться все что угодно. Что делать?)

        • »
          »
          »
          »
          »
          14 лет назад, скрыть # ^ |
           
          Проголосовать: нравится +3 Проголосовать: не нравится
          1. "Векторное произведение < 0" в вещественных числах писать, конечно, нельзя

          2. Почему не понятно, какой eps? Если сравниваем углы, то "минимальное значение угла" / 2. Если сравниваем векторные произведения, то нужно нормировать вектора и опять же получаем "минимальное значение угла" / 2 (sin(a) ~= a).

          Если углы посчитаны точно, получаем eps = 10 - 18 (long double). Что отлично работает, если координаты исходных векторов до 109 (минимальный угол как раз чуть больше 10 - 18).