2014-11-04 9 views
0

Я пытаюсь объяснить пересечения меридиана точно и я столкнулся следующий вопрос о IEEE арифметику с плавающей точкой (округляется до ближайшего):Деление с плавающей запятой достаточно для определения номера ячейки точно?

Пусть п быть целым числом, и d небольшое положительное число. Имеет ли

у = п * 360 - д < п * 360

гарантия того, что пол (у /360) < п? Здесь все операции (* - </floor) равны , как плавающие операции (с использованием, например, двойной точности IEEE).

О том, что 360 в этом вопросе заменяется каким-либо другим положительным числом чисел с плавающей запятой . (Тот же вопрос возникает всякий раз, когда величина с плавающей точкой в настоящее время назначается равномерно бункеров.)

ответ

0

n * 360 - d < n * 360 ->0 - d < 0 ->d > 0 верно, потому что «д (есть) небольшое положительное число » ,

Значение n пока не имеет значения.


y = n * 360 - d ->y/360 = n - d/360 ->

С 0.0 <= q < 1.0,
floor(y/360) + q = n - d/360 ->floor(y/360) - n = -q - d/360

Для всех значений q и d, -q - d/360 < 0 ->

floor(y/360) - n < 0 ->floor(y/360) < n. Q.E.D.


Если 360 был заменен x как любое целое число больше 0, то ответ все тот же. I думаю это также верно, если x заменяется на любое число> = 1.0. Придется подумать о 0 < x < 1.

маленький из d не имеет значения до сих пор - только что это положительное число (d > 0).

+0

К сожалению, неравенство п * 360 - d <п * 360 должен был быть истолковано как «то, что вы получите, если вы сделали все операции с использованием арифметики с плавающей точкой». Таким образом, при достаточно малых d, например, 1.0e-30, , неравенство выполняется только при n = 0 (с использованием double). Я отредактирую на вопрос, чтобы уточнить. – cffk

+0

Перейдем к проблеме более высокого уровня: «для учета пересечений простого меридиана точно» В 'C', а не использовать' y = n * 360 - d; 'для некоторого' n', используйте 'longitude = fmod (долгота, 360.0); 'и терпеть _no_ потерю точности независимо от' долготы'. [ref] (http://stackoverflow.com/questions/20928253/is-fmod-exact-when-y-is-an-integer) – chux

+0

Да, я уже использую это (и мне приходится иметь дело с досадной проблемой, которая результат может лежать в (-360,360)).В моем текущем приложении мне нужно определить, какой период долготы я нахожу, т. Е. Пол (долгота/360). – cffk

0

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

int bin(double x, double m) 

, вычисляющий

int(floor(x/m)) 

точно. Предположим, m положительный и что результат находится в диапазоне int.

Первая попытка

int bin0(double x, double m) { 
    return int(std::floor(x/m)); 
} 

, но это не удается для случая м = 360,0 и х = -denorm_min (0 возвращается вместо -1).

Поскольку эта неудача только для х близко к нулю, то вторая попытка

int bin1(double x, double m) { 
    int n = int(std::floor(x/m)); 
    return n == 0 && x < 0 ? -1 : n; 
} 

Я верю это возвращает точный ответ при условии что п * м является точно представляемый как двойной. Для m = 360,0 включает в себя все n представляется в виде 32-разрядного целого. Я прав? Доказательством будет приятно!

Если это условие не выполняется, например, м = 0,1, то лучшее, что я могу придумать является

int bin2(double x, double m) { 
    double z = std::fmod(x, m); 
    return int(std::floor((x - z)/m + 0.5)) + (z < 0 ? -1 : 0); 
} 

ли это всегда возвращает правильный результат? Есть ли некоторые «чистые» решения ?

ADDENDUM: В моем приложении мне нужно было получить только четность номера ячейки (четного или нечетного). (Мое приложение измеряет площадь геодезического многоугольника , и мне нужно отслеживать, охватывает ли край полюс четное или нечетное число раз.) Поэтому предложение chux использовать remquo является хорошим. К сожалению (1) std :: remquo требует C++ 11 и (2) более серьезно, реализация glibc remquo является ошибкой; см. это bug report. Так что я в конечном итоге делает по существу

int binparity(real x, real m) { 
    // return the parity of int(floor(x/m)) 
    x = std::fmod(x, 2 * m); 
    return (x >= 0 && x < m) || x < -m ? 0 : 1 
} 
+0

(повтор: вторая попытка) Предположим, что 'm> = 1.0': Если' x/m' не переполняется до 0.0, 'return int (std :: floor (x/m) 'очевидно работает.Если' x> = + 0.0', тоже работает. Остается только один случай, когда 'x <0.0' и' x/m' заканчиваются. Код 'n == 0 && x <0 ? -1: n; 'позаботится об этом. Проблемы становятся более сложными, если' m <1.0'. Предложите указать диапазон 'm'. Вы заботитесь о' -0.0'? Ваш подход возвращает 0. Альтернатива, когда ' m> = 1.0': 'double q = x/m; return floor (q? q, x);' – chux

+0

Имейте сомнение 'int (std :: floor ((x - z)/m + 0.5)) ... 'работает в угловых случаях из-за неточного отношения в' (x - z)/m + 0,5) '. – chux

+0

Может быть,' int bin1 x (double x, double m) {double q = x/m; return (int) floor (q? q, - (x <0.0)); } 'для любого' m> 0'. – chux

Смежные вопросы