Xpoint
   [напомнить пароль]

Математические оперции с double и BigDecimal

Метки: [без меток]
[арх]
2007-09-06 21:01:45 [обр] TKV(4/12)[досье]
Как-то раньше не особо приходилось этим заниматься, в силу отсутствия подобных задач. Но вот сейчас встала такая необходимость, в ходе мат. операций с данными типа double, необходимо получать точные значения. Почитал одну статью... Там приводится как пример BigDecimal... В принципе хорошо, но из за затрат на создание объекта, не хотелось бы использовать.
Посоветуйте, есть ли какие-либо другие варианты
спустя 11 минут [обр] TKV(4/12)[досье]
эм... подскажите еще как определяется число десятичных разрядов
спустя 1 час 26 минут [обр] Даниэль Алиевский(35/125)[досье]

Что значит "точные" применительно к типу double? Это ведь тип "с плавающей точкой", по определению допускающий округление.

Сформулируйте задачу полностью, пожалуйста.

спустя 30 минут [обр] TKV(4/12)[досье]
Даниэль Алиевский[досье]
ну вот например при делении 0.003/0.001
в результате получается 2.999.... или скажем если поделить 0.0035/0.001
в результате врядли получится 3.5
спустя 2 часа 59 минут [обр] TKV(4/12)[досье]
хотя если есть метод, в чем я сомневаюсь, который округлял бы до точного значения...
спустя 6 часов [обр] Алексей Полушин(0/231)[досье]
В двоичной системе 0.003 и 0.001 - бескоечные периодические дроби и не могут быть точно прдставлены в компьютере.
спустя 1 час 32 минуты [обр] TKV(4/12)[досье]
Алексей Полушин[досье]
тем неменее если воспользоваться классом BigDecimal, который кстати на сколько я понял и предназначен для операций с числами с плавающей точкой, в результате которых нужно получить точные значения исходя из десятичной системы, то все получается прекрасно
спустя 2 минуты [обр] TKV(4/12)[досье]
В двоичной системе 0.003 и 0.001 - бескоечные периодические дроби и не могут быть точно прдставлены в компьютере.
Кстати вынужден констатировать тот факт что я полный профан. Что впрочем я и так знал )) что впрочем легко исправить со временем ))
спустя 4 минуты [обр] Даниэль Алиевский(35/125)[досье]

Существуют пакеты для обработки точных рациональных чисел, представленных парой числитель/знаменатель. Это бывает важно в некоторых "тяжелых" математических задачах, вроде решения плохо обусловленных систем линейных уравнений. В Java API такого, разумеется, нет, и вряд ли Вам нужно это.

BigDecimal позволяет точно представлять только конечные двоичные дроби. При представлении произвольного рационального числа точность будет сколь угодно большой, но конечной. При представлении иррационального sqrt(2.0) Вам не поможет даже пакет обработки рациональных чисел - такое число в принципе нельзя точно представить на компьютере.

Расскажите, а зачем и где Вам понадобились точные результаты?

спустя 2 часа 4 минуты [обр] 30-ый(59/584)[досье]

Если у вас математическая или физическая задача, типа расчета каких нибудь концентраций или проекций на плоскость, то считайте все в double, а потом (если очень хочется красиво распечатать результат) конвертируйте в BigDezimal и округлите по вкусу. Либо можно double сразу форматеру передать, он сам округлит.

Если у вас финансовая задача, то считайте сразу все в BigDezimal, спокойней спать будуте ;-)

спустя 22 минуты [обр] TKV(4/12)[досье]

Даниэль Алиевский[досье]

При представлении иррационального sqrt(2.0)

 нет, такая задача близко не стоит ))

30-ый[досье], Даниэль Алиевский[досье]
На самом деле нужно построить график котировки валют или как там он называется, я не специалист пока что в этой области ) только начал. В основе графика лежат данные о разнице между валютными парами, например USD/EUR ну и естественно там такие значения как 1.2959, 1.2961...
при чем шаг в данном случае равен 0.0001 поэтому даже небольшие погрешности в ходе вычисления координат, получаются довольно заметными на графике

спустя 17 минут [обр] TKV(4/12)[досье]
кстати наперед неизвестно до какого разряда нужно округлять
30-ый[досье] а что за форматер? какой пакет?
спустя 16 минут [обр] 30-ый(59/584)[досье]
Сории, неудачную ссылку приверстал. Речь идет о NumberFormat, точнее о его потомке DecimalFormat
спустя 8 минут [обр] 30-ый(59/584)[досье]

Издеваетесь? Double имеет точность в 15 значащих цифр. Не существет такого графика, где бы такая точность была бы недостаточна!

