Re[5]: Округление double
От: _DAle_ Беларусь  
Дата: 10.06.15 09:33
Оценка: +2
Здравствуйте, 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;
Отредактировано 10.06.2015 9:38 _DAle_ . Предыдущая версия . Еще …
Отредактировано 10.06.2015 9:35 _DAle_ . Предыдущая версия .
Re: Округление double
От: Muxa  
Дата: 10.06.15 08:25
Оценка: +1
SL>но не могу понять как побороть.
Как побороть что?
Сделать так чтобы 4.7249999999999999 округлялось до 4.73 или я тебя неправильно понял?
Re[3]: Округление double
От: _DAle_ Беларусь  
Дата: 10.06.15 09:10
Оценка: +1
Здравствуйте, SL, Вы писали:

SL>результат например (2625.0 * 0.18)/100 был равен 4.73


У тебя округление изначально написано, в целом, правильно. Может можно поаккуратнее как-то, но основная проблема не в этом, а в том, что в round данные уже приходят с погрешностью. Надо выяснить, какова природа твоих чисел и что нужно с ними сделать.
Re: Округление double
От: bnk СССР http://unmanagedvisio.com/
Дата: 10.06.15 09:15
Оценка: +1
Здравствуйте, SL, Вы писали:

SL>здравствуйте такой вопрос мне нужно округлить тип double до двух знаков, вот моя реализация


Не надо использовать double для денежных рассчетов.
В некоторых местах за такие художества можно и канделябром огрести.

Для этого существует decimal.

Это я в предположении что твои цифры — это рубли/копейки, соответственно.
Re: Округление double
От: PM  
Дата: 10.06.15 10:35
Оценка: +1
Здравствуйте, SL, Вы писали:

SL>здравствуйте такой вопрос мне нужно округлить тип double до двух знаков, вот моя реализация


Для чего вообще нужно округление, ведь это дополнительная потеря точности на и так уже плавающей запятой? Может быть, на самом деле нужно всего лишь выводить на экран/преобразовывать число в строку с заданным количеством знаков после запятой?

Для printf() это достигается флагами наподобие "%f.2", в IOstreams — комбинацией манипуляторов std::fixed << std::setprecision(2)

#include <iostream>
#include <iomanip>

void print(double d, unsigned digits)
{
    std::cout << "round(" << std::defaultfloat << d << ", " << digits << "): " << std::fixed << std::setprecision(digits) << d << '\n';
}

int main()
{
    print(4.725, 2);
    print(1.112222, 3);
    print(0.00001, 2);
    print(12345.6789, 5);

}


Выводит


round(4.725, 2): 4.72
round(1.1, 3): 1.112
round(1e-05, 2): 0.00
round(1.2e+04, 5): 12345.67890
Re[2]: Округление double
От: Кодт Россия  
Дата: 10.06.15 15:14
Оценка: +1
Здравствуйте, __kot2, Вы писали:

__>когда разговор заходит про ньюансы округления, то это обычно финансы. деньги. специально для этого есть тип decimal, просто забудьть про double и все.


Можно просто считать в копейках.
Перекуём баги на фичи!
Re[5]: Округление double
От: _DAle_ Беларусь  
Дата: 10.06.15 15:31
Оценка: +1
Здравствуйте, Serginio1, Вы писали:

S>

S>Не зависит от процессора и приблизительно в 10 раз медленнее чем double


Речь же о том, что зачастую математика в таком софте не является узким местом, поэтому хоть в 10, хоть в 100, это может вообще не повлиять на скорость работы, зато повлияет на правильность вычислений.
Да и что за ссылка вообще на decimal из С#, реализации fixed point вычислений могут быть разные в зависимости от задачи. Иногда можно и обычным long обойтись.
Re[4]: Округление double
От: Кодт Россия  
Дата: 10.06.15 16:20
Оценка: +1
Здравствуйте, __kot2, Вы писали:

К>>Можно просто считать в копейках.

__>вы просто отодвинете проблему на большие суммы, всего на два порядка.

