Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Общие вопросы > Double


Автор: Dmi3ev 24.5.2008, 08:47
Столкнулся с такой проблемой: беру число (тип переменной double), например 0,03, это число сложно представить в двоичной системе(комп ведь в ней работает) (что-то вроде если 1/3 в десятичной представлять, то период получится), поэтому после некоторых действий оно либо немного больше, либо немного меньше того числа, которое получается на самом деле. Я написал свою функцию, которая, так сказать, округляет это дело, как мне надо. Но, интересно, можно это сделать как-нибудь по умному? Подскажите, пожалуйста. Или я заблуждаюсь на счет того, что это из-за двоичной системы счисления?

Автор: bronislav 24.5.2008, 09:26
Цитата(Dmi3ev @  24.5.2008,  08:47 Найти цитируемый пост)
либо немного больше, либо немного меньше того числа, которое получается на самом деле

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

А числа с плавающей точкой в двоичной системе счисления представляются отдельно целаю часть, отдельно дробная. А в памяти компьютера записываются еще http://ru.wikipedia.org/wiki/%D0%A7%D0%B8%D1%81%D0%BB%D0%B0_%D1%81_%D0%BF%D0%BB%D0%B0%D0%B2%D0%B0%D1%8E%D1%89%D0%B5%D0%B9_%D0%B7%D0%B0%D0%BF%D1%8F%D1%82%D0%BE%D0%B9.


Автор: Ken 24.5.2008, 09:50
Можете работать прямо с рациональными числами, например, опередлить класс Rational, переопределить арифметические операции и перобразование к типу double и т.д.

Автор: Dmi3ev 24.5.2008, 09:55
Цитата

А числа с плавающей точкой в двоичной системе счисления представляются отдельно целаю часть, отдельно дробная.

это я понимаю, но, если я после некоторых операций должен получить в дробной части 0,12, то я получаю 0,119999..., причем, это происходит, когда в целой части большое число, например, 123456. вообщем, какая-то ерунда. я поэтому и спрашиваю. 

Автор: Ken 24.5.2008, 10:03
Цитата(Dmi3ev @ 24.5.2008,  09:55)
это я понимаю, но, если я после некоторых операций должен получить в дробной части 0,12, то я получаю 0,119999..., причем, это происходит, когда в целой части большое число, например, 123456. вообщем, какая-то ерунда. я поэтому и спрашиваю.

Из за ограниченности количество разрядов для представления чисел в памяти вы часто получете такие ошибки. Поэтому вам надо все время округлять результат до какой-то точности. Из за этого даже операция проверки чисел с плавающей запятой на равенство часто дает неверный результат. Т.е. нельзя использовать запись типа a == b для таких типов.

Автор: Dmi3ev 24.5.2008, 10:04
Ken, можно и так решить проблему (я функцию написал, ты класс предлагаешь), но, наверняка, есть способ, который до нас придуман, я и хочу узнать его.

Автор: bronislav 24.5.2008, 10:15
Способ был предложен выше. Использовать свой класс для таких расчетов и там реализовать необходимую точность.

Автор: Ken 24.5.2008, 10:18
Цитата(Dmi3ev @ 24.5.2008,  10:04)
Ken, можно и так решить проблему (я функцию написал, ты класс предлагаешь), но, наверняка, есть способ, который до нас придуман, я и хочу узнать его.

Если нужно представление рационального числа со 100% точностью, то способ один: хранить в виде пар чисел. На счет класса, я попробую предложить пример реализации. Пока, если вам просто надо сравнивать 2 числа на (примерное) равенство пишите так:

abs (a - b) < epsilon

здесь a и b числа с плавающей запятой, а epslion погрешность (положительное число ближу к 0).

Автор: mes 24.5.2008, 10:23
Цитата(Dmi3ev @  24.5.2008,  08:47 Найти цитируемый пост)
Или я заблуждаюсь на счет того, что это из-за двоичной системы счисления? 

Да, это не из за двоичной системы счисления, а в из за способа представления числа в памяти. 

Цитата(Dmi3ev @  24.5.2008,  09:55 Найти цитируемый пост)
это я понимаю, но, если я после некоторых операций должен получить в дробной части 0,12, то я получаю 0,119999..., причем, это происходит, когда в целой части большое число, например, 123456. вообщем, какая-то ерунда. я поэтому и спрашиваю.  

используйте тип с фиксированной запятой 

Автор: Ken 25.5.2008, 08:42
Цитата(Dmi3ev @ 24.5.2008,  10:04)
Ken, можно и так решить проблему (я функцию написал, ты класс предлагаешь), но, наверняка, есть способ, который до нас придуман, я и хочу узнать его.

И так, обещал представить решение, но кажется до нас это уже сделали и несколько раз. Смотрите сюда:

http://www.geekpedia.com/tutorial222_Overloading-Operators-Creating-a-Rational-Class.html
http://solarix.ru/for_developers/cpp/boost/rational/ru/rational.shtml

Автор: bronislav 25.5.2008, 09:08
Цитата(Ken @  25.5.2008,  07:42 Найти цитируемый пост)
http://solarix.ru/for_developers/cpp/boost.../rational.shtml


Вот это решение мне нравиться больше.

Автор: ksili 26.5.2008, 10:51
Можно задать режим округления чисел. Операции с вещественными числами выполняет FPU (Floating point unit) поэтому этот режим задается в его флагах. Всего там 4 режима:
к нулю
к минус бесконечности
к плюс бесконечности
к ближайшему числу
Наверняка ваш компилятор сам задает какой-то режим по -умолчанию. Как задать режим на ассемблере мне понятно, могу посмотреть как именно и позже написать (можете сами посмотреть книгу Зубкова по асму в разделе про FPU). А вот на С++ не знаю. Возможно придётся делать ассемблерную вставку.

Автор: Dmi3ev 29.5.2008, 00:29
спасибо за помощь всем, проблему считаю решенной, мы докопались до истины, а точнее вы)))

Ken, отдельное спасибо, больше всех старался)))

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)