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

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

Страниц: [1] 2   Вниз
  Печать  
Автор Тема: MSVC 2015 чудит: float -> double  (Прочитано 9641 раз)
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


Просмотр профиля
« : Июнь 27, 2018, 12:22 »

На 32-битной версии MSVC и Qt 5.9.2 наблюдается такой интересный баг... В 64-битной версии такого нет.

Коротко:
Код
C++ (Qt)
   float f1 = QVector2D::dotProduct(v1,v2); // возвращает float
   double d1 = f1;
   double d2 = QVector2D::dotProduct(v1,v2); // повторяем

Результат: Значения d1 и d2 различаются после пятнадцатого знака после запятой, а именно: d1 = 1.00000000000000000000 d2 = 1.00000000000000355271. Какая-то сумасшедшая оптимизация? Никаких своих флагов в .pro файл я не добавлял.

Подлиннее:
Код
C++ (Qt)
#include <QtCore>
#include <QtGui>
 
int main(int argc, char *argv[])
{
   QCoreApplication app(argc, argv);
 
   QTextStream ts(stdout, QIODevice::WriteOnly);
   QVector2D v1(8.10837e-09, -1);
   QVector2D v2(4.45843e-07, -1);
 
   float f1 = QVector2D::dotProduct(v1,v2);
   double d1 = f1;
   double d2 = QVector2D::dotProduct(v1,v2);
 
   ts.setRealNumberNotation(QTextStream::FixedNotation);
   ts.setRealNumberPrecision(20);
   ts
           << v1.x() << " "
           << v1.y() << " "
           << v2.x() << " "
           << v2.y() << " " << endl
           << f1 << " " << d1 << " " << d2 << endl;
   ts.flush();
 
   return 0;
}
 

Выхлоп:

Цитировать
0.00000000810836997545 -1.00000000000000000000 0.00000044584299985218 -1.00000000000000000000
1.00000000000000000000 1.00000000000000000000 1.00000000000000355271 <--- f, d1 и d2


Проверьте пожалуйста на MSVC 2017 и MSVC 2013 в 32-битной версии. Какие есть гипотезы? Улыбающийся
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #1 : Июнь 27, 2018, 12:55 »

Мне в винду сейчас лезть далеко, но такие варианты не проверяли:
Код
C++ (Qt)
   float f1 = QVector2D::dotProduct(v1,v2);
   float f2 = QVector2D::dotProduct(v1,v2);
   double d1 = f1;
   double d2 = f2;
   double d3 = QVector2D::dotProduct(v1,v2);
   double d4 = QVector2D::dotProduct(v1,v2);
 

Цель - повторяемость и стабильность результатов. Посмотреть на ассемблерный код. Посмотреть на d1, d2, d3, d4 в памяти во время отладки, может вывод в поток чудит. И сборка release или debug?
Записан

Пока сам не сделаешь...
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


Просмотр профиля
« Ответ #2 : Июнь 27, 2018, 14:24 »

повторяемость 100%, это не рандом. Вывод в поток верен, т.к. в более полном коде из проекта я ещё сравниваю полученное double значение с нужным мне, и первый double проходит проверку, а второй - нет. На release и release+debug symbols работает одинаково неправильно. Я подозреваю, что проблема с оптимизацией RVO и внутренним представлением float. На 64-бит не воспроизводится вероятно потому, что в 64-бит ошибки float меньше.
Записан
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


Просмотр профиля
« Ответ #3 : Июнь 27, 2018, 14:54 »

GCC в Ubuntu 16.04 работает нормально, d1 и d2 полностью равны
Записан
deMax
Хакер
*****
Offline Offline

Сообщений: 600



Просмотр профиля
« Ответ #4 : Июнь 27, 2018, 16:21 »

В первом случае вы убиваете точность, real(double) -> float -> double, float 24бита на минтиссу 16000000. а у вас 15ый знак
Вопрос, откуда погрешность берется? Попробуйте ручками скалярное произведение посчитать, с типами float, double.
Записан
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


Просмотр профиля
« Ответ #5 : Июнь 27, 2018, 16:28 »

В первом случае вы убиваете точность, real(double) -> float -> double

Во втором также. В Qt5 QVector2D принимает и возвращает float. Поэтому и во втором случае идёт преобразование входных double литералов во float, потом dotProduct() считает результат также используя значения float, и возвращает также float!
Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Июнь 27, 2018, 16:35 »

А что тут обсуждать? Баг явно, у MSVC они всегда были и будут. Придется отказаться от точного сравнения напр
Код
C++ (Qt)
if (fabs(d1 - d2) < FUDGE_FACTOR)  // считаем ==
 
Чисто для спортивного интереса проверить так
Код
C++ (Qt)
QVector2D v1(8.10837e-09f, -1.0f);
QVector2D v2(4.45843e-07f, -1.0f);
Записан
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


Просмотр профиля
« Ответ #7 : Июнь 27, 2018, 16:37 »

deMax Продолжаем мистику! Улыбающийся