А fixed point, наоборот, сразу придвинет проблему. Придётся во всех расчётах (в том числе, промежуточных) куда-то списывать дробные копейки.
Перекуём баги на фичи!
Округление double
От: SL  
Дата: 10.06.15 08:05
Оценка:
здравствуйте такой вопрос мне нужно округлить тип double до двух знаков, вот моя реализация

 double round(double dValue, int nCount)
 {
    double dSign = dValue < 0 ? -1 : 1;
    double dPow = pow((double)10., nCount);
    return  dSign * (floor((fabs(dValue)* dPow) + 0.5) / dPow);
  }


но происходит ошибка например число

    double d =  4.725;
    double d1 = Round(d, 2);
    std::cout << d << "  round " << d1;


округляется до 4.72 и вроде понятна причина число 4.725 на самом деле представляет собой 4.729999999999999, но не могу понять как побороть.
Re: Округление double
От: SL  
Дата: 10.06.15 08:22
Оценка:
Здравствуйте, SL, Вы писали:

зашел с "другой" стороны
double round(double dValue, int nCount)
 {
    double dSign = dValue < 0 ? -1 : 1;
    double dPow = pow((double)10., nCount);
    return  dSign * (ceil((fabs(dValue)* dPow)  - 0.49 ) / dPow);
  }


пока не нашел значений на которых не правильно округляется.
Re[2]: Округление double
От: _DAle_ Беларусь  
Дата: 10.06.15 08:56
Оценка:
Здравствуйте, SL, Вы писали:

round(0.00491, 2)
Re[2]: Округление double
От: SL  
Дата: 10.06.15 09:05
Оценка:
Здравствуйте, Muxa, Вы писали:

SL>>но не могу понять как побороть.

M>Как побороть что?
M>Сделать так чтобы 4.7249999999999999 округлялось до 4.73 или я тебя неправильно понял?

Ну в общем то да что бы 4.7249999999999999999 округлялось в 4.73, а 4.72499 в 4.72. а точнее что бы

результат например (2625.0 * 0.18)/100 был равен 4.73
Re[3]: Округление double
От: SL  
Дата: 10.06.15 09:05
Оценка:
Здравствуйте, _DAle_, Вы писали:

_DA>Здравствуйте, SL, Вы писали:


_DA>round(0.00491, 2)


Да согласен
Re[3]: Округление double
От: Muxa  
Дата: 10.06.15 09:20
Оценка:
SL>результат например (2625.0 * 0.18)/100 был равен 4.73
Судя по примеру работаешь с финансами. Я прав?
Если да то используй Fixed point floating арифметику, наверняка есть готовые классы, либо напиши велосипед.
Re[4]: Округление double
От: SL  
Дата: 10.06.15 09:24
Оценка:
Здравствуйте, _DAle_, Вы писали:

_DA>Здравствуйте, SL, Вы писали:


SL>>результат например (2625.0 * 0.18)/100 был равен 4.73


_DA>У тебя округление изначально написано, в целом, правильно. Может можно поаккуратнее как-то, но основная проблема не в этом, а в том, что в round данные уже приходят с погрешностью. Надо выяснить, какова природа твоих чисел и что нужно с ними сделать.


Условно говоря у меня есть число 2625, от него мне нужно посчитать значение 0.18% и его округлить до 2 знаков.
Делаю так
double d =  (2625.0  *  0.18)/100;
double d1 = round(d, 2);


и вместо 4.725, в d получаю 4.7249999999999996, тут так то все понятно так устроен double, но ведь можно же это как то учесть.
Re: Округление double
От: uzhas Ниоткуда  
Дата: 10.06.15 09:30
Оценка:
Здравствуйте, SL, Вы писали:

SL>округляется до 4.72 и вроде понятна причина число 4.725 на самом деле представляет собой 4.729999999999999, но не могу понять как побороть.


вам нужно определить степень отклонения (eps), которое считается допустимым. для этого проще всего накидать примеры входных значений и выходных (можно как раз сразу в автотесты засунуть)

