Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате |
Форум программистов > Java: Общие вопросы > Что за фокусы с числами float<->double |
Автор: Aprol 21.12.2009, 17:16 | ||||
Первый фокус: Кусок кода
На тестовых данных k=10, h=2.29,xmin=3.6,xmax=26.5.Это достаточно, чтобы заполнить массив nvi Делим интервал [3.6;26.5] с шагом 2.29, значения подынтервалов записываются в nvi. По логике вещей значения должны быть следующими: 3,6 5,89 8,18 10,47 12,76 15,05 17,34 19,63 21,92 24,21 26,5 [0] = 3.6 [1] = 5.89 [2] = 8.18 [3] = 10.47 [4] = 12.76 [5] = 15.05 [6] = 17.34 [7] = 19.630001 [8] = 21.920002 [9] = 24.210003 [10] = 26.500004 Откуда эти хвосты в 7,8,910 элементах? Второе: mx=15.05
После присваивания получаем a_t= 15.050000190734863 Это откуда такой хвост? |
Автор: LSD 21.12.2009, 17:26 |
Дело в том, что float и double хранятся как двоичная дробь, а ты оперируешь десятичными дробями. И проблема в том, что не всякая десятичная дробь может быть представлена как двоичная, приходится считать приближенно. Отсюда погрешность и берется. |
Автор: Aprol 21.12.2009, 17:35 | ||
Странно, такие простые вычисления, а уже погрешность... как мне быть в такой ситуации? округлять? но по каким правилам, чтобы в других данных не обрубить мне ничего нужного
после присваивания a_t=15.05 как надо... что-то не догоняю... mx=15.05 откуда лаги... |
Автор: MaxPayneC 21.12.2009, 17:47 |
LSD, не могли бы вы рассказать подробнее на тему двоичных и десятичных дробей, или дать ссылку где почитать? |
Автор: Aprol 21.12.2009, 18:15 |
Да в данном случае задача простая разбить интервал с шагом на подинтервалы, что и делает в первом куске кода. А вообще задача состоит в оптимизации значения аргументов функции, то бишь математическая задача, и там вот такие левые хвосты не нужны... потому то алгоритм и не работает. Результат не сходится с тем что посчитано в экселе. Это как? |
Автор: LSD 21.12.2009, 18:22 | ||
Когда ты складываешь два BigDecimal числа, то старые не меняются, а создается новый с результатом вычисления суммы.
Возьми вместо float - double, выбери требуемую точность (скажем 6 знаков после запятой), и округляй после каждого шага. |
Автор: MaxPayneC 21.12.2009, 18:26 | ||
Спасибо) |
Автор: Aprol 21.12.2009, 18:33 |
Какой-то есть метод для округления дробей не до целых чисел а до требуемого разряда? |
Автор: COVD 21.12.2009, 18:37 | ||
Сравнивать результаты работы двух программ следует при одинаковым формате отображаемых данных. Например, если эксель отображает результаты расчета округляя до двух знаков после запятой, то и в другой программе следует применить аналогичный формат. |
Автор: Дрон 21.12.2009, 18:59 | ||||||
Не сочтите за наезд, но можно узнать сколько вам лет? И место учёбы? Вопросы про погрешности в float/double встречаются регулярно, я на них даже иногда подробно отвечаю. А тут вот любопытно стало, кто же их всё-таки задаёт? ![]() А насчёт округления... ну вот например до двух знаков после запятой:
Добавлено @ 19:06
Недавно уже в разделе .NET приводил пример. Считаем в уме: 1 / 3 = 0.3333(3) 1/3 * 3 = 1 0.3333(3) * 3 = 0.9999(9) ≠ 1 Вот и тут то же самое ![]() |
Автор: Aprol 21.12.2009, 19:19 |
причем тут бесконечные дроби? когда результат вполне конкретен и равен он 15.05. Это можно ручками взять и посчитать ![]() Спасибо за пример, я нашел способ. ток мне кажется округляя мы только повышаем ошибку. Раньше она появлялась в меньших разрядах, а теперь мы поднимем ее до , скажем 10тысячных... Конфиденциальные данные не разглашаю. |
Автор: LSD 21.12.2009, 19:33 |
При том, что иногда дробь, можно выразить только бесконечной периодической или непериодической десятичной (доичной, троичной и т.д.) дробью. И это по идее проходят в школе ![]() |
Автор: Дрон 21.12.2009, 19:34 | ||||||
Это в десятичных дробях он конкретен. Допустим 15.05 нормально представляется в float, но во время вычислений где-то появилось непредставимое число (считай что бесконечная дробь) и в итоге появилась погрешность. Я же пример с 1/3 не зря привёл -- результат вычислений (1 / 3) * 3 зависит от того, в какой системе мы считаем. Если считаем в обыкновенных дробях, то всё хорошо, если считаем в десятичных то есть погрешность. Попробуй ещё вариант:
У меня Java под рукой нет, но результат должен быть совсем не 0.
Не знаю. Не буду ничего утверждать, чтобы не вводить в заблуждение. Этому должны в институте учить... Меня учили, но давно ![]() Недавно, кстати, столкнулся с проблемой, что при суммировании большого количества чисел результат был близким к 0 (что-то вроде 1e-10) и, к моему удивлению, в зависимости от того, какой тип я использовал у меня получался разный знак результата, а он-то мне как раз и был нужен ![]() В Java нет встроенного типа decimal, как в C#, поэтому либо считать в double, либо в медленном BigDecimal, как сказал LSD. Только вот мне кажется, что в твоей задаче вполне должно хватать точности double. Да даже те результаты во float, которые ты пишешь вполне нормальны -- это ж последовательное приближение, оно не может быть точным... Не говоря уж о том, что в реальном мире исходные данные не бывают точными, а считать с большей точностью, чем есть в исходных данных -- бесполезно ![]() Ок, проехали ![]() |
Автор: serger 22.12.2009, 08:43 | ||
Особенно если это деньги... ![]() |
Автор: jk1 22.12.2009, 11:52 | ||
В общем случае это не так и зависит от http://ru.wikipedia.org/wiki/%D0%A3%D1%81%D1%82%D0%BE%D0%B9%D1%87%D0%B8%D0%B2%D0%BE%D1%81%D1%82%D1%8C_(%D0%B4%D0%B8%D0%BD%D0%B0%D0%BC%D0%B8%D1%87%D0%B5%D1%81%D0%BA%D0%B8%D0%B5_%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D1%8B)#.D0.90.D1.81.D0.B8.D0.BC.D0.BF.D0.B8.D0.BE.D1.82.D0.B8.D1.87.D0.B5.D1.81.D0.BA.D0.B0.D1.8F_.D1.83.D1.81.D1.82.D0.BE.D0.B9.D1.87.D0.B8.D0.B2.D0.BE.D1.81.D1.82.D1.8C численного метода. |