Pers.narod.ru. Алгоритмы. Round в C++ |
Как известно, в C/C++ нет стандартного метода округления вещественных чисел до целых, и одна
из типовых для начинающих программистов задач - собственная реализация такого метода, обычно называемого round
.
Корректно и в общем виде
решить эту задачу не так легко,
хотя если речь идёт просто об округлении вещественного до целого - большой проблемы нет и вполне
сработает такая реализация:
//round in c++ #include <stdio.h> #include <math.h> double round (double x) { return ((x - floor(x)) >= 0.5) ? ceil(x) : floor(x); } int main() { double f[]={-3.7,3.7,-0.15,0.15,0.5,0.65,0}; //0 must be the last item for (int i=0; ;i++) { printf ("\nround(%lf)=%lf",f[i],round(f[i])); if (f[i]==0) break; } getchar(); return 0; }
Гораздо чаще проблемы вызывает округление числа до N
разрядов в дробной части, так,
естественно развивающая предыдущий листинг реализация округления чисел до 2 знаков в
дробной части:
#include <stdio.h> #include <math.h> double round2 (double x) { double x2=x*100; return (x2 - floor(x2)>=0.5 ? ceil(x2) : floor(x2))/100; } int main() { double f[]={1.255,123.255,1000.5,123.49,0}; //0 must be the last item //1.255 -> 1.25 - error! for (int i=0; ;i++) { printf ("\nround(%lf)=%.02lf",f[i],round2(f[i])); if (f[i]==0) break; } getchar(); return 0; }
работает некорректно (см. замечание в листинге).
В библиотеках VCL и CLX, реализующих типовые интерфейсные компоненты для GUI графических операционных систем, сохраняется та же проблема, приведём кусочек кода на C++ Builder:
void __fastcall TForm1::Button1Click(TObject *Sender) { char buffer[80]; sprintf (buffer," round(%lf)=%.02lf",1.255,round2(1.255)); Memo1->Lines->Add(AnsiString(buffer)); SetRoundMode(rmNearest); Memo1->Lines->Add(RoundTo(1.255,-2)); }
Здесь округление до 2 знаков в дробной части делается с помощью нашего показанного выше метода
round2
и стандартной функции RoundTo
из библиотеки VCL. Обратите внимание, что в заголовочный
файл модуля *.h
должна быть подключена библиотека
#include <Math.hpp>
а не сишная <math.h>
. По скриншоту окна приложения видно, что RoundTo
тоже сработает неверно на "волшебном" числе
1.255
:
Между прочим, RoundTo
считается так называмым "банкирским" округлением,
а есть ещё SimpleRoundTo
— обычное арифметическое округление, но оно на нашем числе работает также некорректно.
Можно попробовать в качестве альтернативы классическое
char string[20]; sprintf(string,"%.02f",var);
- после чего число содержится в Си-строке string
. Увы, результат в моём C++ Builder при округлении до 2 знаков после запятой
оказался столь же неверным - 1.25
вместо 1.26
.
Остаётся либо использовать непростые и платформенно-зависимые решения, как здесь, либо искать Си-среду, где округление есть и сделано без ошибки, либо, наконец, использовать "шаманские" коды:
float round(float f) { double t = (double)f + 6755399441055744.0; return (float)*((int *)(&t)); }
Забавно, но это работает :) А вот версия для 2 разрядов после запятой, построенная на этой round
,
float round2 (float f) { // до сотых float rf = round(f); float t = (f - rf) * 100; return rf + round(t) * 0.01f; }
не смогла правильно округлить упрямое число 1.255
в стареньком Borland C++ 3.1,
но в C++ Builder 6 уже сработала верно. Значит ли это, что не найдётся другого "упрямого" числа? Думаю,
вопрос остаётся открытым, поищите решение, интересно...
гостевая; E-mail |