пример:
exact input value, nCount, expected result

4.725, 2, 4.73
4.7248, 2, ?
4.7249, 2, ?
4.72499, 2, ?
4.7249999, 2, ?
4.72499999999, 2, ?

теперь увеличим nCount:

4.7225, 3, 4.723
4.72248, 3, ?
4.72249, 3, ?
4.722499, 3, ?
4.72249999, 3, ?
4.722499999999, 3, ?

итоговый eps может быть либо конкретным числом, либо формулой от dValue и count. я пока возьму eps = 0.1 ^ (nCount + 3) (вам нужно анализировать свои данные, чтобы принять решение о погрешности). если у dValue большой диапазон возможных значений (от 1e-9 до 1e+9), то скорее всего и его надо учитывать (ведь надо учитывать, что кол-во десятичных цифр в double ограниченно : знаки из мантиссы вытесняют знаки в дробной части)
в тестах желательно покрыть все граничные условия: (положительный/отрицательный) x (минимальный/максимальный по модулю) x (count / value)
double round(double dValue, int nCount)
{
  double dSign = dValue < 0 ? -1 : 1;
  double dPow = pow((double)10., nCount); //лучше иметь предвычисленные значения
  double eps = 0.001;
  return  dSign * (floor(fabs(dValue) * dPow + 0.5 + eps) / dPow);
}

я бы floor еще заменил тупым кастом к целочисленному типу
подобные функции хорошо работают в условиях ограниченного набора входных данных. для широкого круга значений эти функции чаще всего глючат

пример: http://ideone.com/W7J3ES
Re[4]: Округление double
От: SL  
Дата: 10.06.15 09:34
Оценка:
Здравствуйте, 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.
Re[2]: Округление double
От: uzhas Ниоткуда  
Дата: 10.06.15 09:34
Оценка:
Здравствуйте, bnk, Вы писали:

bnk>Здравствуйте, SL, Вы писали:


SL>>здравствуйте такой вопрос мне нужно округлить тип double до двух знаков, вот моя реализация


bnk>Не надо использовать double для денежных рассчетов.

bnk>В некоторых местах за такие художества можно и канделябром огрести.

bnk>Для этого существует decimal.


я бы не был столь категоричен
я проводил анализ публичных библиотек для работы с decimal и оказалось, что банальный 1 * 1 выполняется на порядки медленнее, чем для double
лично я использую double для работы с деньгами именно из-за требований к производительности
Re[3]: Округление double
От: K13 http://akvis.com
Дата: 10.06.15 10:09
Оценка:
U>я проводил анализ публичных библиотек для работы с decimal и оказалось, что банальный 1 * 1 выполняется на порядки медленнее, чем для double
U>лично я использую double для работы с деньгами именно из-за требований к производительности

а профайлером прогоняли? неужели математика над "деньгами" занимают заметную долю?
обычно в основном тормозит доступ к БД, а не вычисление процентов.
думаю, decimal вместо double в худшем случае увеличит время работы на 1%.
Re: Округление double
От: __kot2  
Дата: 10.06.15 14:45
Оценка:
Здравствуйте, SL, Вы писали:
SL>здравствуйте такой вопрос мне нужно округлить тип double до двух знаков, вот моя реализация
когда разговор заходит про ньюансы округления, то это обычно финансы. деньги. специально для этого есть тип decimal, просто забудьть про double и все.
Re[4]: Округление double
От: Serginio1 СССР https://habrahabr.ru/users/serginio1/topics/
Дата: 10.06.15 15:20
Оценка:
Здравствуйте, K13, Вы писали:

U>>я проводил анализ публичных библиотек для работы с decimal и оказалось, что банальный 1 * 1 выполняется на порядки медленнее, чем для double

U>>лично я использую double для работы с деньгами именно из-за требований к производительности

K13>а профайлером прогоняли? неужели математика над "деньгами" занимают заметную долю?

K13>обычно в основном тормозит доступ к БД, а не вычисление процентов.
K13>думаю, decimal вместо double в худшем случае увеличит время работы на 1%.

