Модераторы: LSD, AntonSaburov

Поиск:

Ответ в темуСоздание новой темы Создание опроса
> Что за фокусы с числами float<->double, появляются хвосты в числах 
:(
    Опции темы
Aprol
Дата 21.12.2009, 17:16 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Первый фокус: Кусок кода
Код

public class KoordSpusk {
    ArrayList<Float> nv = new ArrayList<Float>();
    float[] nvi;
    float[] ni;
    float mx = 0, dx = 0, sko = 0;
    int k;

    public void prepare() {

        //чтение выборки
        try {
            FileInputStream inputStream = new FileInputStream("D:\\stat.txt");
            DataInputStream dataInputStream = new DataInputStream(inputStream);
            String line = "";
            while ((line = dataInputStream.readLine()) != null) {
                nv.add(Float.valueOf(line));
           
            }
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
  float xmin = nv.get(0);
        float xmax = nv.get(0);
        for (int i = 0; i < nv.size(); i++) {
            if (xmin > nv.get(i)) xmin = nv.get(i);
            if (xmax < nv.get(i)) xmax = nv.get(i);
        }
        k = 10;
        float h = Math.abs(xmin - xmax) / (k);
        nvi = new float[k + 1];
        nvi[0]=xmin;
        for (int i = 1; i < nvi.length; i++) {
            nvi[i] = nvi[i-1] + h;
        }

На тестовых данных 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
Код

 double a_t = mx,

После присваивания получаем
a_t= 15.050000190734863

Это откуда такой хвост?
PM MAIL   Вверх
LSD
Дата 21.12.2009, 17:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

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



Цитата(Aprol @  21.12.2009,  17:16 Найти цитируемый пост)
Откуда эти хвосты в 7,8,910 элементах?

Дело в том, что float и double хранятся как двоичная дробь, а ты оперируешь десятичными дробями. И проблема в том, что не всякая десятичная дробь может быть представлена как двоичная, приходится считать приближенно. Отсюда погрешность и берется.


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
Aprol
Дата 21.12.2009, 17:35 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



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

 double a_t = 15.05,

после присваивания a_t=15.05  как надо... что-то не догоняю... mx=15.05 откуда лаги...

Это сообщение отредактировал(а) Aprol - 21.12.2009, 17:39
PM MAIL   Вверх
MaxPayneC
Дата 21.12.2009, 17:47 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



LSD, не могли бы вы рассказать подробнее на тему двоичных и десятичных дробей, или дать ссылку где почитать?
PM   Вверх
LSD
Дата 21.12.2009, 18:02 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

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



Цитата(Aprol @  21.12.2009,  17:35 Найти цитируемый пост)
как мне быть в такой ситуации? округлять? но по каким правилам, чтобы в других данных не обрубить мне ничего нужного

Ну тут вопрос, в том что за вычисления ты делаешь. В принципе для расчетов с фиксированной точностью (n-знаков после запятой) хорошо подоходит BigDecimal, но у него один недостаток он медленно работает и требует много памяти (каждое вычисление создает новый объект). Еще есть такой класс Fraction он делает вычисления оперируя натуральными дробями, можешь попробовать его. Но вообще, надо побольше знать о задаче, чтобы посоветовать что-то конкретное.



Цитата(MaxPayneC @  21.12.2009,  17:47 Найти цитируемый пост)
LSD, не могли бы вы рассказать подробнее на тему двоичных и десятичных дробей, или дать ссылку где почитать? 

Базовое это стандарт IEEE 754. Еще есть IEEE-754 Conversion Applet, который показывает как это число будет представлено в памяти. Ну и можешь в качестве упражнения на бумажке попробовать привести дробь 5/100 к основанию 2^x smile


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
Aprol
Дата 21.12.2009, 18:15 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Да в данном случае задача простая разбить интервал с шагом на подинтервалы, что и делает в первом куске кода.
А вообще задача состоит в оптимизации значения аргументов функции, то бишь математическая задача, и там вот такие левые хвосты не нужны... потому то алгоритм и не работает. Результат не сходится с тем что посчитано в экселе.

Цитата(LSD @  21.12.2009,  18:02 Найти цитируемый пост)
(каждое вычисление создает новый объект).

Это как? 
PM MAIL   Вверх
LSD
Дата 21.12.2009, 18:22 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

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



Цитата(Aprol @  21.12.2009,  18:15 Найти цитируемый пост)
Это как?  

Когда ты складываешь два BigDecimal числа, то старые не меняются, а создается новый с результатом вычисления суммы.



Цитата(Aprol @  21.12.2009,  18:15 Найти цитируемый пост)
А вообще задача состоит в оптимизации значения аргументов функции, то бишь математическая задача, и там вот такие левые хвосты не нужны... потому то алгоритм и не работает. Результат не сходится с тем что посчитано в экселе.

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


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
MaxPayneC
Дата 21.12.2009, 18:26 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(LSD @  21.12.2009,  18:02 Найти цитируемый пост)
Базовое это стандарт IEEE 754. Еще есть IEEE-754 Conversion Applet, который показывает как это число будет представлено в памяти. Ну и можешь в качестве упражнения на бумажке попробовать привести дробь 5/100 к основанию 2^x smile


Спасибо)
PM   Вверх
Aprol
Дата 21.12.2009, 18:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(LSD @  21.12.2009,  18:22 Найти цитируемый пост)
округляй после каждого шага. 

Какой-то есть метод для округления дробей не до целых чисел а до требуемого разряда?
PM MAIL   Вверх
COVD
Дата 21.12.2009, 18:37 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Эксперт
***


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

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



Цитата

Результат не сходится с тем что посчитано в экселе.


Сравнивать результаты работы двух программ следует при одинаковым формате отображаемых данных. Например, если эксель отображает результаты расчета округляя до двух знаков после запятой, то и в другой программе следует применить аналогичный формат.

PM MAIL   Вверх
Дрон
Дата 21.12.2009, 18:59 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java-ненавистник :)
****


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

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



Цитата(Aprol @  21.12.2009,  18:33 Найти цитируемый пост)
Какой-то есть метод для округления дробей не до целых чисел а до требуемого разряда? 

Не сочтите за наезд, но можно узнать сколько вам лет? И место учёбы? Вопросы про погрешности в float/double встречаются регулярно, я на них даже иногда подробно отвечаю. А тут вот любопытно стало, кто же их всё-таки задаёт? smile

А насчёт округления... ну вот например до двух знаков после запятой:
Код
float a = 1.234567;
float b = Math.Round(a * 100) / 100f;


Добавлено @ 19:06
Цитата(Aprol @  21.12.2009,  17:35 Найти цитируемый пост)
после присваивания a_t=15.05  как надо... что-то не догоняю... mx=15.05 откуда лаги...

Недавно уже в разделе .NET приводил пример. Считаем в уме:
1 / 3 = 0.3333(3)
1/3 * 3 = 1
0.3333(3) * 3 = 0.9999(9)  1
Вот и тут то же самое smile 

Это сообщение отредактировал(а) Дрон - 21.12.2009, 19:09


--------------------
Да. Именно так.
PM   Вверх
Aprol
Дата 21.12.2009, 19:19 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(Дрон @  21.12.2009,  18:59 Найти цитируемый пост)
1 / 3 = 0.3333(3)
1/3 * 3 = 1
0.3333(3) * 3 = 0.9999(9) ≠ 1

причем тут бесконечные дроби? когда результат вполне конкретен и равен он 15.05. Это можно ручками взять и посчитать smile 
Спасибо за пример, я нашел способ. ток мне кажется округляя мы только повышаем ошибку. Раньше она появлялась в меньших разрядах, а теперь мы поднимем ее до , скажем 10тысячных...

Конфиденциальные данные не разглашаю.
PM MAIL   Вверх
LSD
Дата 21.12.2009, 19:33 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Leprechaun Software Developer
****


Профиль
Группа: Модератор
Сообщений: 15718
Регистрация: 24.3.2004
Где: Dublin

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



Цитата(Aprol @  21.12.2009,  19:19 Найти цитируемый пост)
причем тут бесконечные дроби?

При том, что иногда дробь, можно выразить только бесконечной периодической или непериодической десятичной (доичной, троичной и т.д.) дробью. И это по идее проходят в школе smile 


--------------------
Disclaimer: this post contains explicit depictions of personal opinion. So, if it sounds sarcastic, don't take it seriously. If it sounds dangerous, do not try this at home or at all. And if it offends you, just don't read it.
PM MAIL WWW   Вверх
Дрон
Дата 21.12.2009, 19:34 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Java-ненавистник :)
****


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

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



Цитата(Aprol @  21.12.2009,  19:19 Найти цитируемый пост)
причем тут бесконечные дроби? когда результат вполне конкретен и равен он 15.05

Это в десятичных дробях он конкретен. Допустим 15.05 нормально представляется в float, но во время вычислений где-то появилось непредставимое число (считай что бесконечная дробь) и в итоге появилась погрешность.
Я же пример с 1/3 не зря привёл -- результат вычислений (1 / 3) * 3 зависит от того, в какой системе мы считаем. Если считаем в обыкновенных дробях, то всё хорошо, если считаем в десятичных то есть погрешность.

Попробуй ещё вариант:
Код
System.out.println(0.1 * 3 - 0.3);

У меня Java под рукой нет, но результат должен быть совсем не 0.

Цитата(Aprol @  21.12.2009,  19:19 Найти цитируемый пост)
ток мне кажется округляя мы только повышаем ошибку. Раньше она появлялась в меньших разрядах, а теперь мы поднимем ее до , скажем 10тысячных

Не знаю. Не буду ничего утверждать, чтобы не вводить в заблуждение. Этому должны в институте учить... Меня учили, но давно smile
Недавно, кстати, столкнулся с проблемой, что при суммировании большого количества чисел результат был близким к 0 (что-то вроде 1e-10) и, к моему удивлению, в зависимости от того, какой тип я использовал у меня получался разный знак результата, а он-то мне как раз и был нужен smile
В Java нет встроенного типа decimal, как в C#, поэтому либо считать в double, либо в медленном BigDecimal, как сказал LSD.

Только вот мне кажется, что в твоей задаче вполне должно хватать точности double. Да даже те результаты во float, которые ты пишешь вполне нормальны -- это ж последовательное приближение, оно не может быть точным... Не говоря уж о том, что в реальном мире исходные данные не бывают точными, а считать с большей точностью, чем есть в исходных данных -- бесполезно smile

Цитата(Aprol @  21.12.2009,  19:19 Найти цитируемый пост)
Конфиденциальные данные не разглашаю. 

Ок, проехали smile



--------------------
Да. Именно так.
PM   Вверх
serger
Дата 22.12.2009, 08:43 (ссылка) | (нет голосов) Загрузка ... Загрузка ... Быстрая цитата Цитата


Опытный
**


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

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



Цитата(Дрон @  21.12.2009,  19:34 Найти цитируемый пост)
Только вот мне кажется, что в твоей задаче вполне должно хватать точности double. Да даже те результаты во float, которые ты пишешь вполне нормальны -- это ж последовательное приближение, оно не может быть точным... Не говоря уж о том, что в реальном мире исходные данные не бывают точными, а считать с большей точностью, чем есть в исходных данных -- бесполезно smile

Особенно если это деньги...  smile 


--------------------
упс!
PM MAIL WWW Skype GTalk Jabber   Вверх
Ответ в темуСоздание новой темы Создание опроса
Правила форума "Java"
LSD   AntonSaburov
powerOn   tux
javastic
  • Прежде, чем задать вопрос, прочтите это!
  • Книги по Java собираются здесь.
  • Документация и ресурсы по Java находятся здесь.
  • Используйте теги [code=java][/code] для подсветки кода. Используйтe чекбокс "транслит", если у Вас нет русских шрифтов.
  • Помечайте свой вопрос как решённый, если на него получен ответ. Ссылка "Пометить как решённый" находится над первым постом.
  • Действия модераторов можно обсудить здесь.
  • FAQ раздела лежит здесь.

Если Вам помогли, и атмосфера форума Вам понравилась, то заходите к нам чаще! С уважением, LSD, AntonSaburov, powerOn, tux, javastic.

 
0 Пользователей читают эту тему (0 Гостей и 0 Скрытых Пользователей)
0 Пользователей:
« Предыдущая тема | Java: Общие вопросы | Следующая тема »


 




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


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

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