Округление в С++
От: Adriano  
Дата: 16.12.10 09:31
Оценка: :)
Всем привет!

В примре две функции good и bad. В good получаем правильный результат, в bad — неправильный.

#include <cmath>
#include <iostream>
#include <iomanip>

double round( double value )
{
    return std::floor( value + 0.5 );
}

double round(double value, double tick)
{
    return round(value/tick)*tick;
}

void print_mid_bid_ask(double mid, double bid, double ask)
{
    std::cout
        << std::fixed
        << "mid = " << std::setprecision(3) << mid << std::endl
        << "bid = " << std::setprecision(3) << bid << std::endl
        << "ask = " << std::setprecision(3) << ask << std::endl;
}

void good(char const * const str_mid)
{
    double mid = atof(str_mid);   // mid = 5.0324999999999998
    mid = round(mid, 0.0001);     // mid = 5.0325000000000006
    print_mid_bid_ask(mid, mid+0.1, mid-0.1);
}

void bad(char const * const str_mid)
{
    double mid = atof(str_mid);   //mid = 5.0324999999999998
    print_mid_bid_ask(mid, mid+0.1, mid-0.1);
}

int main()
{
    char const* str_mid = "5.0325";
    std::cout
        << "mid = " << str_mid << std::endl
        << std::endl;

    std::cout
        << "good:" << std::endl;
    good(str_mid);

    std::cout
        << "bad:" << std::endl;
    bad(str_mid);

    return 0;
}


Вопрос: как правильно работать с double что бы не терять биты на границах 0.5?
Re: Округление в С++
От: telek1024  
Дата: 16.12.10 09:50
Оценка:
Здравствуйте, Adriano, Вы писали:

A>Всем привет!


A>В примре две функции good и bad. В good получаем правильный результат, в bad — неправильный.


A>Вопрос: как правильно работать с double что бы не терять биты на границах 0.5?


А почему вы решили, что биты теряются на границе 0.5? И что значит "граница 0.5"?

Мне кажется, что для начала вам нужно почитать документацию о том, как хранятся вещественные числа в памяти. Начните отсюда: http://en.wikipedia.org/wiki/IEEE_754-1985

Или ещё можно написать std::setprecision(20) вместо std::setprecision(3)и посмотреть, что выведет программа.
Re: Округление в С++
От: Chorkov Россия  
Дата: 16.12.10 10:33
Оценка:
Здравствуйте, Adriano, Вы писали:

A>Всем привет!


A>В примре две функции good и bad. В good получаем правильный результат, в bad — неправильный.

...
A>Вопрос: как правильно работать с double что бы не терять биты на границах 0.5?

Использовать atod вместо atof. Тогда в промежуточных вычислениях не будет округления до float.
Re[2]: Округление в С++
От: Adriano  
Дата: 16.12.10 10:38
Оценка:
Здравствуйте, telek1024, Вы писали:

T>А почему вы решили, что биты теряются на границе 0.5? И что значит "граница 0.5"?


Я плаваю в этой теме

T>Мне кажется, что для начала вам нужно почитать документацию о том, как хранятся вещественные числа в памяти. Начните отсюда: http://en.wikipedia.org/wiki/IEEE_754-1985


Я понимаю, что не все десятичные дроби можно представить в двоичной системе.

T>Или ещё можно написать std::setprecision(20) вместо std::setprecision(3)и посмотреть, что выведет программа.


Если установить точность — 4, то все правильно:
mid = 5.0325

good:
mid = 5.032500
bid = 5.132500
ask = 4.932500
bad:
mid = 5.032500
bid = 5.132500
ask = 4.932500


Как быть если нужна точность 3 знака после запятой?
Re[3]: Округление в С++
От: uzhas Ниоткуда  
Дата: 16.12.10 10:43
Оценка:
Здравствуйте, Adriano, Вы писали:

A>Как быть если нужна точность 3 знака после запятой?

у вас ведь есть функция good
чем она вас не устраивает?
Re[4]: Округление в С++
От: Adriano  
Дата: 16.12.10 11:25
Оценка:
Здравствуйте, uzhas, Вы писали:

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


A>>Как быть если нужна точность 3 знака после запятой?

U>у вас ведь есть функция good
U>чем она вас не устраивает?

1. Мне не понятно почему bad работает неправильно. В каком месте ошибка?
2. atof используется очень часто ( и в сторонних библиотеках), возникает вопрос: "округлять" каждый double?

