Russian Qt Forum

Программирование => С/C++ => Тема начата: Dimon-st от Июля 19, 2010, 13:29



Название: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Dimon-st от Июля 19, 2010, 13:29
Всем привет !
Столкнулся с банальной, но неприятной и неразрешимой (пока) ситуацией при простой операции деления с вещественными числами.
Суть проблемы: при делении меньшего числа на большее число (по величине отличаются в 10 раз) результат получается: "0.10000000000000001" (и при отладке и при при прогоне программы). Причем результат не зависит от типа вещественных данных (пробовал qreal, double, long double).
Вот фрагмент кода:
Код:
double vX, vY, vRez;

vX=1.0;
vY=10.0;

vRez=vX/vY;

Кроме того, при принудительном присваивании переменной значения "vX=0.1;", результат получается "vX=0.10000000000000001".  :o  ???
Код:
long double vX;

vX=0.1;


Пробовал округлять, но при нем вываливается тот же косяк....
У меня нет больше вариантов как с этим бороться .....

Использую Qt Creator (Qt 4.7.0 (32-битн.)) от Nokia Corp.
Пожалуйста, помогите ! :'(


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: ecspertiza от Июля 19, 2010, 14:24
Для вещественных чисел это обычная погрешность.

для сравнения используй
Код:
if (fabs(a - b) < 1e-6)
{
     return true;
}esle
     return false;


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Wlad_C от Июля 19, 2010, 14:25
С чем бороться?
То, что Вы получаете - так и должно быть!
Это происходит при переводе из двоичной системы в десятичню. Можете проверить ручками!


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Dimon-st от Июля 19, 2010, 14:39
Цитировать
С чем бороться?
То, что Вы получаете - так и должно быть!
Это происходит при переводе из двоичной системы в десятичную. Можете проверить ручками!

Я допускаю такое при делении, но при присваивании ????
Код:
long double vX;

vX=0.1;

при выполнении этого фрагмента получается "vX=0.10000000000000001".
Может у кого по другому? Отпишитесь .....


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: ecspertiza от Июля 19, 2010, 14:50
Боюсь что нет :) Это нормально вроде как  :) а какие трудности в вязи с этим?


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Dimon-st от Июля 19, 2010, 15:10
Да, проблемы есть.
Разрабатываю класс, и в одном (именно этом) случае, он лагает. :(
Имеется 4 числа: начальное,  конечное значения, шаг и количество: vFirstValue, vLastValue, vStep, vCount.
Изменяя одно из значений, пересчитываются остальные (или одно из них). При следующих данных:
vLastValue=10, vFirstValue=0
задаем: vCount=101
и пересчитываем vStep и vFirstValue
Получаю
vStep=0.10000000000000001
vFirstValue=-5.55112e-16

Хотя, по идее должно быть:
vStep=0.1
vFirstValue=0

Как этого добиться ?


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: SimpleSunny от Июля 19, 2010, 16:20
"-5.55112e-16" можно сказать, что 0. Какая проблема?


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Sancho_s_rancho от Июля 19, 2010, 17:42
Код:
qFuzzyCompare ( double p1, double p2 )   [static]
вам поможет. Прочтите в документации.
И обратите внимание на
Код:
// Instead of comparing with 0.0
                 qFuzzyCompare(0.0,1.0e-200); // This will return false
         // Compare adding 1 to both values will fix the problem
                 qFuzzyCompare(1 + 0.0, 1 + 1.0e-200); // This will return true


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Dimon-st от Июля 19, 2010, 18:51
Цитировать
"-5.55112e-16" можно сказать, что 0. Какая проблема?
Проблема в том, что именно так он выдает и при преобразовании числа в стоку (без форматирования), хотя хотелось бы "0".

Сравнение чисел в данном алгоритме не подходит
Цитировать
qFuzzyCompare(0.0,1.0e-200);


Пробовал принудительно так:
Код:
    if (vStep!=10.0)
        {
         vStep=1.0/vStep;
        }
        else
            {
            vStep=1.0e-1;
            }
но при попытке присвоить "vStep=1.0e-1;", значение оказывается "vStep=0.10000000000000001", но почему ???? Это же простое присваивание?

Программисты Qt, пожалуйста отпишитесь, у всех при отладке после банального присвоения "vStep=1.0e-1;" получается "vStep=0.10000000000000001"? Протестировал в BDS и MSVS2008, там все работает без всяких проблем. Не знаю что и думать, версия Qt у меня такая глючная, или я уже ничего не понимаю...


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: m_ax от Июля 19, 2010, 18:58
QString QString::number ( double n, char format = 'g', int precision = 6 )   [static]

Не вижу проблемы..

P.S. так вспомнилось:
Российским учёным первыми в мире
удалось раздуть из мухи слона  ;D



Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Igors от Июля 19, 2010, 19:01
В дополнение к сказанному

- др. отладчики также показывают подобным образом

- для чисел с конечным числом знаков после запятой равенство срабатывает, напр
Код:
float t = 1.0f;
t /= 10;
...
if (t == 0.1f) {   // вернет true
...
}

- пример c 1.0e-200 специфичен, насколько я помню: диапазон от 1.0e-38  до 1.0e+38 (для всех float, double и др., разница между ними в точности). Меньшие значения INF - не ноль, но и значение неизвестно

Edit: см. также недавнее обсуждение http://www.prog.org.ru/topic_14154_0.html (http://www.prog.org.ru/topic_14154_0.html)  


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Dimon-st от Июля 19, 2010, 19:21
Цитировать
Российским учёным первыми в мире
удалось раздуть из мухи слона
Шутку оценил ! Но мне искренне не понятно: почему в BDS и MSVS код "1.0/10.0=0.1" работает без проблем, а в Qt банальное "x=1.0e-1;", приводит к "х=0.10000000000000001"? (получается риторический вопрос) ;D

Как я понял от этого косяка не избавиться, единственное, что можно - воспользоваться округлением. Поэтому вопрос такой: есть ли какие стандартные функции округления, или придется извращаться через преобразование в строку и обратно "QString QString::number ( double n, char format = 'g', int precision = 6 )" ?


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: m_ax от Июля 19, 2010, 19:40
Вы там что, поправку к сверхтонкой структуре считаете, чтоль?

Сейчас Вам кто-нить расскажет, что дело здесь вовсе не в кутэ.. Так что не надо крошить на Qt))

