Russian Qt Forum
Май 04, 2024, 04:22 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: Шутка QLineF::angleTo()  (Прочитано 8211 раз)
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« : Июль 07, 2015, 22:20 »

Гоняю приложение в Релизе, и только сейчас заметил, что у меня кое-где не сработала оптимизация ортогональных маршрутов. Запускаю Креатор, в нём отладочную версию - а там всё работает, как надо. Недавно похожее было, но там с доступами к файлам библиотек и диагностикой были проблемы, но сейчас никаких библиотек нет и в помине, используется только QtCore. Проблему быстро обнаружил, она оказалась в функции вычисления угла между двумя линиями QLineF::angleTo(QLineF). В первой колонке таблицы значения, которые эта функция мне выдала при запуске отладочной версии, а во второй колонке - при запуске релиза:

0
180
0
0
180
0
0
180
0
0
180
0
0
180
0
0
180
0
    360
180
360
360
180
360
360
180
360
3.50414e-15
180
3.50414e-15
3.50414e-15
180
3.50414e-15
3.50414e-15
180
3.50414e-15

Ну 3.5*10^-15 вместо 0, это ещё ладно, я этот момент заранее предусмотрел fuzzy-сравнением. Но вот разворот на 360 градусов (именно в этих случаях оптимизация не сработала), причём блин ровно  - это уже издевательство какое-то...  Веселый
« Последнее редактирование: Июль 07, 2015, 22:41 от Гурман » Записан

2^7-1 == 127, задумайтесь...
Fregloin
Супер
******
Offline Offline

Сообщений: 1025


Просмотр профиля
« Ответ #1 : Июль 07, 2015, 23:19 »

что мешает сделать свою реализацию? лично я так и делал
Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #2 : Июль 08, 2015, 00:09 »

Да ничего не мешает, только не нужна она. Ну добавил проверку ещё и на 360 градусов, тоже fuzzy на всякий случай, и проблема ушла. Просто как-то это было ну ооочень неожиданно, и нормальной логике не поддаётся. Особенно смачно это смотрится по сравнению с дебаггером, где тихо всё, шито-крыто... А тема тут - на всякий случай, для тех, кто дальше упрётся фейсом об тейбл в похожих ситуациях. Я быстро в Сети ничего такого не нашёл.
Записан

2^7-1 == 127, задумайтесь...
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #3 : Июль 09, 2015, 00:11 »

Зарепортьте багу с примером компилябельным. Я косяков не вижу, похоже оптимизатор шалит:
Код:
qreal QLineF::angleTo(const QLineF &l) const
{
    if (isNull() || l.isNull())
        return 0;

    const qreal a1 = angle();
    const qreal a2 = l.angle();

    const qreal delta = a2 - a1;
    const qreal delta_normalized = delta < 0 ? delta + 360 : delta;

    if (qFuzzyCompare(delta, qreal(360)))
        return 0;
    else
        return delta_normalized;
}
Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #4 : Июль 09, 2015, 00:22 »

Зарепортьте багу с примером компилябельным. Я косяков не вижу, похоже оптимизатор шалит:
Код:
delta < 0 ? delta + 360 : delta

Вот. Если delta условно "-0", например -3.50414e-15, то будет ответ 360, а при таком же положительном значении - это значение, то есть, "+0". Зависит уже от вычисления angle() каждого отрезка. Фишка в том, что отрезки могут быть разнонаправленными и с четырьмя разными углами к горизонтали. angle() получается немного разным в разных случаях, это же вещественное двойной точности, с неизбежным "шумом" младших разрядов. Чтобы пример получить, надо потратить время на охоту за нужными положениями отрезков. Сейчас у меня его нет, может быть на выходных.

Почему в отладчике всё ровно 0 - это другой вопрос... Это действительно похоже на баг.
« Последнее редактирование: Июль 09, 2015, 00:29 от Гурман » Записан

2^7-1 == 127, задумайтесь...
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #5 : Июль 09, 2015, 10:16 »

А, собственно бага-то вот она:
Код:
- if (qFuzzyCompare(delta, qreal(360)))
+ if (qFuzzyCompare(delta_normalized, qreal(360)))
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #6 : Июль 09, 2015, 10:33 »

