Comments 32
Ну очень старая тема. Перетертая очень много раз и не только на хабре…
+30
> Вопреки всем моим ожиданиям
А каковы были ваши ожидания от арифметики с плавающей точкой? 1.0? :)
Может быть тайну открою, но это не только в Java справедливо.
А каковы были ваши ожидания от арифметики с плавающей точкой? 1.0? :)
Может быть тайну открою, но это не только в Java справедливо.
+19
+1
Читал статью и недоумевал — а Джава то причем?
Кстати, по теме есть хороший текст на английском «What Every Computer Scientist Should Know About Floating-Point Arithmetic», David Goldberg, March, 1991
Доступен в т.ч. на сайте Оракл: docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
Читал статью и недоумевал — а Джава то причем?
Кстати, по теме есть хороший текст на английском «What Every Computer Scientist Should Know About Floating-Point Arithmetic», David Goldberg, March, 1991
Доступен в т.ч. на сайте Оракл: docs.oracle.com/cd/E19957-01/806-3568/ncg_goldberg.html
+3
По-моему, это уже в школе преподают, не говоря уже о университете.
+12
В какой школе, вы о чём? В школах либо учат алгоритмы чистки картошки, либо играют в Prehistoric/CS/Worms и т.д.
Ах да, ещё изучают стили Word и анимации PowerPoint.
Ах да, ещё изучают стили Word и анимации PowerPoint.
+4
Очередное поколение узнаёт про существование сопроцессора.
(шёпотом) А ещё Деда Мороза не существует.
(шёпотом) А ещё Деда Мороза не существует.
+16
Конечно не ново, но мне кажется надо каждые лет пять-семь постить такой текст. Чтобы очередное поколение «хакеров» выучило. А то будут снова деньги через double считать…
+4
Вы, конечно, будете очень смеяться, но биткойн-клиент считает именно с плавающей точкой.
(поищите «double» например в bitcoinrpc.cpp — я уж не буду копипастить код сюда).
Почему это было сделано так — мне лично не понятно.
В принципе деньги можно считать с плавающей точкой, если их количество не превышает определенного максимума и знаков после запятой тоже немного. Но зачем из буханки делать троллейбус?
(поищите «double» например в bitcoinrpc.cpp — я уж не буду копипастить код сюда).
Почему это было сделано так — мне лично не понятно.
В принципе деньги можно считать с плавающей точкой, если их количество не превышает определенного максимума и знаков после запятой тоже немного. Но зачем из буханки делать троллейбус?
0
Заинтересовался и посмотрел. Действительно на уровне API принимает double и конвертирует в int64_t. Странное решение, но на уровне протокола всё нормально. Только пользователям будем неудобно, нельзя посылать количество денег, которое бы и можно бы выразить на уровне протокола, да точности параметров API не хватает.
Ещё забавная проверка на «dAmount > 21000000.0». API не даст даже попытаться послать все деньги в мире сразу :)
Но, по крайней мере, на уровне bitcoin протокола всё в порядке.
Ещё забавная проверка на «dAmount > 21000000.0». API не даст даже попытаться послать все деньги в мире сразу :)
Но, по крайней мере, на уровне bitcoin протокола всё в порядке.
0
>>поищите «double» например в bitcoinrpc.cpp
Спасибо большое! Весьма поучительно ;)
Спасибо большое! Весьма поучительно ;)
0
Деньги можно считать через double. При этом никаких проблем с накоплением ошибки не будет, если округлять только на последнем шаге и не использовать округлённое значение в последующих расчетах.
Насколько я понимаю, главное правило при работе с валютой — никогда не использовать деление. То есть вместо деления пополам мы берем 50% (умножаем на 0.5). Таким образом мы, например, признваем, что невозможно разделить сумму на три. Можно это сделать только с заданной точностью. И это будет управляемо. B = A * 0.333
А все остальные операции — и сложение, и вычитание, и умножение не дают накопления ошибки. И разницы между использованием десятичной и двоичой системы счисления никакой.
Насколько я понимаю, главное правило при работе с валютой — никогда не использовать деление. То есть вместо деления пополам мы берем 50% (умножаем на 0.5). Таким образом мы, например, признваем, что невозможно разделить сумму на три. Можно это сделать только с заданной точностью. И это будет управляемо. B = A * 0.333
А все остальные операции — и сложение, и вычитание, и умножение не дают накопления ошибки. И разницы между использованием десятичной и двоичой системы счисления никакой.
0
При этом никаких проблем с накоплением ошибки не будет, если округлять только на последнем шагеПроблемы могут быть, так как конечное представление чисел в любом случае требует промежуточных округлений. Если длина мантиссы 23 бита, то при перемножении двух чисел имеющих 20-битную мантису ошибки будут появляться.
0
>Начиная с процессоров Pentium модели MMX модуль операций с плавающей точкой интегрирован в центральный процессор.
Это немного пораньше произошло, последний сопроцессор вроде на 386-м был.
Это немного пораньше произошло, последний сопроцессор вроде на 386-м был.
+1
+7
Иногда мне кажется, что это такой очень тонкий намёк. «Странно, почему-то не работают теги...»
Впрочем, имею право ошибиться ;)
upd: Упс, товарищ WarAngel_alk, это ответ вам.
Впрочем, имею право ошибиться ;)
upd: Упс, товарищ WarAngel_alk, это ответ вам.
0
Не хватает статьи как правильно складывать суммы с плавающей точкой, чтобы результат получался более точным.
0
Более точным в общем случае не получится, если не увеличивать разрядность мантиссы.
Да, можно порассуждать, что десять раз по 0.1 должен получиться литр… тьфу! килограмм… да нет же! единица ровно должна получиться.
А сколько должно получиться, если десять раз по 0.100000000001? А Пи-в-степени-E?
В общем, в стандартах явно указано с какой точностью считаются числа с плавающей точкой.
Для 99% вычислений — этого достаточно (но конечно нужно помнить, что вычисления не абсолютно точные)
Если вас стандартная точность не устраивает — можно исхитряться разными способами, но за счет уменьшения производительности
Да, можно порассуждать, что десять раз по 0.1 должен получиться литр… тьфу! килограмм… да нет же! единица ровно должна получиться.
А сколько должно получиться, если десять раз по 0.100000000001? А Пи-в-степени-E?
В общем, в стандартах явно указано с какой точностью считаются числа с плавающей точкой.
Для 99% вычислений — этого достаточно (но конечно нужно помнить, что вычисления не абсолютно точные)
Если вас стандартная точность не устраивает — можно исхитряться разными способами, но за счет уменьшения производительности
-3
Есть кстати весьма полезных алгоритм суммирования Кохэна (вики), который сильно уменьшает погрешность суммирования.
В некоторых случаях он позволяет получить результат с использованием float-а более точный, без этого алгоритма с использованием double. Хотя это конечно не повод повсеместно переходить с double на float, или использовать числа с плавающей точкой для денег :-) Не стоит к тому же пихать такое суммирование всюду, достаточно только в тех местах, где действительно повышенные требования к точности.
Пример:
Само суммирование (из вики):
В некоторых случаях он позволяет получить результат с использованием float-а более точный, без этого алгоритма с использованием double. Хотя это конечно не повод повсеместно переходить с double на float, или использовать числа с плавающей точкой для денег :-) Не стоит к тому же пихать такое суммирование всюду, достаточно только в тех местах, где действительно повышенные требования к точности.
Пример:
float[] floats = new float[100];
Arrays.fill(floats, 0.01f);
double[] doubles = new double[100];
Arrays.fill(doubles, 0.01);
float fsum = 0;
for (float x: floats) {
fsum += x;
}
System.out.println(fsum);
// -> выведет 0.99999934, позорная ошибка округления
double dsum = 0;
for (double x: doubles) {
dsum += x;
}
System.out.println(dsum);
// -> выведет 1.0000000000000007 - уже неплохо
System.out.println(kahanSum(floats));
// -> выведет ровно 1.0 - ура!
Само суммирование (из вики):
static float kahanSum(float... input) {
float sum = 0;
float c = 0;
for (float v : input) {
float y = v - c;
float t = sum + y;
c = (t - sum) - y;
sum = t;
}
return sum;
}
0
Просто оставлю это здесь, для будущих поколений переводчиков. Товарищ кстати из Valve. ИМХО очень компетентен.
0
0
Через 50 лет на Хабре будет очень трудно написать статью, чтобы она не была заминусована ворчливыми стариками… Помнится года 4 назад точно такая же статья стала хитом.
-2
Судя по всему, имеется в виду эта статья («Что нужно знать про арифметику с плавающей запятой»). Так а вы сравните:
1) Здесь читается тезис: «А я вот узнал, что в Java...». В статье по ссылке — полномасшабный, основательный разбор стандарта IEEE754.
2) Статья по ссылке оформлена приятнее (ну, на мой личный взгляд).
3) Тема начинает быть избитой (см. тезис и рейтинг первого комментария).
1) Здесь читается тезис: «А я вот узнал, что в Java...». В статье по ссылке — полномасшабный, основательный разбор стандарта IEEE754.
2) Статья по ссылке оформлена приятнее (ну, на мой личный взгляд).
3) Тема начинает быть избитой (см. тезис и рейтинг первого комментария).
0
+8
Я тоже не пойму причем тут Java.
Насколько помню, округлять FP числа при выводе учат еще в школе, ну или институте курсе так на первом.
Инфа хранится в двоичной форме, выводится в десятичной, отсюда все проблемы.
А ну если хотите точных вычислений с точкой для денег или еще чего, используйте BigDecimal, если на то пошло.
Насколько помню, округлять FP числа при выводе учат еще в школе, ну или институте курсе так на первом.
Инфа хранится в двоичной форме, выводится в десятичной, отсюда все проблемы.
А ну если хотите точных вычислений с точкой для денег или еще чего, используйте BigDecimal, если на то пошло.
0
> Инфа хранится в двоичной форме, выводится в десятичной, отсюда все проблемы.
Побуду Капитаном Очевидностью, но скажу, что проблемы не только в этом.
Ни при каком способе хранения, ни при каком выводе ни в какой системе счисления вы не сможете точно представить любое вещественное число, просто потому что мощность множества вещественных чисел равна континууму, а количество разрядов в компьютере (и даже атомов в Солнечной системе, и даже фотонов в Галактике) конечно. Следовательно, для любого представления чисел в компьютере существуют числа которые не могут быть представлены (и обработаны) точно.
Побуду Капитаном Очевидностью, но скажу, что проблемы не только в этом.
Ни при каком способе хранения, ни при каком выводе ни в какой системе счисления вы не сможете точно представить любое вещественное число, просто потому что мощность множества вещественных чисел равна континууму, а количество разрядов в компьютере (и даже атомов в Солнечной системе, и даже фотонов в Галактике) конечно. Следовательно, для любого представления чисел в компьютере существуют числа которые не могут быть представлены (и обработаны) точно.
0
Sign up to leave a comment.
Пара слов о числах с плавающей точкой в Java