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

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

Добрый день!

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

http://pastebin.com/gUPnDVpQ

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

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

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

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

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

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

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

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится 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 дают разные значения. В этом-то и заключается вопрос : Почему?

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

      Мирас, спасибо конечно, но я сам понял косяк. Все дело в точности. Вот посылка, получившая АС.

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

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

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

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

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

    То есть, все функции такие. Ок, понял, спасибо.)

  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится +3 Проголосовать: не нравится

    Я неожиданно осознал, что эта проблема снова вернулась ко мне. Еще раз, как мне писать сортировку по углам, чтобы такой проблемы не существовало? http://codeforces.me/contest/257/submission/3207519 Предположим, я могу не ставить лишних ассертов. Но сортировка и сама прекрасно падает.

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

      вроде, это можно делать векторным и скалярным произведением в целых числах

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

        А если координаты точек в даблах?

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

          atan2, если различие больше 0.1, то векторное произведение. Если нет точек около нуля, то оно существенно отличается от нуля.

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

            А векторное произведение не будет также падать внутри sort?

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

              А да, действительно будут такие же проблемы с точностью. Это хорошо работает когда точки целые, но по кругу.

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

                Если целые, то я по-другому делаю. Я делю все вокруг на четыре части: 1) y = 0, x > 0 2) y > 0 3) y = 0, x < 0 4) y < 0 От точки есть функция type(). Сравниваю сначала ее, потом векторное. Но интересно про даблы.

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

    В VC++ результат тот же.

»
12 лет назад, # |
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]);
  • »
    »
    12 лет назад, # ^ |
      Проголосовать: нравится 0 Проголосовать: не нравится

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

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

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

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

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

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

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

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

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

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

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

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