Внимание, кстати! В арифметике с плав.запятной важна не абсолютная точность, а количество значащих цифр. Таким образом число 123456789 требует 9 значащих цифр, а 0.0000000012 лишь две. Таким образом для представления (без потреть) второго числа вам хватит даже менее точного формата float.

Советую перечитать теорию перед началом программирования.

спустя 8 минут [обр] TKV(4/12)[досье]

30-ый[досье]
эм... вот пример

  double d = 29.0 * 0.01;
  System.out.println(d);  //0.29
  System.out.println((int) (d * 100)); //28

вы верно не совсем точно меня поняли, я ничего не имею против Double или Float. Но после мат. операций (как-то умножение, деление) получаются не совсем точные, с точки зрения исчисления в десятичной системе, результаты.

спустя 8 минут [обр] TKV(4/12)[досье]
или я что-то не понимаю :(
спустя 38 минут [обр] TKV(4/12)[досье]
так, теорию я прочитал... очень интересно, образование у меня не математическое, поэтому раньше как-то особо не считал нужным вникать
вот статья интересная по теме
http://www.ibm.com/developerworks/ru/library/j-jtp0114/index.html
спустя 58 минут [обр] 30-ый(59/584)[досье]

У вас в d видимо лежит 0.289999999999999. Т.е. 0.29 с точностью до 15 знака. Думаю такая точность должна вас более чем устроить :-)

Мораль: не надо делать "(int)d". Пользуйтесь функциями округления из класса Math.

З.Ы. Математического образования иметь не нужно - это все написано в любой книжке по основам программирования.

спустя 13 минут [обр] TKV(4/12)[досье]

30-ый[досье]
вы статью читали? )) мне не нужно округлять, нужны точные десятичные значения
при делении 0.0003 на 0.0001 мне нужно получить 3, при чем без округлений, а не 2.99999999...

А если у меня допустим такое деление 0.00029 на 0.0001
в результате мне нужно получить 2.9, а не те же 2.899999... и уж тем более меня не устаивает round, ceil или floor

спустя 1 минуту [обр] TKV(4/12)[досье]
30-ый[досье]
виноват, образование нужно специализированное, но я заканчивал экономику, маркетинг. Там как-то не в ходу была двоичная система исчисления )
спустя 45 минут [обр] Даниэль Алиевский(35/125)[досье]

TKV[досье] Все-таки Вы, кажется, чего-то не понимаете.
Записи вроде 2.999999999... получаются оттого, что компьютер работает в двоичной, а не десятичной системе. Число вроде 0.3=1/2*3/5 в двоичной системе в принципе не представимо точно, поскольку 1/5 записывается бесконечной двоичной дробью. И BigDecimal тут не спасет. Для сравнения: как Вы считаете, если в обычной десятичной системе Вы поделите 10 на 3, а потом умножите на 3, вас устроит результат 9.99999999...?

Что же Вы все-таки хотите?

спустя 1 час 3 минуты [обр] TKV(4/12)[досье]
Даниэль Алиевский[досье]
мне нужно получить точное число именно в десятичном виде...
ну вот скажем поделил я 0.0003 на 0.0001, получилось у меня 2.999999999999998
далее мне нужно совершать с ним различные математические операции и чем больше этих операций, тем больше погрешность.
спустя 3 часа 1 минуту [обр] Даниэль Алиевский(35/125)[досье]
TKV[досье] Так и в десятичной системе будут погрешности, просто при других делителях. Двоичная система позволяет точно делить на степени двойки (до какого-то предела), десятичная - на степени двойки и пятерки (тоже до какого-то предела). На тройку или семерку все равно не получится поделить точно, кроме случаев, когда исходное число является их целым кратным. Еще раз задаю вопрос: что Вам, собственно, нужно?
спустя 45 минут [обр] 30-ый(59/584)[досье]

Вы таки что-то действительно не понимаете!

Попробуйте "посовершать различные математические операции". Поверьте 3 и 2.999999999999998 это одно и тоже число. Как только вы захотите вывести его на экран с необходимой точностью (в фин.расчетах это редко точнее 4ого знака после запятой), число будет преобразовано форматером в 3.000. Ибо именно так выглядит 2.999999999999998, округленное до 3 знака после запятой.

Вы просто ради интереса прикинте, сколько операций надо совершить, чтобы погрешность в 15 знаке переползла например в 3 знак (раньше она на графике заметна не станет). Очень грубо это 10 в 12 степени операций, т.е. 1'000'000'000'000. Т.е. если вы проделываете миллион операций в секунду, то у вас на это расчет уйдет 2 недели. У вас есть такие расчеты?

спустя 8 минут [обр] 30-ый(59/584)[досье]