Можете привести корды линий, на к-ых бага происходит? Не могу подобрать:)
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #7 : Июль 09, 2015, 10:52 »

https://codereview.qt-project.org/#/c/121039/
Записан
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #8 : Июль 09, 2015, 12:36 »

А, собственно бага-то вот она:
Код:
- if (qFuzzyCompare(delta, qreal(360)))
+ if (qFuzzyCompare(delta_normalized, qreal(360)))

Не отвечает на вопрос - почему в отладчике работает правильно. Условная транслация не видна. Разве только если... отладочная библиотека собрана из другого исходника.  В замешательстве
« Последнее редактирование: Июль 09, 2015, 12:43 от Гурман » Записан

2^7-1 == 127, задумайтесь...
Гурман
Гуру общения
******
Offline Offline

Сообщений: 1442

Qt 2.2, 3.3, 4.5, 4,7, 4.8, 5.3, 5.6, 5.9, 5.12


Просмотр профиля
« Ответ #9 : Июль 09, 2015, 12:37 »

Можете привести корды линий, на к-ых бага происходит? Не могу подобрать:)

Чуть попозже, другие дела сейчас. Тоже ловить надо, где именно это вылазит. На сцене под сотню отрезков...
Записан

2^7-1 == 127, задумайтесь...
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #10 : Июль 09, 2015, 13:04 »

Пока тестил, нашел еще одну "проблему" - qDebug печатает 359+1-0 (0 - очень малое) как 360, т.е. 359+1-0 < 360, но в консоли именно 360. Причем это "очень малое" ни фига не малое, на самом деле (оно больше чем e-5). То есть фаззи компар не всегда отрабатывает (см тест).
« Последнее редактирование: Июль 09, 2015, 13:19 от Авварон » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #11 : Июль 09, 2015, 16:27 »

Код:
qreal QLineF::angleTo(const QLineF &l) const
{
    if (isNull() || l.isNull())
        return 0;

    const qreal a1 = angle();
    const qreal a2 = l.angle();

    const qreal delta = a2 - a1;
    const qreal delta_normalized = delta < 0 ? delta + 360 : delta;

    if (qFuzzyCompare(delta, qreal(360)))
        return 0;
    else
        return delta_normalized;
}
А, собственно бага-то вот она:
Код:
- if (qFuzzyCompare(delta, qreal(360)))
+ if (qFuzzyCompare(delta_normalized, qreal(360)))
Да, но это еще не все. В одну сторону округляем, в др нет. Поэтому возможна такая ситуация
Код
C++ (Qt)
a.angleTo(b)  // возвращает ноль  
b.angleTo(a)  // возвращает НЕ ноль
Народная примета: если угол в градусах - реализация слабенькая. Fuzzy здесь вообще не к месту, вызывающему все равно самому надо округлять.
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #12 : Июль 09, 2015, 23:08 »

Ошибки округления возможны, вы же не удивляетесь, получая разный результат в зависимости от того, сперва умножаете, а потом делите, или наоборот?
Upd: даже не так, эта проверка бесполезна, вы же не будете в клиентском коде писать
Код:
if (a.angleTo(b)) == 0) ...
Вы напишете
Код:
if (qFuzzyIsNull(a.angleTo(b)))) ...
Зачем делать дважды одно и то же?
« Последнее редактирование: Июль 09, 2015, 23:16 от Авварон » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #13 : Июль 10, 2015, 09:19 »

Вы напишете
Код:
if (qFuzzyIsNull(a.angleTo(b)))) ...
Из этого ничего хорошего не выйдет. Вообще в прикладной задаче есть свой epsilon, который может намного отличаться от используемого в Qt. Поэтому вероятно я напишу примерно
Код
C++ (Qt)
const qreal angleFudgeMin = 1.0e-5;   // может скопирую значение из qFuzzyIsNull, но может и нет  
const qreal angleFudgeMax = 360 - angleFudgeMin;
...
qreal angle = a.angleTo(b);
if (angle < angleFudgeMin || angle > angleFudgeMax) ...  // считаем что вектора совпадают
 
   
Записан
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3258


Просмотр профиля
« Ответ #14 : Июль 10, 2015, 13:47 »

Ну вот, уже qAbs забыли:)
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.1 секунд. Запросов: 23.