Хочу узнать, так сказать, best practices
Re[2]: Округление в С++
От: Adriano  
Дата: 16.12.10 11:33
Оценка:
Здравствуйте, Chorkov, Вы писали:

C>Использовать atod вместо atof. Тогда в промежуточных вычислениях не будет округления до float.


atod стандартная функция?

atof работает с double:

double atof ( const char * str );
Re[3]: Округление в С++
От: telek1024  
Дата: 16.12.10 11:42
Оценка:
Здравствуйте, Adriano, Вы писали:

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


T>>Или ещё можно написать std::setprecision(20) вместо std::setprecision(3)и посмотреть, что выведет программа.


A>Если установить точность — 4, то все правильно:


Так я сказал про 20 Поставьте 20 и поймёте, почему при 3 — неправильно, а при 4 правильно.

A>Как быть если нужна точность 3 знака после запятой?

Не использовать числа с плавающей точкой.

Я подозреваю, что у вас там деньги или котировки. Ни double, ни float для этого не подходят. Напишите свой класс, который будет точно хранить десятичные знаки или возьмите готовую реализацию. Пример запроса для поиска: http://www.google.ru/search?q=C%2B%2B+BigDecimal
Re[4]: Округление в С++
От: Adriano  
Дата: 16.12.10 12:52
Оценка:
Здравствуйте, telek1024, Вы писали:

A>>Если установить точность — 4, то все правильно:


T>Так я сказал про 20 Поставьте 20 и поймёте, почему при 3 — неправильно, а при 4 правильно.


A>>Как быть если нужна точность 3 знака после запятой?

T>Не использовать числа с плавающей точкой.

Библиотеки разрабатываются другим отделом и заставить их переписать все с использованием decimal не так то просто.
Также используется питон:
с++ double -> Python double -> c++ double

В питоне есть модуль decimal, на моем примере питоновский decimal в 2000 раз медленнее чем double, говорят что он хранит число как список из integer.
Re[5]: Округление в С++
От: uzhas Ниоткуда  
Дата: 16.12.10 12:52
Оценка:
Здравствуйте, Adriano, Вы писали:

вас тяжело понять что для вас правильно. а что неправильно
попробуйте описать задачу подробнее
что вы вычисляете? почему работая с типом double вам приходится сохранять лишь три знака после запятой? как преобразование из произвольного числа для вас правильное?
A>1. Мне не понятно почему bad работает неправильно. В каком месте ошибка?
возможная ошибка в print_mid_bid_ask
A>2. atof используется очень часто ( и в сторонних библиотеках), возникает вопрос: "округлять" каждый double?
ваша задача неясна. почему другие функции работают с double, а не с округленным число?

если нужны только три знака после запятой, то делают так:
double xNew = ((int)(x*1000))/1000.0;
то есть отбрасываю все остальные цифры
A>Хочу узнать, так сказать, best practices
если возникает потребность в числах определенной природы, то используют другие типы, такие как money, rational (p\q)
Re[3]: Округление в С++
От: Chorkov Россия  
Дата: 16.12.10 13:20
Оценка:
Здравствуйте, Adriano, Вы писали:

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


C>>Использовать atod вместо atof.


Глупость я написал, неподумовши.
Округления до float здесь, конечно, нет.
Re[5]: Округление в С++
От: telek1024  
Дата: 16.12.10 15:23
Оценка:
Здравствуйте, Adriano, Вы писали:

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


A>>>Если установить точность — 4, то все правильно:


T>>Так я сказал про 20 Поставьте 20 и поймёте, почему при 3 — неправильно, а при 4 правильно.


A>>>Как быть если нужна точность 3 знака после запятой?

T>>Не использовать числа с плавающей точкой.

A>Библиотеки разрабатываются другим отделом и заставить их переписать все с использованием decimal не так то просто.

Придётся. Так как тип используется не по назначению.
Или писать обёртку.

A>Также используется питон:

A>с++ double -> Python double -> c++ double

A>В питоне есть модуль decimal, на моем примере питоновский decimal в 2000 раз медленнее чем double, говорят что он хранит число как список из integer.

Мир не совершенен. double работает быстро, но портит младшие разряды. decimal — медленно, но не портит результат.
Re[6]: Округление в С++
От: gegMOPO4  
Дата: 16.12.10 17:21
Оценка:
Здравствуйте, uzhas, Вы писали:
U>если нужны только три знака после запятой, то делают так:
U>double xNew = ((int)(x*1000))/1000.0;
U>то есть отбрасываю все остальные цифры

x=1.001
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.