Единственное, где вас ждут проблемы, это в точном представлении каких-нибудь огромных сумм. Типа оборот завода, который составил:
3'000'000'000 рублей и 13 копеек. В таких расчетах часто нужна дейсвительно точность до последнего знака. Для этого служит BigDecimal. Но такие расчеты как правило предельно просты: сложить, вычесть, расчитать НДС. Тут жадничасть на несколько лишних объектов не стоит.

Но как только вы уходите из финансовов в математику или статистику, "во сколько оборот завода 1 больше оборота завода 2 на каждый день в течение 2006" точность до копейки уже никого не интересует. 4 значащих цифры (34.76%) здесь всех более чем устроят. А у вас их будет аж 15 в случае с double.

спустя 1 час 10 минут [обр] TKV(4/12)[досье]

так... я тогда сначала попробую просто не округляя что-нибудь такое сделать, посмотрю на результат и доложу...

Даниэль Алиевский[досье] а что вы хотите услышать? мне нужно сделать график. На данный момент график представляет валютную пару, скажем EUR/USD по абсцисе таймфреймы, по ординате пункты. скажем если разница между валютами в разные периоды времени 1.2664, 12665, 12666 и т.д. то один минимальный пункт будет равен 0.0001
Мне нужно соответственно все это отобразить на графике

спустя 4 минуты [обр] Даниэль Алиевский(35/125)[досье]
Если вам достаточно точности 0.0001, то чем же вас не устраивает тип double? Вам уже неоднократно намекали, что точность этого типа данных намного выше.
спустя 23 минуты [обр] TKV(4/12)[досье]
Даниэль Алиевский[досье]
да нет, мне в принципе хватает и float... просто видимо в теории я слабоват...
например вот если так
int a = 245;
int b = 15;
float c = a/b; //16.0 хотя должно получится 16.333... видимо этот косяк исключительно из за незнания теории...
спустя 40 минут [обр] TKV(4/12)[досье]
вроде бы все нормально, но полное понимание еще не пришло
спустя 11 часов [обр] Даниэль Алиевский(35/125)[досье]

TKV[досье] Если a и b целые (типа int), то a/b - тоже целое. Усеченное в сторону нуля. Чтобы получить 16.333..., вам следовало написать, например, так: double c = (double)a / (double)b. (На самом деле достаточно привести к типу double только делимое или только делитель. Можно даже так: (a + 0.0) / b)

И еще маленький совет: не используйте float, используйте double. На современных процессорах тип float практически не имеет никаких преимуществ перед double, за исключением одного-единственного случая: когда вам требуется хранить миллионы и миллиарды вещественных чисел, и экономия памяти в два раза имеет значение.

спустя 1 час 7 минут [обр] Даниэль Алиевский(35/125)[досье]

30-ый[досье] Вообще-то Вы не совсем правы: для представления "огромных сумм" более чем достаточно встроенного типа long. 8 миллиардов миллиардов - человечество еще не научилось осваивать такие суммы, даже в постперестроечных копейках :) Более того, если double используется ограниченно - содержит либо целые числа, либо целые кратные величинам вида 1/2^k - то и этот тип будет содержать абсолютно точные значения для очень и очень немаленьких чисел, что-то типа 2^53 (не помню точно длину мантиссы). (Чем мне Java нравится - если уж вещественные числа ведут себя определенным образом, то они будут вести себя так всегда и на всех платформах. Скажем, сложив 10 раз 1.0, мы наверняка получим точно 10.0 - чего, конечно, нельзя сказать про 0.1.)

Насчет погрешностей double, пожалуй, не стоит чересчур успокаивать. Конечно, нехватка точности при операциях сложения действительно маловероятна. Но при сложных расчетах вопрос конечной точности double может "встать во весь рост" - например, при решении плохообусловленных систем уравнений. В конкретной ситуации автора, вероятно, все должно быть в порядке, но в других приложениях не стоит слепо доверять "большой точности" double.

спустя 11 дней [обр] Alex ilmarranen[досье]

Хаха товарищи...
Есть жёстче задача... есть дробь (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

Есть ли способ решить эту проблему?

спустя 9 дней [обр] TKV(4/12)[досье]
всем спасибо, график вроде бы получился приличный, точность при вычислении та что нужно.
спустя 8 часов [обр] 30-ый(59/584)[досье]
Даниэль Алиевский[досье] "long" конечно хорош, но уж больно в нем не удобно хранить копейки. Чем возиться с самодельным умножителем на 100, проще взять BigDecimal. Там в частности есть много полезных методов для округления и регулировки точности.
Powered by POEM™ Engine Copyright © 2002-2005