Математические оперции с double и BigDecimal
Посоветуйте, есть ли какие-либо другие варианты
Что значит "точные" применительно к типу double? Это ведь тип "с плавающей точкой", по определению допускающий округление.
Сформулируйте задачу полностью, пожалуйста.
ну вот например при делении 0.003/0.001
в результате получается 2.999.... или скажем если поделить 0.0035/0.001
в результате врядли получится 3.5
тем неменее если воспользоваться классом BigDecimal, который кстати на сколько я понял и предназначен для операций с числами с плавающей точкой, в результате которых нужно получить точные значения исходя из десятичной системы, то все получается прекрасно
В двоичной системе 0.003 и 0.001 - бескоечные периодические дроби и не могут быть точно прдставлены в компьютере.Кстати вынужден констатировать тот факт что я полный профан. Что впрочем я и так знал )) что впрочем легко исправить со временем ))
Существуют пакеты для обработки точных рациональных чисел, представленных парой числитель/знаменатель. Это бывает важно в некоторых "тяжелых" математических задачах, вроде решения плохо обусловленных систем линейных уравнений. В Java API такого, разумеется, нет, и вряд ли Вам нужно это.
BigDecimal позволяет точно представлять только конечные двоичные дроби. При представлении произвольного рационального числа точность будет сколь угодно большой, но конечной. При представлении иррационального sqrt(2.0) Вам не поможет даже пакет обработки рациональных чисел - такое число в принципе нельзя точно представить на компьютере.
Расскажите, а зачем и где Вам понадобились точные результаты?
Если у вас математическая или физическая задача, типа расчета каких нибудь концентраций или проекций на плоскость, то считайте все в double, а потом (если очень хочется красиво распечатать результат) конвертируйте в BigDezimal и округлите по вкусу. Либо можно double сразу форматеру передать, он сам округлит.
Если у вас финансовая задача, то считайте сразу все в BigDezimal, спокойней спать будуте ;-)
Даниэль Алиевский[досье]
При представлении иррационального sqrt(2.0)
нет, такая задача близко не стоит ))
30-ый[досье], Даниэль Алиевский[досье]
На самом деле нужно построить график котировки валют или как там он называется, я не специалист пока что в этой области ) только начал. В основе графика лежат данные о разнице между валютными парами, например USD/EUR ну и естественно там такие значения как 1.2959, 1.2961...
при чем шаг в данном случае равен 0.0001 поэтому даже небольшие погрешности в ходе вычисления координат, получаются довольно заметными на графике
30-ый[досье] а что за форматер? какой пакет?
Издеваетесь? Double имеет точность в 15 значащих цифр. Не существет такого графика, где бы такая точность была бы недостаточна!
Внимание, кстати! В арифметике с плав.запятной важна не абсолютная точность, а количество значащих цифр. Таким образом число 123456789 требует 9 значащих цифр, а 0.0000000012 лишь две. Таким образом для представления (без потреть) второго числа вам хватит даже менее точного формата float.
Советую перечитать теорию перед началом программирования.
30-ый[досье]
эм... вот пример
double d = 29.0 * 0.01; System.out.println(d); //0.29 System.out.println((int) (d * 100)); //28
вы верно не совсем точно меня поняли, я ничего не имею против Double или Float. Но после мат. операций (как-то умножение, деление) получаются не совсем точные, с точки зрения исчисления в десятичной системе, результаты.
вот статья интересная по теме
http://www.ibm.com/developerworks/ru/library/j-jtp0114/index.html
У вас в d видимо лежит 0.289999999999999. Т.е. 0.29 с точностью до 15 знака. Думаю такая точность должна вас более чем устроить :-)
Мораль: не надо делать "(int)d". Пользуйтесь функциями округления из класса Math.
З.Ы. Математического образования иметь не нужно - это все написано в любой книжке по основам программирования.
30-ый[досье]
вы статью читали? )) мне не нужно округлять, нужны точные десятичные значения
при делении 0.0003 на 0.0001 мне нужно получить 3, при чем без округлений, а не 2.99999999...
А если у меня допустим такое деление 0.00029 на 0.0001
в результате мне нужно получить 2.9, а не те же 2.899999... и уж тем более меня не устаивает round, ceil или floor
виноват, образование нужно специализированное, но я заканчивал экономику, маркетинг. Там как-то не в ходу была двоичная система исчисления )
TKV[досье] Все-таки Вы, кажется, чего-то не понимаете.
Записи вроде 2.999999999... получаются оттого, что компьютер работает в двоичной, а не десятичной системе. Число вроде 0.3=1/2*3/5 в двоичной системе в принципе не представимо точно, поскольку 1/5 записывается бесконечной двоичной дробью. И BigDecimal тут не спасет. Для сравнения: как Вы считаете, если в обычной десятичной системе Вы поделите 10 на 3, а потом умножите на 3, вас устроит результат 9.99999999...?
Что же Вы все-таки хотите?
мне нужно получить точное число именно в десятичном виде...
ну вот скажем поделил я 0.0003 на 0.0001, получилось у меня 2.999999999999998
далее мне нужно совершать с ним различные математические операции и чем больше этих операций, тем больше погрешность.
Вы таки что-то действительно не понимаете!
Попробуйте "посовершать различные математические операции". Поверьте 3 и 2.999999999999998 это одно и тоже число. Как только вы захотите вывести его на экран с необходимой точностью (в фин.расчетах это редко точнее 4ого знака после запятой), число будет преобразовано форматером в 3.000. Ибо именно так выглядит 2.999999999999998, округленное до 3 знака после запятой.
Вы просто ради интереса прикинте, сколько операций надо совершить, чтобы погрешность в 15 знаке переползла например в 3 знак (раньше она на графике заметна не станет). Очень грубо это 10 в 12 степени операций, т.е. 1'000'000'000'000. Т.е. если вы проделываете миллион операций в секунду, то у вас на это расчет уйдет 2 недели. У вас есть такие расчеты?
Единственное, где вас ждут проблемы, это в точном представлении каких-нибудь огромных сумм. Типа оборот завода, который составил:
3'000'000'000 рублей и 13 копеек. В таких расчетах часто нужна дейсвительно точность до последнего знака. Для этого служит BigDecimal. Но такие расчеты как правило предельно просты: сложить, вычесть, расчитать НДС. Тут жадничасть на несколько лишних объектов не стоит.
Но как только вы уходите из финансовов в математику или статистику, "во сколько оборот завода 1 больше оборота завода 2 на каждый день в течение 2006" точность до копейки уже никого не интересует. 4 значащих цифры (34.76%) здесь всех более чем устроят. А у вас их будет аж 15 в случае с double.
так... я тогда сначала попробую просто не округляя что-нибудь такое сделать, посмотрю на результат и доложу...
Даниэль Алиевский[досье] а что вы хотите услышать? мне нужно сделать график. На данный момент график представляет валютную пару, скажем EUR/USD по абсцисе таймфреймы, по ординате пункты. скажем если разница между валютами в разные периоды времени 1.2664, 12665, 12666 и т.д. то один минимальный пункт будет равен 0.0001
Мне нужно соответственно все это отобразить на графике
да нет, мне в принципе хватает и float... просто видимо в теории я слабоват...
например вот если так
int a = 245;
int b = 15;
float c = a/b; //16.0 хотя должно получится 16.333... видимо этот косяк исключительно из за незнания теории...
TKV[досье] Если a и b целые (типа int), то a/b - тоже целое. Усеченное в сторону нуля. Чтобы получить 16.333..., вам следовало написать, например, так: double c = (double)a / (double)b. (На самом деле достаточно привести к типу double только делимое или только делитель. Можно даже так: (a + 0.0) / b)
И еще маленький совет: не используйте float, используйте double. На современных процессорах тип float практически не имеет никаких преимуществ перед double, за исключением одного-единственного случая: когда вам требуется хранить миллионы и миллиарды вещественных чисел, и экономия памяти в два раза имеет значение.
30-ый[досье] Вообще-то Вы не совсем правы: для представления "огромных сумм" более чем достаточно встроенного типа long. 8 миллиардов миллиардов - человечество еще не научилось осваивать такие суммы, даже в постперестроечных копейках :) Более того, если double используется ограниченно - содержит либо целые числа, либо целые кратные величинам вида 1/2^k - то и этот тип будет содержать абсолютно точные значения для очень и очень немаленьких чисел, что-то типа 2^53 (не помню точно длину мантиссы). (Чем мне Java нравится - если уж вещественные числа ведут себя определенным образом, то они будут вести себя так всегда и на всех платформах. Скажем, сложив 10 раз 1.0, мы наверняка получим точно 10.0 - чего, конечно, нельзя сказать про 0.1.)
Насчет погрешностей double, пожалуй, не стоит чересчур успокаивать. Конечно, нехватка точности при операциях сложения действительно маловероятна. Но при сложных расчетах вопрос конечной точности double может "встать во весь рост" - например, при решении плохообусловленных систем уравнений. В конкретной ситуации автора, вероятно, все должно быть в порядке, но в других приложениях не стоит слепо доверять "большой точности" double.
Хаха товарищи...
Есть жёстче задача... есть дробь (A-B)/((C-B)/D))
от результата необходимо округление до целого по правилу
окр(X.3YYYY...)=X
окр(X.7YYYY...)=X+1
окр(X.5)=X+1
Для этого используем Int(x+0.5)
При получении результатом вышеприведённой формулы+0.5 периодической дроби вида X.9999... или X.5555 в переменной получаем значение близкие к X.5 и X.0 слевой или правой стороны(например 4.49999...8 или 4.0000...1 ) мы не получим правильных результатов округления...
Пример на числах Int(((7-2)/((12-2)/9))+0.5)... Получится 4... а должно быть 5
Есть ли способ решить эту проблему?
![[logo]](/site/images/logo.jpg)