Внесу небольшие исправления опечаток в код и подведу грустные итоги:
1) Исправления
вместо —
f ( _d06, _d059999999999999998, "0.06", "0.059999999999999998" );
f ( _d01, _d059999999999999998, "0.06", "0.059999999999999998" );
правим на :
f ( _d06, _d059999999999999998, "0.06", "0.059999999999999998" );
f ( _d06, _d059999999999999998, "0.06", "0.059999999999999998" );
2) Итоги.
Я плакаль. Ни один не догнал в чем суть "проблемы" но поучений было выданно "кучками".
N>Это Питон; '%' это аналог форматирования по printf. Как видите, после '%.53g' уже ничего нового нет — представление вывелось точно, и это никак не 0.059999999999999998
А причем тут нахрен питон — если тут С++ и формат double — 64 бита, 8 байт... Какие 53g )))
N>Разберитесь с этими всеми деталями и тогда сможете сформировать правильные вопросы. N>Ну и рядом уже прозвучали советы, что надо начинать с алгоритма. Присоединяюсь к ним.
Очень грустно. Я тут давеча собеседование проходил, мне какие то вопросы с умным видом задавали с целью узнать не обманываю ли я их про себя — программист ли я.
Глядя на все выше прочитанное печаль берет — сколько народу надо гнать в шею. Атасс. ))
Но попробую упростить понимание для все —
double _d3 = 0.05 + 0.01;
double _d4 = 0.6 / 10.;
Чему должны быть равны d3 И d4?
Надеюсь сумели посчитать — 0.06.
Ну дык — возмите и стравните их нахрен.
Достал квт совсем.
А>Чему должны быть равны d3 И d4? А>Надеюсь сумели посчитать — 0.06.
Фигня. Это — на бумаге.
А в компьютере 0.1 точно в двоичную систему не переводится. Ибо периодическая дробь при переводе получается.
Она там либо с недостатком, либо с избытком. Сие зависит от некоторых битов в регистрах сопроцессоре.
Там 4 (четыре) варианта округления, между прочим! А>Ну дык — возмите и стравните их нахрен. А>Достал квт совсем.
Вместо истерики — повторите матчасть — сопроцессор Intel.
Хочешь быть счастливым — будь им!
Без булдырабыз!!!
А>Чему должны быть равны d3 И d4? А>Надеюсь сумели посчитать — 0.06.
хороший пример, вот на нем хорошо поучиться. результат сложения не обязан быть 0.6. почему? выше уже написали почему : из-за неточного представления десятичных дробей в виде double
Здравствуйте, Аноним, Вы писали:
А>Я плакаль. Ни один не догнал в чем суть "проблемы" но поучений было выданно "кучками".
Потому что Вы так описываете, что никто из ответивших не понял, что же именно хотелось.
Потому и идут встречные гадания на кофейной гуще и попытки применить /dev/telepathy.
Описывайте сразу нормально и такого не будет.
N>>Это Питон; '%' это аналог форматирования по printf. Как видите, после '%.53g' уже ничего нового нет — представление вывелось точно, и это никак не 0.059999999999999998 А>А причем тут нахрен питон — если тут С++ и формат double — 64 бита, 8 байт... Какие 53g )))
При том, что методы одинаковы и если Вы повторите то же самое на C++, получите ровно те же результаты. Хотите подтверждения? Рисуем код:
[c++]
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
double a = 0.06;
cout << setprecision(60) << a << endl;
return 0;
}
[/c++]
На Питоне я показал результаты с тем же double внутри и тем же форматированием. Зато на нём не нужно компилировать, потому удобнее проверять такие простые вещи.
N>>Разберитесь с этими всеми деталями и тогда сможете сформировать правильные вопросы. N>>Ну и рядом уже прозвучали советы, что надо начинать с алгоритма. Присоединяюсь к ним. А>Очень грустно. Я тут давеча собеседование проходил, мне какие то вопросы с умным видом задавали с целью узнать не обманываю ли я их про себя — программист ли я. А>Глядя на все выше прочитанное печаль берет — сколько народу надо гнать в шею. Атасс. ))
Пока что кандидат на изгнание тут один — Вы. За полное чайниковство в области работы с плавучкой и неумение хоть как-то внятно сформулировать свой вопрос но при этом уже куча амбиций с наездами.
А>Но попробую упростить понимание для все —
А>Чему должны быть равны d3 И d4? А>Надеюсь сумели посчитать — 0.06. А>Ну дык — возмите и стравните их нахрен.
Для Вас новость, что вычисления с плавающей точкой в принципе неточные? И что ни одно число, если оно не кратно степени двойки, не может быть представлено точно в каком-то из стандартных IEEE binary форматах? Тогда у Вас ещё очень много открытий. Вот, например, второй пример, которому учат в вузе на соответствующем курсе:
#include <stdio.h>
int main() {
double i, last;
for (i = 0.0; i < 10.0; i += 0.1)
last = i;
printf("%g\n", last);
return 0;
}
Как вы думаете, какой ответ она напишет? 9.9? Нет, она напишет 10. Потому что внутри этот 10 является 9.9999999999999804600747665972448885440826416015625.
И вот там же на лекциях чётко говорят: хотите сравнивать числа на равенство, полученные из разных даже очень простых вычислений? Нет проблем, определите так называемый эпсилон для такого сравнения и проверяйте abs(_d3 — _d4) < epsilon.
Чему этот epsilon будет равен в Вашем случае — не знаю. Но если Вы хотите двух цифр после запятой, он равен 0.005.
А>Достал квт совсем.
Конечно, он "достанет", если, нифига в теме не зная и не представляя особенности, сунуться с заявлением "почините-ка это мне", считая, что это какие-то "ошибки представления". Нету тут ошибок представления, не надейтесь. Есть законно сделанные по всем правилам вычисления и есть естественные проблемы округлений при таких вычислениях. Не нравится — у Вас есть масса вариантов:
1. Применить вместо этого библиотеку работы с плавающей точкой с десятичными порядком и мантиссой. Это очень правильный метод, если задача — не из матфизики, а из финансов, бухгалтерии и т.д.
2. Применять округление вручную, как в прошлом моём ответе.
3. Сравнивать через epsilon, как в этом сообщении.
4. Уйти вообще от проблемы сравнения через замену алгоритма.
5. Бросить всё и заплакать.
Здравствуйте, netch80, Вы писали:
N>... N>И вот там же на лекциях чётко говорят: хотите сравнивать числа на равенство, полученные из разных даже очень простых вычислений? Нет проблем, определите так называемый эпсилон для такого сравнения и проверяйте abs(_d3 — _d4) < epsilon. N>Чему этот epsilon будет равен в Вашем случае — не знаю. Но если Вы хотите двух цифр после запятой, он равен 0.005. N>... N>Выбирайте.
Кстати, epsilon не всегда помогает. Например, как в вышеприведённом цикле.
У меня был реальный случай.
В настоящем проекте использовались не константы и вычисления были раскиданы по большому коду,
а для простоты
float a,b,c,d,e;
int Result;
a = 16.0;
b = 2.0;
c = 3.0;
d = a + b / c;
e = d * 3;
if(e<50)
Result = 0;
else
Result = 5;
Так вот, на х86 результат равен нулю, а при портировании на один контроллер — 5
При анализе выяснилось, что на контроллере библиотека эмуляции плавающей точки
вычислила, что e точно равно 50.
Хотя на х86, как здесь и пишут, 49.9999980.
epsilon помогает при сравнении на равенство, а при сравнении на больше/меньше могут быть интересные эффекты.
Здравствуйте, icWasya, Вы писали:
W>epsilon помогает при сравнении на равенство, а при сравнении на больше/меньше могут быть интересные эффекты.
epsilon надо применять при любом сравнении, а не только на равенство.
Здравствуйте, icWasya, Вы писали:
W>У меня был реальный случай. W>В настоящем проекте использовались не константы и вычисления были раскиданы по большому коду, W>а для простоты
W>float a,b,c,d,e; W>int Result;
W> a = 16.0; W> b = 2.0; W> c = 3.0;
W> d = a + b / c;
W> e = d * 3;
W> if(e<50) W> Result = 0; W> else W> Result = 5;
W>Так вот, на х86 результат равен нулю, а при портировании на один контроллер — 5 W>При анализе выяснилось, что на контроллере библиотека эмуляции плавающей точки W>вычислила, что e точно равно 50. W>Хотя на х86, как здесь и пишут, 49.9999980.
Кто пишет?
Я сейчас проверил — ровно 50. Это на: FreeBSD/i386 во всех режимах размерности (single, double, extended) через fpsetprec(); FreeBSD/amd64 опять со всеми размерами (хотя не уверен, что оно на что-то влияет — в коде видны mulss и divss); Linux/x86-64 (тоже mulss, divss).
Я даже пометил все переменные как volatile, чтобы запретить транслятору что-то оптимизировать. Всё равно не могу добиться никаких 49.9999980. Надо уточнить обстоятельства у того, кто выдал это число.
W>epsilon помогает при сравнении на равенство, а при сравнении на больше/меньше могут быть интересные эффекты.
Да, там добавляется вариант "ну типа меньше, но мы ещё не уверены"...
А>Чему должны быть равны d3 И d4? А>Надеюсь сумели посчитать — 0.06.
В десятичной системе (на бумаге) — да, но процессоры обычно двоичные, поэтому в _данном_ случае значение не будет в точности равным 0.06.
Вот вывод из Питона (лень С++ поднимать, там примерно та же картина будет):
Всё это очень известные грабли, усугубляемые тем, что в современных отладчиках любят округлять значения, т.е. вместо 0.060000000000000004718447854657 программисту могут показать обманчивые 0.06.
Здравствуйте, B0FEE664, Вы писали:
BFE>epsilon надо применять при любом сравнении, а не только на равенство.
зависит от специфики задачи. я бы рекомендовал пользоваться точным сравнением в купе с проверкой на равенство с точностью до epsilon. так яснее намерения будут видны.
проблема в сравнении вкупе с epsilon это то, что оно не транзитивно. сортировать контейнеры (std::set<double>, std::map<double, ...>) таким сравнением может вылиться в UB. точное сравнение даблов такой проблемой не обладает имхо.
на примере метакода я бы ввел разные функции:
bool IsClose(double x, double y, double epsilon)
{
return abs(x - y) < epsilon;
}
bool IsCloseToZero(double x, double epsilon)
{
return IsClose(x, 0.0, epsilon);
}
//usable for sortingbool IsLessStrict(double x, double y)
{
return x < y;
}
//not usable for container sortingbool IsLess(double x, double y, double epsilon)
{
return IsLessStrict(x + epsilon, y);
//or return IsLessStrict(x, y) && !IsClose(x, y, epsilon); <-- вот здесь на уровне кода мы подчеркиваем, что для близких значений мы не хотим, чтобы Less возрващал true
}
Здравствуйте, andy1618, Вы писали:
А>>Глядя на все выше прочитанное печаль берет — сколько народу надо гнать в шею. Атасс. )) A>Перечитайте эти свои сообщения лет через пять — будет стыдно, уверяю!
Вряд ли анониму будет стыдно — он просто хочет странного, такого желания никто не ожидает, вот и не могут его понять.
А>>Но попробую упростить понимание для все — А>>
А>>Чему должны быть равны d3 И d4? А>>Надеюсь сумели посчитать — 0.06. A>В десятичной системе (на бумаге) — да, но процессоры обычно двоичные, поэтому в _данном_ случае значение не будет в точности равным 0.06.
Аноним ранее писал: А>>Понятно что есть "не точность", представления, понятно что есть "не точность вычисления". А>>Но как быть с ошибкой сложения? А>>То есть хотелось бы получив некое число (не известно как оно было полученно, например "сложением") как-то его, так "обработать" чтобы получить его равным форме представления.
Я не понимаю анонима, но вижу две "разумных" возможности:
1) на ошибки вычислений он хочет забить, а вот ошибку представления учесть.
Т.е., если я правильно понимаю все его пассажи, задачу можно переформулировать так:
есть число double после неизвестно каких вычислений. Задача: найти самую короткую обычную (не в экспоненциальной форме) десятичную запись числа соответствующую заданному числу в рамках точности представления double.
2) аноним не знает как подсчитывать ошибки вычислений:
Но как быть с ошибкой сложения?
Тогда можно предположить, что анониму нужно найти самую короткую десятичную запись с учётом ошибок не только представления, но и с учётом накопленных ошибок вычисления.
Странно здесь то, что ни первый, ни второй вариант практического смысла обычно не имеют. Я бы понял, если бы вопрос стоял так: есть число, хочу его распечатать в виде 0.0ddd +/- delta. А так это похоже на сферического коня в вакууме.
Здравствуйте, B0FEE664, Вы писали:
А>>>Глядя на все выше прочитанное печаль берет — сколько народу надо гнать в шею. Атасс. )) A>>Перечитайте эти свои сообщения лет через пять — будет стыдно, уверяю! BFE>Вряд ли анониму будет стыдно — он просто хочет странного, такого желания никто не ожидает, вот и не могут его понять.
Не за странное желание, оно как раз нормально — всё новое изначально "странное". А за наезды за то, что его никто не может понять, хотя формулировать он не умеет и даже не пытался.
BFE>Я не понимаю анонима, но вижу две "разумных" возможности: BFE>1) на ошибки вычислений он хочет забить, а вот ошибку представления учесть. BFE>Т.е., если я правильно понимаю все его пассажи, задачу можно переформулировать так: BFE>есть число double после неизвестно каких вычислений. Задача: найти самую короткую обычную (не в экспоненциальной форме) десятичную запись числа соответствующую заданному числу в рамках точности представления double.
Как раз это ему не нужно с самого начала — примеры с этими двумя представлениями вокруг 0.06 с самого начала показали, что он видит разницу. Но при этом категорически не желает, чтобы железо давало ему эту разницу как реальную. Почему я и начал говорить о принудительном округлении. Он же в итоге выкрутился на сравнение через подсчёт количества дискретных шагов, которое отличает два значения. Возможно даже, увидев, что этих шагов в реальном случае мало (до 10), поставил какое-то значение с большим запасом (как 1000) и успокоился.
Нет, конечно, как временная затычка это годится. Но, судя по уровню апломба анонима, он думает, что эта затычка есть идеальное решение. Тогда ему предстоит ещё много открытий...
К сожалению, у подхода IEEE754 с непрерывной цепочкой бинарных представлений от 0 до INF есть и такие недостатки — искушение инженерного одоления сложных проблем.
BFE>2) аноним не знает как подсчитывать ошибки вычислений: BFE>
BFE>Но как быть с ошибкой сложения?
BFE>Тогда можно предположить, что анониму нужно найти самую короткую десятичную запись с учётом ошибок не только представления, но и с учётом накопленных ошибок вычисления.
То есть округлить до нужной точности, если он вообще в состоянии подсчитать эту точность.
BFE>Странно здесь то, что ни первый, ни второй вариант практического смысла обычно не имеют. Я бы понял, если бы вопрос стоял так: есть число, хочу его распечатать в виде 0.0ddd +/- delta. А так это похоже на сферического коня в вакууме.
Так он и не хочет реально считать эти погрешности.
Здравствуйте, uzhas, Вы писали:
U>возвращаясь к исходной проблеме я предположу, что вам нужны вычисления с фиксированной десятичной точкой (fixed point decimal)
Что-то я не догоняю, а что, в формате с фиксированной точкой, скажем, 1.15 можно представить число 0.6 точно? Вот уж не думал
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)
Здравствуйте, slava_phirsov, Вы писали:
_>Здравствуйте, uzhas, Вы писали:
U>>возвращаясь к исходной проблеме я предположу, что вам нужны вычисления с фиксированной десятичной точкой (fixed point decimal)
_>Что-то я не догоняю, а что, в формате с фиксированной точкой, скажем, 1.15 можно представить число 0.6 точно? Вот уж не думал
Здравствуйте, uzhas, Вы писали:
U>для подобных сценариев уже нужна работа с рациональными числами, например, как здесь: http://www.boost.org/doc/libs/1_55_0/libs/rational/rational.html
Для каких именно? С вероятностью 99.999% у ТС не та задача для которой нужна подобная библиотека. Скорее всего double или может быть decimal вполне подойдут. Но первом случае об особенностях представления чисел и округлени помнить точно нужно, но и во втором может оказаться, что тоже не стоит забывать. Пример с 1/3 как раз был намёком на это.
Здравствуйте, pagid, Вы писали:
P>Для каких именно?
для работы с 1/3 точно (для манипуляций типа сложения и деления на такие же числа, то есть дроби в целыми числителем и знаменателем)
P>С вероятностью 99.999% у ТС
ваш вопрос вообще не ясно к чему был и связь с проблемой ТС не видна, явный офтопик, на который я, видимо зря, дал прямой ответ
Здравствуйте, uzhas, Вы писали:
U>ваш вопрос вообще не ясно к чему был и связь с проблемой ТС не видна, явный офтопик, на который я, видимо зря, дал прямой ответ
Рискну предположить, что проблема одна и та же. В случае 1.0 / 3.0, очевидно, что не получится сравнить результат деления с заранее известным значением, записанным литералом. По причине того, что в C++ нет синтаксиса записи бесконечных периодических десятичных дробей (кстати, а почему? Ау, WG21, добавьте в C++ эту фишку; пожалуйста!). В случае 6.0 / 10.0 это уже не очевидно — заранее известное значение выражается конечной десятичной дробью, почему бы не сравнить с ней? То, что компьютер считает не в десятичных, а в двоичных дробях, не сразу приходит в голову. Вот когда человечество (скорее бы уж!) откажется от десятичной системы счисления (все равно на пальцах уже никто не считает) и перейдет к 16-ричной, а десятичная система останется в истории, вместе с римскими цифрами и арифметикой без нуля — (кажущийся) парадокс исчезнет: 6.0 / 10.0, как и 1.0 / 3.0 выражается бесконечной периодической шестнадцатиричной дробью. Вот как-то так
Люди! Люди, смотрите, я сошел с ума! Люди! Возлюбите друг друга! (вы чувствуете, какой бред?)