Что значит округление после запятой?
Если просто округление — то есть ф-ции:
#include <math.h> — хедер
double floor(double x) — наибольшее целое, которое меньше или равно x
double ceil(double x) — наименьшее целое, которое больше или равно x
Если тебе нужно округление так, как это обычно понимается, то используй
floor(x+.5)
Успехов.
- Простите, профессор, не пса, а когда он уже был человеком.
— То-есть он говорил? Это еще не значит быть человеком. (с) Булгаков
Re[4]: ф-я округления вещественного числа ???
От:
Аноним
Дата:
04.03.03 06:46
Оценка:
Здравствуйте, small_cat, Вы писали:
SC>Что значит округление после запятой?
это значит :
10.11111 = 10.1
10.56 = 10.6
10.6 = 10.6
SC>Если просто округление — то есть ф-ции:
SC>#include <math.h> — хедер
SC>double floor(double x) — наибольшее целое, которое меньше или равно x SC>double ceil(double x) — наименьшее целое, которое больше или равно x
SC>Если тебе нужно округление так, как это обычно понимается, то используй
SC>floor(x+.5)
Функцию pow (тем более — дважды) использовать неэкономно.
Если нужна быстрая реализация, то достаточно вспомнить, что потолок double — это 1.7e308,
следовательно, нужна таблица из 308 значений целых степеней 10.
Всем привет!
Для меня тоже оказалась актуальна тема округления.
У меня для вас есть немного вкусненького.
Есть небольшая модификация алгоритма округления до любого
числа знаков после запятой:
double round( double val, unsigned signs ) //[1]
{
double p = pow( 10., signs );
return floor( val * p + .5 ) / p;
}
val — само число,
signs — количество знаков после запятой.
Исследуя, природу этой ошибки, меня посетила такая мысль.
Что не любое число может быть представлено в формате типа
double. Одним из этих чисел является 0.285, и то что мы
видим 0.285 — это не так. На самом деле число 0.285
представлено числом, близким к нему, это число
0.284(9) ( 9 в периоде ). В доказательство этому привожу
фрагмент кода:
После выполнения строки номер 4 переменная iv принимает значение,
как вы думаете, не 50, а 49!
В результате стал перед проблемой правильного округления.
Если у кого есть соображения по этому поводу, пожалуйста,
высказываейтесь, будет интересно узнать.
Примечание: В компиляторе Borland Pascal 7.0 округление,
вроде корректно со всеми числами. Может все дело в структуре
хранения чисел с плавающей запятой в C?
Здравствуйте, XXXL, Вы писали:
X> Исследуя, природу этой ошибки, меня посетила такая мысль. X> Что не любое число может быть представлено в формате типа X> double. Одним из этих чисел является 0.285
Верное наблюдение
X> В результате стал перед проблемой правильного округления. X> Если у кого есть соображения по этому поводу, пожалуйста, X> высказываейтесь, будет интересно узнать.
1) Не трогать сами числа, а "округлять" при выводе
2) Перейти на числа с фиксированной точкой
Posted via RSDN NNTP Server 1.7 "Bedlam"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>1) Не трогать сами числа, а "округлять" при выводе ПК>2) Перейти на числа с фиксированной точкой
У меня была такая идея, перейти на целые числа, и я
почти добился успеха. Но не могу осмыслить, как понять
что в случае c числом 0.285 0.004(9) — это 0.005? Была
идея определить такое мале число E, которое при сложении
с округляемым его выравнивала, если это число не точное,
и не влияло на результат, если число задано точно. Но
идея была отброшена так как 0.285 — это 0.284(9)6...!
Не отнимет ли у Вас много времени более подробное описание
перехода к числам с фиксированной запятой?
Здравствуйте, XXXL, Вы писали:
ПК>> 1) Не трогать сами числа, а "округлять" при выводе ПК>> 2) Перейти на числа с фиксированной точкой
X> более подробное описание перехода к числам с фиксированной запятой?
Не очень подробно, но идея, в общем, такая: float/double вообще не используются.
Вместо этого используются целые с достаточным запасом точности (обычно хватает
__int64/long long). Также выбирается какой-то произвольный множитель, определяющий
"позицию" фиксированной десятичной точки/запятой. Для большинства бизнес-приложений
6-8 знаков после точки/запятой вполне достаточно. Для уменьшения количества ошибок
программирования все это, конечно, оборачивается в какие-нибудь классы. Более того:
таких классов, уже готовых, вполне достаточно в Интернете
Posted via RSDN NNTP Server 1.7 "Bedlam"
Легче одурачить людей, чем убедить их в том, что они одурачены. — Марк Твен
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>Вместо этого используются целые с достаточным запасом точности (обычно хватает ПК>__int64/long long). Также выбирается какой-то произвольный множитель, определяющий ПК>"позицию" фиксированной десятичной точки/запятой.
Спасибо за совет! Так действительно решается проблема округления во многих составляющих
элементах бизнес-систем, например в кассовых аппаратах.
Здравствуйте, Павел Кузнецов, Вы писали:
ПК>...Для уменьшения количества ошибок ПК>программирования все это, конечно, оборачивается в какие-нибудь классы. Более того: ПК>таких классов, уже готовых, вполне достаточно в Интернете
Я написал класс-обертку, о котором Вы говорили, для реализации чисел с фиксированой
запятой. Может найдутся желающие его "попинать" или те, кому он может пригодиться ?
Некоторыми моментами в этом коде я остался недоволен , так как не нашел однозначно-правильного
решения . Например, при делении чисел с разной точностью (количество знаков после запятой,
ведь числа с фиксированной запятой) не понятно , какую точность должен иметь результат,
остановился на том, что операцию деления можно выполнять только над числами с одинаковой точностью .
Тоже самое относится к операциям сложения и вычитания. По хорошему надо брать максимальную точность
из двух операндов, но я не смог догадаться как. Может быть препроцессорные диррективы могут
решить эту задачу? А вот при умножении все ясно, точность результата — это сумма точночтей операндов.
И еще одна мелочь, количество символов (22) под строку я выделил из соображения, что количество
цифр в unsigned __int64 не может быть больше 20-ти, и плюс два символа на минус и разделяющую
точку. Символ конца строки учитываю при выделении памяти под строку. Так вот хотелось бы
поинтересоваться , можно ли как-нибудь определить по размеру целого типа максимальное
количество цифр, которое он может представить?
В общем, все. Пинайте на здоровье. Интересно будет послушать ваши мысли.
Здравствуйте, XXXL, Вы писали:
XXX>Я написал класс-обертку, о котором Вы говорили, для реализации чисел с фиксированой XXX>запятой. Может найдутся желающие его "попинать" или те, кому он может пригодиться ?
Пока не удалил — попинаю.
Поскольку у тебя VAL_TYPE является внутренним представленим — то, имхо, не стоит делать конструктор с параметром VAL_TYPE. Или хотя бы сделать его explicit — чтобы избежать ошибок.
Незачем хранить value и mult. Так ты просто получаешь все ту же плавающую арифметику...
Имхо, можно проще.
template<int A> // A = 1/accuracy. То есть 0.001 --> 1000class FixedPoint
{
public:
typedef _int64 value_t;
typedef FixedPoint<A> self_t;
private:
value_t v_;
public:
value_t value() const { return v_; }
value_t floor() const { return v_ / A; }
value_t ceil() const { return (v_+A-1) / A; }
value_t round() const { return (v_+A/2) / A; }
value_t frac() const { return v_ % A; }
double dbl() const { return double(v_) / A; }
operator value_t() const { return round(); }
operator double() const { return dbl(); }
FixedPoint(int v) : v_(v * A) {}
template<int B>
FixedPoint(const FixedPoint& v) : v_(v.value() * A/B) {}
FixedPoint(double v) : v_(v * A) {}
// все остальные - только со своим типом. Чужие типы - через имплицитные конструкторы
self_t& operator = (const self_t& v) { v_ = v.value(); return *this; }
self_t& operator += (const self_t& v) { v_ += v.value(); return *this; }
self_t& operator -= (const self_t& v) { v_ += v.value(); return *this; }
self_t& operator *= (const self_t& v) { v_ = v_ * v.value() / A; return *this; }
self_t& operator /= (const self_t& v) { v_ = v_ * A / v.value(); return *this; }
};
template<int A, class T>
FixedPoint<A> operator + (FixedPoint<A> const& x, const T& y) { FixedPoint<A> z(x); z += y; return z; }
// и т.п.
// или можешь затащить их в члены класса.template<int A>
int floor(const FixedPoint<A>& v) { return v.floor(); }
template<int A>
int ceil(const FixedPoint<A>& v) { return v.ceil(); }
template<int A>
int frac(const FixedPoint<A>& v) { return v.frac(); }
template<int A>
int round(const FixedPoint<A>& v) { return v.round(); }
Д>Так как в противном случае возникает неопределенность: Д>round(-1.5, 1.0) = -1.0 (должно бы получиться -2.0) Д>в то время как Д>round(1.5, 1.0) = 2.0
Д>В MatLab round работает "одинаково" как для отрицательных чисел, так и для положительных.
неправильно работает
по определению операции округления, действительное число округляется до целого х, если оно попадает в [x-0.5, x+0.5), независимо от знака.