Мне тут потребовалось отобразить на экране дугу окружности большого радиуса.
Лобовое решение - воспользоваться функцией
Ellipse, что я и сделал. И радовался, до тех пор, пока (с увеличением радиуса моей окружности) дуга не сместилась с позиции, на которой она должна была находиться, к краю экрана, после чего пропала.
Несколько озадаченный таким ее поведением, я решил не разбираться, а воспользоваться функцией
Arc. Я уж не знаю, какие параметры я ей задал (они считались в зависимости от выбранного масштаба), но дуга, которую она мне выдала, не только не соответствовала заданной, но и имела форму, которую (если верить MSDN) эта функция начертить не может в принципе. Поэтому на эту функцию я плюнул и дальше решил экспериментировать с Ellipse.
Сперва я подумал, что неправильно считаю координаты углов описывающего прямоугольника. Несколько экспериментов с проверкой ручным счетом показали, что все в порядке. Окружность с заданными параметрами действительно должна была проходить через экран. Потом я подумал, что при переходе от типа double, в котором я проводил расчеты, к типу int, который принимают функции, происходит потеря старших разрядов. Однако я быстро убедился, что это не так - значения координат достигали только десятков миллионов, в то время как int может вместить 2 миллиарда.
Тогда я полез в старый MSDN, установленный на моем компьютере, и обнаружил в описаниях обоих функций примечание: Windows 95/98/Me: The sum of the coordinates of the bounding rectangle cannot exceed 32 767. The sum of nLeftRect and nRightRect or nTopRect and nBottomRect parameters cannot exceed 32 767. Про XP там ничего не было написано, но поставив ограничение радиуса 32 767/2 я действительно получил корректные дуги. Поскольку при большем радиусе кривизны дугу на экране можно без потерь точности заменить отрезком прямой, моя задача была решена. Но мне все-таки хотелось разобраться, как работают Ellipse и Arc.
Поскольку Windows 95 и 98 являлись 16-разрядными системами (если я ничего не путаю), я решил, что значение радиуса не должно превышать половины максимального значения int, которое в 32-разрядной XP равно 2 147 483 647. Однако, в моих экспериментах дуги пропадали уже при достижении радиусом значения десятков миллионов, т.е. задолго до этого предела.
Тогда я просто стал искать максимальное значение радиуса, при котором дуга окружности отображается корректно. В результате выяснилось, что
при превышении радиусом окружности значения 67 108 863 (0x3ff ffff) функции Ellipse и Arc перестают работать корректно.С чем это связано, я точно не знаю, однако могу предположить, что при расчете координат отображаемых точек внутри функций Ellipse и Arc производится возведение радиуса в квадрат (при предварительном преобразовании его в double). При этом 26-разрядное число дает 52-разрядное, а мантисса double содержит как раз 52 разряда. Поэтому при превышении этого значения происходит переполнение мантиссы и потеря точности, что и приводит к некорректной работе.
Вот такая недокументированная (по крайней мере я не встречал нигде упоминаний об этом) особенность. Не попадайтесь :)