функции округления в C.

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Absurd, нас не интересуют краевые значения задачи. Постановка намного проще: есть два соседних пикселя. Задача подсветить тот пиксел, значение которого ближе к данному рациональному значению. Округление даёт в два раза большую точность отображения, чем просто отбрасывание дробной части. Здесь не о чём спорить потому, что это очевидный теоретический факт.

Если для тебя это не очевидно, то ради интереса попробуй нарисовать отрезок прямой попиксельно старым дедовским способом - имея две точки и использую уравнение прямой из школьной геометрии. Нарисуй двумя способами - с отбрасыванием дробной части и с округлением. Тебя сразу же бросится в глаза тот факт, что отрезок второй прямой будет значительно глаже.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Romeo писал(а):Если для тебя это не очевидно, то ради интереса попробуй нарисовать отрезок прямой попиксельно старым дедовским способом - имея две точки и использую уравнение прямой из школьной геометрии. Нарисуй двумя способами - с отбрасыванием дробной части и с округлением. Тебя сразу же бросится в глаза тот факт, что отрезок второй прямой будет значительно глаже.
Предложи код.

Я делал так:

[php]
void paintLine(HDC hdc) {
const double theta = 0.5235987756; // 30 degrees
const double cosTheta = cos(theta);
const double sinTheta = sin(theta);
for (int i = 0; i != 1000; ++i) {
double x = double(i)*cosTheta;
double y = double(i)*sinTheta;
SetPixel(hdc, (int)x, (int)y, RGB(0, 0, 0));
}

for (int i = 0; i != 1000; ++i) {
double x = double(i)*cosTheta;
double y = double(i)*sinTheta;
SetPixel(hdc, round(x), round(y) + 50, RGB(0, 0, 0));
}
}

int round(const double& arg) {
if (arg >= 0.0)
return ((int)(arg + 0.5));
else
return ((int)(arg - 0.5));
}
[/php]

Результат вложен
Вложения
romeo_test.gif
romeo_test.gif (3.99 КБ) 371 просмотр
2B OR NOT(2B) = FF
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Эх, не понять тебе этого. Не писал, зачит, на asm'е под 320*200 :) Там сразу разница чувствуется :) Особенно для более сложных линий: например для окружности.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
BBB
Сообщения: 1298
Зарегистрирован: 27 дек 2005, 13:37

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

Вот, кстати, пример с окружностями. В первом случае (отбрасывание целой части) видны "углы" (точки) по "сторонам света" окружности.

Код: Выделить всё

void CRoundDlg::OnPaint() 
{
  if (IsIconic())
  {
	  CPaintDC dc(this); // device context for painting

	  SendMessage(WM_ICONERASEBKGND, (WPARAM) dc.GetSafeHdc(), 0);

	  // Center icon in client rectangle
	  int cxIcon = GetSystemMetrics(SM_CXICON);
	  int cyIcon = GetSystemMetrics(SM_CYICON);
	  CRect rect;
	  GetClientRect(&rect);
	  int x = (rect.Width() - cxIcon + 1) / 2;
	  int y = (rect.Height() - cyIcon + 1) / 2;

	  // Draw the icon
	  dc.DrawIcon(x, y, m_hIcon);
  }
  else
  {
//   CDialog::OnPaint();

    CPaintDC dc(this); // device context for painting
    int i;

    const double pi = 3.1415926535;
    const int    raduis = 100;
    int centerX1, centerY, centerX2;
    int    x1, y1, x2, y2;

    double x_, y_;

    centerX1 = centerY = 110;
    centerX2 = centerX1 + 2 * raduis + 20;

    for (i = 0; i < 1000; ++i) { 

       x_ = raduis * cos (2.0 * pi * i / 1000);
       y_ = raduis * sin (2.0 * pi * i / 1000);

       x1 = centerX1 + (int)x_;
       y1 = centerY  + (int)y_;

       x2 = centerX2 + round (x_);
       y2 = centerY  + round (y_);

       dc.SetPixel(x1, y1, RGB(0, 0, 0)); 


       dc.SetPixel(x2, y2, RGB(0, 0, 0)); 

    };
  }
};
Вложения
OKR.jpg
OKR.jpg (34.19 КБ) 371 просмотр
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Romeo писал(а):Эх, не понять тебе этого. Не писал, зачит, на asm'е под 320*200 :) Там сразу разница чувствуется :) Особенно для более сложных линий: например для окружности.
Вообще-то обе линии идентичны - обе имеют некрасивые узлы через одинаковый интервал. Можно даже математически доказать что способ округления не влияет на гладкость получаемых фигур при этом алгоритме - тут надо использовать Брезенхайм.

В общем, я ввязался в этот спор потому что цифровая обработка иллюстраций была связана с моим дипломным проектом и я очень упорно в свое время боролся с нузкочастотными муарами которые возникают из-за циклических ошибок округления в double'ах. Способ округления влиял только на фазу муара но ни на его частоту или амплитуду. Пришлось перейти на Рациональные числа из __int64 числителя и такого же знаменателя. В отличие от фиксированной запятой так можно изобразить бесконечные циклические дроби (1/3 например) без потери. Вообще, такие числа можно как угодно умножать и делить без потери разрядов.
2B OR NOT(2B) = FF
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Вот, что и следовало доказать. В случае прямой действительно разницы почти никакой по той причине, что погрешность линейная и уравнение тоже линейное.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Svet_v
Сообщения: 1
Зарегистрирован: 18 сен 2010, 23:05

А как можно реализовать округление не просто до целых, а до заданной точности,например, 0,001?
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Умножить 1000, округлить, разделить на 1000.
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Ответить