Различия между double и decimal

Не зависит от процессора и приблизительно в 10 раз медленнее чем double

и солнце б утром не вставало, когда бы не было меня
Re[3]: Округление double
От: __kot2  
Дата: 10.06.15 15:45
Оценка:
Здравствуйте, Кодт, Вы писали:
К>Здравствуйте, __kot2, Вы писали:
__>>когда разговор заходит про ньюансы округления, то это обычно финансы. деньги. специально для этого есть тип decimal, просто забудьть про double и все.
К>Можно просто считать в копейках.
вы просто отодвинете проблему на большие суммы, всего на два порядка.
Re: Округление double
От: Caracrist https://1pwd.org/
Дата: 10.06.15 18:01
Оценка:
Здравствуйте, SL, Вы писали:

SL>здравствуйте такой вопрос мне нужно округлить тип double до двух знаков, вот моя реализация


как на счёт пользоваться fraction?

например:
http://codepad.org/OzHKJe5A
там int_t можно поменять на bignum
~~~~~
~lol~~
~~~ Single Password Solution
Re[6]: Округление double
От: pagid Россия  
Дата: 14.06.15 20:02
Оценка:
Здравствуйте, _DAle_, Вы писали:

_DA>Иногда можно и обычным long обойтись.


С long есть маленькая проблемка

123*100/118 != 123/118*100

С decimal с необходимым числом знаков после точки всё проще, но и там следует осознавать когда нужно округлять и какие эффекты могут проявиться. Точно так же как и с double.
... << RSDN@Home 1.2.0 alpha 5 rev. 1495>>
Re[5]: Округление double
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 14.06.15 20:27
Оценка:
Здравствуйте, 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 и так поступает)
Маньяк Робокряк колесит по городу
Re[7]: Округление double
От: Marty Пират https://www.youtube.com/channel/UChp5PpQ6T4-93HbNF-8vSYg
Дата: 14.06.15 20:31
Оценка:
Здравствуйте, pagid, Вы писали:

_DA>>Иногда можно и обычным long обойтись.


P>С long есть маленькая проблемка


P>123*100/118 != 123/118*100


Нет проблемы, просто часть бит нужно отвести под дробную часть с нужной чточностью, и не забывать нормализовывать.

с double кстати, такая же фигня скорее всего

P>С decimal с необходимым числом знаков после точки всё проще, но и там следует осознавать когда нужно округлять и какие эффекты могут проявиться. Точно так же как и с double.


Да вообще всегда нужно понимать, что делаешь
Маньяк Робокряк колесит по городу
Re[8]: Округление double
От: pagid Россия  
Дата: 15.06.15 02:50
Оценка:
Здравствуйте, Marty, Вы писали:

M>Нет проблемы, просто часть бит нужно отвести под дробную часть с нужной чточностью, и не забывать нормализовывать.

Тогда мы вернемся по кругу в начало разговора и к вопросу ТС

M>с double кстати, такая же фигня скорее всего

+1

M>Да вообще всегда нужно понимать, что делаешь

+1
... << RSDN@Home 1.2.0 alpha 5 rev. 1495>>
Re: Округление double
От: pagid Россия  
Дата: 15.06.15 02:50
Оценка:
Здравствуйте, SL, Вы писали:

SL>округляется до 4.72 и вроде понятна причина число 4.725 на самом деле представляет собой 4.729999999999999, но не могу понять как побороть.

Скорее всего "бороть" и не нужно. Округлятся должно при выводе на экран/печать и библиотечные функции поступают именно так. Если программа работает с БД, и речь идет про деньги, то можно убедится, что при сохранении в субдешную DECIMAL происходит именно округление, но вроде по стандарту SQL это так.

Использование чего-то подобного decimal из С# тоже вариант, но полезность этого стоит оценить, пока из того, что ты написал необходимость так извращаться не очевидна.
... << RSDN@Home 1.2.0 alpha 5 rev. 1495>>
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.