Модераторы: bsa
  

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> double переменная 
:(
    Опции темы
peanucm
Дата 10.3.2008, 11:40 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 4
Регистрация: 10.3.2008

Репутация: нет
Всего: нет



Здрасьте всем. Столкнулся с такой проблемой: при использовании переменной double в С++, компилятор (visual C++ 7.1) вместо ее значения использует значение, отличающееся на микроскопическую величину, что однако ж приводит к кардинальному изменению в результате программы. Например, в ф-ю принимающую переменную типа double передается значение 0.57, однако ж когда я смотрю ее значение через отладчик, выясняю, что компилятор реально передает 0.56999999999999995. В самой же функции у меня стоит выделение целой части числа, что приводит к совершенно неправильному результату, т.к. вместо 0.57 получается 0.56. Причем это явление не поддается никакой закономерности - какие-то числа он изменяет подобным образом, а какие-то нет, какие-то занижает, какие-то завышает. Подскажите пожалуйста, что же это такое и как с этим бороться?
PM MAIL   Вверх
maxim1000
Дата 10.3.2008, 11:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Участник
Сообщений: 3334
Регистрация: 11.1.2003
Где: Киев

Репутация: 1
Всего: 110



можно использовать более традиционное округление - к ближайшему, а не к ближайшему меньшему
функции не помню, но смысл такой: int(x+0.5)
тогда если дробная часть была меньше 0.5, она будет просто отброшена
если больше - округлится в бОльшую сторну

Добавлено через 38 секунд
а... ещё надо посмотреть будет, как оно с отрицательными работает...


--------------------
qqq
PM WWW   Вверх
peanucm
Дата 10.3.2008, 12:07 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 4
Регистрация: 10.3.2008

Репутация: нет
Всего: нет



Проблема не с округлением, а с тем, что он неправильное число ставит - я ему 0.57 пишу, а он его меняет - ну ваще беспредел какой-то. Тем более, что мне не округление нужно, а выделение целой части, хотя это только пример того, к каким неправильностям приводят подобные изменения, их можно привести и больше. Вопрос в том, почему он так воспринимает некоторые double числа, может в конце числа что-то надо ставить, либо вначале какую-нить волшебную директиву препроцессора написать?
PM MAIL   Вверх
JackYF
Дата 10.3.2008, 12:36 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


полуавантюрист
****


Профиль
Группа: Участник
Сообщений: 5814
Регистрация: 28.8.2004
Где: страна тысячи озё р

Репутация: 6
Всего: 162



Цитата(peanucm @  10.3.2008,  11:07 Найти цитируемый пост)
я ему 0.57 пишу, а он его меняет

число 0.57 не может быть отображено точно в двоичной памяти компьютера. Никто ничего не меняет.


--------------------
Пожаловаться на меня как модератора можно здесь.
PM MAIL Jabber   Вверх
Alexeis
Дата 10.3.2008, 12:50 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 4
Всего: 459



Цитата(peanucm @  10.3.2008,  11:07 Найти цитируемый пост)
Проблема не с округлением, а с тем, что он неправильное число ставит - я ему 0.57 пишу, а он его меняет - ну ваще беспредел какой-то.

  Просто число 0.57 в двоичной системе получается с периодом. Примерно так как в десятичной нельзя представить 2/3. Это ж вам не калькулятор smile .


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
comcon1
Дата 10.3.2008, 12:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 838
Регистрация: 11.6.2005
Где: Москва ДАС-МГУ

Репутация: нет
Всего: 17



если нужна высокая точность - юзайте gmp (gnu multiply precision). Если юзаешь double и нужна точность, пиши обёртки для сравнения. Я в свое время скачал на винграде обертку от earnest: 
Код

#ifndef _FLOAT_COMPARE
#define _FLOAT_COMPARE

const double FLT_EPSILON = 0.0001;

#include <cmath>
// ЯПЮБМЕМХЕ Я ГЮДЮММНИ РНВМНЯРЭЧ
template <class _Val,class _Prec>
class dcmp
{
public:
   typedef _Val  value_type;
   typedef _Prec prec_type;

public:
   template <class _Tp>
   dcmp(const _Tp& val): m_Val(val) {}
   bool operator== (const dcmp& rhs) const { return fabs (m_Val - rhs.m_Val) < prec_type::eps(); }
   bool operator!= (const dcmp& rhs) const { return !(*this == rhs) ; }
   bool operator<  (const dcmp& rhs) const { return *this != rhs && m_Val < rhs.m_Val ; }
   bool operator>  (const dcmp& rhs) const { return *this != rhs && m_Val > rhs.m_Val ; }
   bool operator<= (const dcmp& rhs) const { return *this == rhs || m_Val < rhs.m_Val ; }
   bool operator>= (const dcmp& rhs) const { return *this == rhs || m_Val > rhs.m_Val ; }
   friend bool operator== (const value_type& Val, const dcmp& rhs) { return dcmp(Val) == rhs ; }
   friend bool operator!= (const value_type& Val, const dcmp& rhs) { return dcmp(Val) != rhs ; }
   friend bool operator<  (const value_type& Val, const dcmp& rhs) { return dcmp(Val) < rhs ;  }
   friend bool operator>  (const value_type& Val, const dcmp& rhs) { return dcmp(Val) > rhs ;  }
   friend bool operator<= (const value_type& Val, const dcmp& rhs) { return dcmp(Val) <= rhs ; }
   friend bool operator>= (const value_type& Val, const dcmp& rhs) { return dcmp(Val) >= rhs ; }

private:
   value_type m_Val;
};
struct _FloatPrec
{
    static double eps() { return FLT_EPSILON; }
};
template<class _Val>
   inline dcmp<_Val,_FloatPrec> fcmp(const _Val& val) { return dcmp<_Val,_FloatPrec>(val); }


#endif


Добавлено через 4 минуты и 27 секунд
подключаешь эту фигулину и юзаешь так 
Код

if (fcmp(a) != 0.57) { ; }



--------------------
PM MAIL   Вверх
peanucm
Дата 10.3.2008, 15:04 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Новичок



Профиль
Группа: Участник
Сообщений: 4
Регистрация: 10.3.2008

Репутация: нет
Всего: нет



Я в шоке от ответов...честно.....
Вообще пишется программа для подсчета интеграла от сингулярной функции. Рассмотрим след. участок проги:

double z,x1,x2,y1,y2,n=10000;
int i1,i2;

z=0.57;

i1=(int)(z*n); после этой строчки i1 должно равняться 5700, а компилятор дает 5699
i2=i1+1; соответственно после этой i2 должно равняться 5701, а компилятор дает 5700
if ((i1/n-z)==0) i1=i1-1; после этой строчки i1 должно равняться 5699, так и есть, но не из-за того, что условие удовлетворилось, а из-за того, что оно стало таким еще выше
x1=(double)i1/n;
x2=(double)i2/n;
y1=f(x1)*x1/(z-x1);
y2=f(x2)*x2/(z-x2);
Т.к. i2 не увиличилась должным образом до 5701, то i2/n, т.е. x2 оказывается равной z, в результате чего y2 равно бесконечности - проще говоря сингулярность попадает в узловую точку, когда я методом трапеций подсчитываю.

В принципе можно все это помножить на n, тогда i2, т.е. x2*n не будет равно z*n в знаменателе...но это же пардон маразм. Вот вы все говорите, что double число не точное...Но мне же нужно производить с ним точную операцию сравнения - равно-неравно. Как тогда быть то?


comcon1, неужели все так сложно, а проще нельзя?
PM MAIL   Вверх
Alexeis
Дата 10.3.2008, 15:45 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Амеба
Group Icon


Профиль
Группа: Админ
Сообщений: 11743
Регистрация: 12.10.2005
Где: Зеленоград

Репутация: 4
Всего: 459



Цитата(peanucm @  10.3.2008,  14:04 Найти цитируемый пост)
Но мне же нужно производить с ним точную операцию сравнения - равно-неравно. Как тогда быть то?

  Это бесполезно. Так никто не делает. Нужно вычислять модуль разности чисел и проверять чтобы он был меньше требуемой погрешности (маленького числа, например 1e-6)


--------------------
Vit вечная память.

Обсуждение действий администрации форума производятся только в этом форуме

гениальность идеи состоит в том, что ее невозможно придумать
PM ICQ Skype   Вверх
comcon1
Дата 10.3.2008, 16:03 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 838
Регистрация: 11.6.2005
Где: Москва ДАС-МГУ

Репутация: нет
Всего: 17



чувак, я ж тебе кинул обёртку - ее и юзай. Или тебе лень fcmp( перед переменной дописывать? Думаешь, ты один математикой занимаешься??
проблема, что оператор == вещественных чисел нельзя юзать - это проблема с такоооой бородой! странно, что ты впервые об этом услышал.
Цитата

проще говоря сингулярность попадает в узловую точку, когда я методом трапеций подсчитываю

аахахаха - зацените это предложение в отрыве от контекста!!
НЕ лИЖИ ОКТАЭДР! ЮЗАЙ ОБЁРТКУ!

Это сообщение отредактировал(а) comcon1 - 10.3.2008, 16:13


--------------------
PM MAIL   Вверх
baldina
Дата 10.3.2008, 16:53 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
****


Профиль
Группа: Завсегдатай
Сообщений: 3433
Регистрация: 5.12.2007
Где: Москва

Репутация: 15
Всего: 101



peanucm, в компьютерах числа с плавающей запятой (в отличие от целых) представляются с конечной точностью. Фактически мы манипулироем не множеством действительных чисел, а его подмножеством. Это все должны знать, а уж кто математику считает - тем более.
Сравнивать оператором == числа double можно лишь в случае, если они НЕ получены в результате вычислений, т.е. почти никогда.
Сравнивать надо по формуле 
Код

abs (x-y) / max (abs(x), abs(y)) < epsilon

Здесь epsilon - относительная погрешность.

Округлить double до int можно так:
Код

double round (double x)
{
  if (x > 0)
    return floor(x+0.5);
  else
    return ceil(x-0.5);
}


соответственно строка
Код

i1=(int)(z*n); 
должна быть
Код

i1=(int)round(z*n); 


а вообще то, вот так не проще будет?
Код

x1 = z - 1/n;
x2 = z + 1/n;
y1=f(x1)*x1/(z-x1);
y2=f(x2)*x2/(z-x2);



comcon1, обертка интересная. Подумай, почему бы вместо struct _FloatPrec не использовать std::numeric_limits? ;)
PM MAIL   Вверх
korian
Дата 10.3.2008, 17:00 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 651
Регистрация: 8.3.2008
Где: Украина, Харьков

Репутация: 2
Всего: 17



peanucm
для всех вычислений используйте один типа переменных, пока все переменные не будут вычеслены
Цитата
i1=(int)(z*n); после этой строчки i1 должно равняться 5700, а компилятор дает 5699

в данном случае, если не приводить к int, то будет не 5699, а 5699.99999999999

сравнение значений с плавующей точкой, как тут уже было сказано, надо производить в таком виде:
double x,y;
count double epsilon = 1e-6;
...
вместо if (x == y)
пишем
if (fabs(x - y) < epsilon)

можна так писать, но лучше использовать какую-нибудь обертку (для того, чтобы код был более читабельным)
например, использовать, то что предложил comcon1


Это сообщение отредактировал(а) korian - 10.3.2008, 17:02
PM   Вверх
comcon1
Дата 10.3.2008, 17:20 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


Профиль
Группа: Участник
Сообщений: 838
Регистрация: 11.6.2005
Где: Москва ДАС-МГУ

Репутация: нет
Всего: 17



да не моя это обертка, а earnest - модератор С++ раздела. Очень грамотная девушка, кстати.
она ее выложила, когда только на форум пришла, а я вот сохранил)) (ну потому как юзал)


--------------------
PM MAIL   Вверх
  
Ответ в темуСоздание новой темы Создание опроса
Правила форума "C/C++: Для новичков"
JackYF
bsa

Запрещается!

1. Публиковать ссылки на вскрытые компоненты

2. Обсуждать взлом компонентов и делиться вскрытыми компонентами

  • Действия модераторов можно обсудить здесь
  • С просьбами о написании курсовой, реферата и т.п. обращаться сюда
  • Вопросы по реализации алгоритмов рассматриваются здесь


Если Вам понравилась атмосфера форума, заходите к нам чаще! С уважением, JackYF, bsa.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | C/C++: Для новичков | Следующая тема »


 




[ Время генерации скрипта: 0.0770 ]   [ Использовано запросов: 22 ]   [ GZIP включён ]


Реклама на сайте     Информационное спонсорство

 
По вопросам размещения рекламы пишите на vladimir(sobaka)vingrad.ru
Отказ от ответственности     Powered by Invision Power Board(R) 1.3 © 2003  IPS, Inc.