Код
C++ (Qt)
int main(int argc, char *argv[])
{
   QCoreApplication app(argc, argv);
 
   QTextStream ts(stdout, QIODevice::WriteOnly);
 
   QVector2D v1(8.10837e-09, -1);
   QVector2D v2(4.45843e-07, -1);
 
   // считаем руками
   const double d1 = v1.x() * v2.x() + v1.y() * v2.y();
   // считаем стандартно
   const double d2 = QVector2D::dotProduct(v1,v2);
 
   ts.setRealNumberNotation(QTextStream::FixedNotation);
   ts.setRealNumberPrecision(20);
   ts
           << v1.x() << " "
           << v1.y() << " "
           << v2.x() << " "
           << v2.y() << " " << endl
           << d1 << " " << d2 << endl;
   ts.flush();
 
   return 0;
}

Выхлоп разный!

Цитировать
1.00000000000000000000 1.00000000000000355271
Записан
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


Просмотр профиля
« Ответ #8 : Июнь 27, 2018, 16:39 »

Igors суффикс f не помог. Я проверил - внутри QVector2D данных хранятся правильно и одинаково как в 32-бит, так и в 64-бит версии. Проблема появляется только при вызове функции dotProduct (см. код выше) в 32-бит версии. Когда считаем руками - всё нормально Улыбающийся
« Последнее редактирование: Июнь 27, 2018, 16:49 от Alex Custov » Записан
kuzulis
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2812


Просмотр профиля
« Ответ #9 : Июнь 27, 2018, 17:50 »

Запости баг об хреновеньком QVector2D::dotProduct(), если ручками все нормально Улыбающийся
Записан

ArchLinux x86_64 / Win10 64 bit
Авварон
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 3257


Просмотр профиля
« Ответ #10 : Июнь 27, 2018, 18:25 »

Igors суффикс f не помог. Я проверил - внутри QVector2D данных хранятся правильно и одинаково как в 32-бит, так и в 64-бит версии. Проблема появляется только при вызове функции dotProduct (см. код выше) в 32-бит версии. Когда считаем руками - всё нормально Улыбающийся

Что дизассемблирование говорит? С мелкомягкого компилятора станется возвращать результат в дабле.
Записан
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


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

Запости баг об хреновеньком QVector2D::dotProduct(), если ручками все нормально Улыбающийся

Исходники dotProduct() - это копия того что делаю руками: https://code.woboq.org/qt5/qtbase/src/gui/math3d/qvector2d.cpp.html#_ZN9QVector2D10dotProductERKS_S1_ . Проблема явно не в коде как в таковом. Проблема с конпилятором как мне кажется.
Записан
Alex Custov
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 2063


Просмотр профиля
« Ответ #12 : Июнь 27, 2018, 19:29 »

Авварон дизассемблирование пока не смотрел
Записан
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #13 : Июнь 28, 2018, 00:18 »

Цитировать
Оптимизирующий компилятор Microsoft (R) C/C++ версии 19.14.26430 для x86
(C) Корпорация Майкрософт (Microsoft Corporation).  Все права защищены.
тот же вывод:
Цитировать
0.00000000810836997545 -1.00000000000000000000 0.00000044584299985218 -1.00000000000000000000
1.00000000000000000000 1.00000000000000000000 1.00000000000000355271

Кусок асма (без оптимизации):
Цитировать
; 12   :     float  f1 = QVector2D::dotProduct(v1, v2);

   lea   edx, DWORD PTR _v2$[ebp]
   push   edx
   lea   eax, DWORD PTR _v1$[ebp]
   push   eax
   call   DWORD PTR __imp_?dotProduct@QVector2D@@SAMABV1@0@Z
   add   esp, 8
   fstp   DWORD PTR _f1$[ebp]

; 13   :     double d1 = f1;

   cvtss2sd xmm0, DWORD PTR _f1$[ebp]
   movsd   QWORD PTR _d1$[ebp], xmm0

; 14   :     double d2 = QVector2D::dotProduct(v1, v2);

   lea   ecx, DWORD PTR _v2$[ebp]
   push   ecx
   lea   edx, DWORD PTR _v1$[ebp]
   push   edx
   call   DWORD PTR __imp_?dotProduct@QVector2D@@SAMABV1@0@Z
   add   esp, 8
   fstp   QWORD PTR _d2$[ebp]

Я не спец в ассемблере, но настораживают fstp:
Цитировать
   fstp   DWORD PTR _f1$[ebp]
        ...
   fstp   QWORD PTR _d2$[ebp]

Похоже в случае с float он снимает со стека float и потом его переводит в double, а в случае с double сразу double и снимает, хотя dotProduct() float возвращает. Может тут собака порылась Улыбающийся.
Записан

Пока сам не сделаешь...
ViTech
Гипер активный житель
*****
Offline Offline

Сообщений: 858



Просмотр профиля
« Ответ #14 : Июнь 28, 2018, 00:29 »

Костыль:
Код
C++ (Qt)
   double d2 = 0.0f + QVector2D::dotProduct(v1, v2);

Вывод (без оптимизации):
Код:
0.00000000810836997545 -1.00000000000000000000 0.00000044584299985218 -1.00000000000000000000
1.00000000000000000000 1.00000000000000000000 1.00000000000000000000

Улыбающийся

Если включить оптимизацию, то всё возвращается на круги своя.
Записан

Пока сам не сделаешь...
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  


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