Здравствуйте, SL, Вы писали:
SL>и вместо 4.725, в d получаю 4.7249999999999996, тут так то все понятно так устроен double, но ведь можно же это как то учесть.
Если у тебя изначально числа с фиксированной разрядностью, то надо стараться использовать fixed point арифметику. То есть в твоем примере надо умножать не на 0.18, а на 18, и затем использовать целочисленные операции:
int d = 2625 * 18;
d = ((d + 50) / 100);
cout << d / 100 << '.' << setfill('0') << setw(2) << d % 100;
Здравствуйте, SL, Вы писали:
SL>результат например (2625.0 * 0.18)/100 был равен 4.73
У тебя округление изначально написано, в целом, правильно. Может можно поаккуратнее как-то, но основная проблема не в этом, а в том, что в round данные уже приходят с погрешностью. Надо выяснить, какова природа твоих чисел и что нужно с ними сделать.
Здравствуйте, SL, Вы писали:
SL>здравствуйте такой вопрос мне нужно округлить тип double до двух знаков, вот моя реализация
Для чего вообще нужно округление, ведь это дополнительная потеря точности на и так уже плавающей запятой? Может быть, на самом деле нужно всего лишь выводить на экран/преобразовывать число в строку с заданным количеством знаков после запятой?
Для printf() это достигается флагами наподобие "%f.2", в IOstreams — комбинацией манипуляторов std::fixed << std::setprecision(2)
Здравствуйте, __kot2, Вы писали:
__>когда разговор заходит про ньюансы округления, то это обычно финансы. деньги. специально для этого есть тип decimal, просто забудьть про double и все.
S>Не зависит от процессора и приблизительно в 10 раз медленнее чем double
Речь же о том, что зачастую математика в таком софте не является узким местом, поэтому хоть в 10, хоть в 100, это может вообще не повлиять на скорость работы, зато повлияет на правильность вычислений.
Да и что за ссылка вообще на decimal из С#, реализации fixed point вычислений могут быть разные в зависимости от задачи. Иногда можно и обычным long обойтись.
Здравствуйте, Muxa, Вы писали:
SL>>но не могу понять как побороть. M>Как побороть что? M>Сделать так чтобы 4.7249999999999999 округлялось до 4.73 или я тебя неправильно понял?
Ну в общем то да что бы 4.7249999999999999999 округлялось в 4.73, а 4.72499 в 4.72. а точнее что бы
результат например (2625.0 * 0.18)/100 был равен 4.73
SL>результат например (2625.0 * 0.18)/100 был равен 4.73
Судя по примеру работаешь с финансами. Я прав?
Если да то используй Fixed point floating арифметику, наверняка есть готовые классы, либо напиши велосипед.
Здравствуйте, _DAle_, Вы писали:
_DA>Здравствуйте, SL, Вы писали:
SL>>результат например (2625.0 * 0.18)/100 был равен 4.73
_DA>У тебя округление изначально написано, в целом, правильно. Может можно поаккуратнее как-то, но основная проблема не в этом, а в том, что в round данные уже приходят с погрешностью. Надо выяснить, какова природа твоих чисел и что нужно с ними сделать.
Условно говоря у меня есть число 2625, от него мне нужно посчитать значение 0.18% и его округлить до 2 знаков.
Делаю так
Здравствуйте, SL, Вы писали:
SL>округляется до 4.72 и вроде понятна причина число 4.725 на самом деле представляет собой 4.729999999999999, но не могу понять как побороть.
вам нужно определить степень отклонения (eps), которое считается допустимым. для этого проще всего накидать примеры входных значений и выходных (можно как раз сразу в автотесты засунуть)
пример:
exact input value, nCount, expected result
итоговый eps может быть либо конкретным числом, либо формулой от dValue и count. я пока возьму eps = 0.1 ^ (nCount + 3) (вам нужно анализировать свои данные, чтобы принять решение о погрешности). если у dValue большой диапазон возможных значений (от 1e-9 до 1e+9), то скорее всего и его надо учитывать (ведь надо учитывать, что кол-во десятичных цифр в double ограниченно : знаки из мантиссы вытесняют знаки в дробной части)
в тестах желательно покрыть все граничные условия: (положительный/отрицательный) x (минимальный/максимальный по модулю) x (count / value)
я бы floor еще заменил тупым кастом к целочисленному типу
подобные функции хорошо работают в условиях ограниченного набора входных данных. для широкого круга значений эти функции чаще всего глючат
Здравствуйте, Muxa, Вы писали:
SL>>результат например (2625.0 * 0.18)/100 был равен 4.73 M>Судя по примеру работаешь с финансами. Я прав? M>Если да то используй Fixed point floating арифметику, наверняка есть готовые классы, либо напиши велосипед.
В целом да с финансами, насчет Fixed point floating это понятно, но интересно Excel тоже использует Fixed point floating арифметику, потому что если в нем в трех ячейках вбить цифры 2625, 0.18, 100 и в четвертой указать операцию с тремя ячейками то есть (2625.0 * 0.18)/100 и указать в формате два знака после запятой, то Excel выводит 4.73.
Здравствуйте, bnk, Вы писали:
bnk>Здравствуйте, SL, Вы писали:
SL>>здравствуйте такой вопрос мне нужно округлить тип double до двух знаков, вот моя реализация
bnk>Не надо использовать double для денежных рассчетов. bnk>В некоторых местах за такие художества можно и канделябром огрести.
bnk>Для этого существует decimal.
я бы не был столь категоричен
я проводил анализ публичных библиотек для работы с decimal и оказалось, что банальный 1 * 1 выполняется на порядки медленнее, чем для double
лично я использую double для работы с деньгами именно из-за требований к производительности
U>я проводил анализ публичных библиотек для работы с decimal и оказалось, что банальный 1 * 1 выполняется на порядки медленнее, чем для double U>лично я использую double для работы с деньгами именно из-за требований к производительности
а профайлером прогоняли? неужели математика над "деньгами" занимают заметную долю?
обычно в основном тормозит доступ к БД, а не вычисление процентов.
думаю, decimal вместо double в худшем случае увеличит время работы на 1%.
Здравствуйте, SL, Вы писали: SL>здравствуйте такой вопрос мне нужно округлить тип double до двух знаков, вот моя реализация
когда разговор заходит про ньюансы округления, то это обычно финансы. деньги. специально для этого есть тип decimal, просто забудьть про double и все.
Здравствуйте, K13, Вы писали:
U>>я проводил анализ публичных библиотек для работы с decimal и оказалось, что банальный 1 * 1 выполняется на порядки медленнее, чем для double U>>лично я использую double для работы с деньгами именно из-за требований к производительности
K13>а профайлером прогоняли? неужели математика над "деньгами" занимают заметную долю? K13>обычно в основном тормозит доступ к БД, а не вычисление процентов. K13>думаю, decimal вместо double в худшем случае увеличит время работы на 1%.
Здравствуйте, Кодт, Вы писали: К>Здравствуйте, __kot2, Вы писали: __>>когда разговор заходит про ньюансы округления, то это обычно финансы. деньги. специально для этого есть тип decimal, просто забудьть про double и все. К>Можно просто считать в копейках.
вы просто отодвинете проблему на большие суммы, всего на два порядка.
Здравствуйте, _DAle_, Вы писали:
_DA>Иногда можно и обычным long обойтись.
С long есть маленькая проблемка
123*100/118 != 123/118*100
С decimal с необходимым числом знаков после точки всё проще, но и там следует осознавать когда нужно округлять и какие эффекты могут проявиться. Точно так же как и с double.
Здравствуйте, SL, Вы писали:
SL>В целом да с финансами, насчет Fixed point floating это понятно, но интересно Excel тоже использует Fixed point floating арифметику, потому что если в нем в трех ячейках вбить цифры 2625, 0.18, 100 и в четвертой указать операцию с тремя ячейками то есть (2625.0 * 0.18)/100 и указать в формате два знака после запятой, то Excel выводит 4.73.
Скорее всего.
В double ты никогда точно не округлишь — т.е. конечно могут быть числа, которые точно представимы в формате double, но это только в отдельных частных случаях.
double принято округлять при выводе пользователю (кстати, возможно, что Excel и так поступает)
Здравствуйте, pagid, Вы писали:
_DA>>Иногда можно и обычным long обойтись.
P>С long есть маленькая проблемка
P>123*100/118 != 123/118*100
Нет проблемы, просто часть бит нужно отвести под дробную часть с нужной чточностью, и не забывать нормализовывать.
с double кстати, такая же фигня скорее всего
P>С decimal с необходимым числом знаков после точки всё проще, но и там следует осознавать когда нужно округлять и какие эффекты могут проявиться. Точно так же как и с double.
Здравствуйте, Marty, Вы писали:
M>Нет проблемы, просто часть бит нужно отвести под дробную часть с нужной чточностью, и не забывать нормализовывать.
Тогда мы вернемся по кругу в начало разговора и к вопросу ТС
M>с double кстати, такая же фигня скорее всего
+1
M>Да вообще всегда нужно понимать, что делаешь
+1
Здравствуйте, SL, Вы писали:
SL>округляется до 4.72 и вроде понятна причина число 4.725 на самом деле представляет собой 4.729999999999999, но не могу понять как побороть.
Скорее всего "бороть" и не нужно. Округлятся должно при выводе на экран/печать и библиотечные функции поступают именно так. Если программа работает с БД, и речь идет про деньги, то можно убедится, что при сохранении в субдешную DECIMAL происходит именно округление, но вроде по стандарту SQL это так.
Использование чего-то подобного decimal из С# тоже вариант, но полезность этого стоит оценить, пока из того, что ты написал необходимость так извращаться не очевидна.