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 уже сработала верно. Значит ли это, что не найдётся другого "упрямого" числа? Думаю,
вопрос остаётся открытым, поищите решение, интересно...
|
|