Ответ кроется в том, как представляются числа с плавающей точкой.. Спросите у гугла, как устроен double, например) 


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Dimon-st от Июля 19, 2010, 19:56
Цитировать
Ответ кроется в том, как представляются числа с плавающей точкой.. Спросите у гугла, как устроен double, например)
Я знаком со структурой double и float, и на Qt никто не "крошит". Просто неясно: если в BDS, MSVS и Qt структуры типов данных одинаковые, то почему результаты BDS, MSVS и Qt - разные ? Может это быть из-за того, что установленная у меня версия Qt Creator "кривая" ? Поэтому я и просил протестировать на др. компах небольшие фрагменты кода.


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Igors от Июля 19, 2010, 20:18
Просто неясно: если в BDS, MSVS и Qt структуры типов данных одинаковые, то почему результаты BDS, MSVS и Qt - разные ?
Результаты - одинаковые и здесь и там. Просто "здесь" отладчик показывает с большей точностью - вот и все


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Dimon-st от Июля 19, 2010, 20:22
Никаких стандартных функций округления я не нашел. Таким образом придется извращаться через преобразование в строку и обратно "QString QString::number". Правильно я понимаю ?


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: SimpleSunny от Июля 19, 2010, 20:24
Я всё еще понять не могу, чем вас не устраивает точность числа порядка e16?


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Dimon-st от Июля 19, 2010, 20:49
Цитировать
Я всё еще понять не могу, чем вас не устраивает точность числа порядка e16?
Точность устраивает, просто для моего примера:
Цитировать
vLastValue=10, vFirstValue=0
задаем: vCount=101
и пересчитываем vStep и vFirstValue

vStep = (vLast - vFirst)/(vCount-1.0);
vFirstValue = vLast - vStep *(vCount-1.0);


Получаю
vStep=0.10000000000000001
vFirstValue=-5.55112e-16

Хотя, по идее должно быть:
vStep=0.1
vFirstValue=0
вместе с изменением vStep=0.10000000000000001 изменяется и vFirstValue=-5.55112e-16. И бороться с этим можно только округлением, но для этого придется всякий раз определять количество значащих разрядов vStep, которых больше, чем должно быть ... и опять замкнутый круг...


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: SimpleSunny от Июля 19, 2010, 21:05
"То ли лыжи не едут, то ли я неординарный."

Если точность вас устраивает, как вы заметили выше, то в чем проблема, если vFirstValue == 0 с точностью, которая вас устраивает?

P.S. vFirstValue = vLast - vStep *(vCount-1.0); Если подставить vStep и раскрыть скобки, то получим vFirstValue = vFirst;


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Igors от Июля 19, 2010, 21:09
вместе с изменением vStep=0.10000000000000001 изменяется и vFirstValue=-5.55112e-16. И бороться с этим можно только округлением, но для этого придется всякий раз определять количество значащих разрядов vStep, которых больше, чем должно быть ... и опять замкнутый круг...
Вам не с чем бороться - Вы имеете точно те же числа что и в MSVC и др. (там где все устраивает). Это просто "трусы линяют" как в старом анекдоте  :)


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Dimon-st от Июля 19, 2010, 21:17
Цитировать
Если подставить vStep и раскрыть скобки, то получим vFirstValue = vFirst;
Логично, только вот компьютер раскрывать скобки и упрощать выражения не умеет. При изменении vCount=101 (вместо vCount=11) в алгоритме изменяются значения vStep и vFirstValue по приведенным выше формулам. И погрешность закравшаяся в vStep приводит к изменению vFirstValue, хотя по идее она не должна меняться.


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Dimon-st от Июля 19, 2010, 21:39
Цитировать
Это просто "трусы линяют" как в старом анекдоте
Ссылочку на анекдот не дадите ? Просто интересно стало. :)
(извиняюсь, что не по теме)


Название: Re: Неожиданный результат при делении (1.0/10.0=0.10000000000000001 ?????????)
Отправлено: Igors от Июля 20, 2010, 13:49
Ссылочку на анекдот не дадите ? Просто интересно стало. :)
http://prikol.i.ua/user/1920258/?p=3 (http://prikol.i.ua/user/1920258/?p=3)