2015-07-21 13 views
6

У меня есть угол, и мне нужно вернуть представительный угол в диапазоне [-180: 180].Mod Ближайший к нулю

Я написал функцию, чтобы сделать это, но, кажется, такой простой процесс, мне было интересно, есть ли оператор или функция, которая уже сделала это:

int func(int angle){ 
    angle %= 360; 

    if(angle > 180){ 
     angle -=360; 
    }else if(angle < -180){ 
     angle += 360; 
    } 
    return angle; 
} 

Я сделал live example для тестирования ожидаемой функциональности.

+2

Не вы просто ищете 'обратный станд :: остаток (угла, 180);'? – atlaste

+1

Можете ли вы выбрать один из C и C++ для этого? – fuz

+0

@atlaste Подтверждает то, что решает мои примеры, но это, к сожалению, не сработает. Например, ввод -190 должен давать 170, но 'остаток (-190, 180)' равен -10. –

ответ

3

Код оптимален или, по крайней мере, почти так. Некоторые платформы могут работать лучше с некоторыми вариантами.

Существует не один оператор целочисленного С, который обрабатывает это.

Проблемы, связанные с этим, состоит в том, что диапазон результатов: [-180:180] и это 361 различное значение. Неясно, разрешено ли иметь func(180)-180.

Следующая задача состоит в том, чтобы код работал по всему диапазону [INT_MIN...INT_MAX], так как angle + 180 может переполняться. angle %= 360; позаботится об этом.

Ниже приведено эффективное изменение кода OP, которое может работать быстрее на машинах с трубкой. Это только одна операция % - возможно, самая дорогая. Положительный angle возвращает [-179: 180] и отрицательный angle возвращает [-180: 179]

int func2(int angle) { 
    angle %= 360; 
    return angle + 360*((angle < -180) - (angle > 180)); 
} 

Ниже приведено один вкладыш, который возвращает значение [-180: 179]. Он не использует angle + 180, так как это может переполняться.

int func3(int angle) { 
    return ((angle % 360) + (360+180))%360 - 180; 
} 

Существует <math.h> функция double remainder(double x, double y);, которая близко соответствует цели OP еще. (Возможно, доступно с C99.) Он вернет значения FP [-180: 180]. Примечание: int может иметь целочисленный диапазон, превышающий то, что может точно представлять double.

int func4(int angle) { 
    angle = remainder(angle, 360.0); 
    return angle; 
} 
+0

765 должен возвращать 45 и -765 должен возвращать -45. В вашей реализации они возвращают 405 и 315 соответственно. –

+0

@ Джонатан Ми Ли ладонь! Код исправлен. – chux

+0

@JonathanMee Это возвращает '170' для ввода' -190', так что он действительно работает. Посмотрите на http://ideone.com/T0Ewi6. – fsacer

3

Я не знаю стандартного оператора или функции, но вы можете сделать это в одном выражении:

int func(int angle) { 
    return ((((angle + 180) % 360) + 360) % 360) - 180; 
} 

Примечание: Мой первоначальный ответ используется следующее выражение:

((angle + 180) % 360) - 180; 

Это намного опережает, но полагается на то, что модуль отрицательного числа положителен. Некоторые языки (например, Python) имеют эти семантики, но C и C++ обычно этого не делают. Вышеприведенное выражение объясняет это добавлением дополнительного сдвига 360.

+1

будет работать для угла == -190? –

+2

Вы проверили это с примерами OP? Это не работает. http://ideone.com/3oHleK – Adrian

+1

Это не работает: http://ideone.com/VrTJTt – NathanOliver

1

Что вам нужно, это простая реализация wrap функции:

#include <stdio.h> 

int wrap(int value, int lower_bound, int upper_bound) { 
    int range = upper_bound - lower_bound; 
    value -= lower_bound; // shift from [lower, upper) to [0, upper - lower)... 
    value %= range;  // ... so modulo operator could do all the job 
    if (value < 0) {  // deal with negative values 
     value += range; 
    } 
    value += lower_bound; // shift back to [lower, upper) 
    return value; 
} 

void show(int value, int lower_bound, int upper_bound) { 
    printf("%4d wrapped to the range of [%d, %d) is %d\n", 
     value, lower_bound, upper_bound, 
     wrap(value, lower_bound, upper_bound) 
    ); 
} 

int main(void) { 
    // examples 
    show(0, -180, 180); 
    show(-200, -180, 180); 
    show(720, -180, 180); 
    show(1234, -180, 180); 
    show(5, 0, 10); 
    show(-1, 0, 10); 
    show(112, 0, 10); 
    show(-3, -10, 0); 
    show(7, -10, 0); 
    show(-11, -10, 0); 
    return 0; 
} 
+0

Код, кажется, выключен. e, g, 'wrap (value, -180,180)' никогда не возвращает '180' и' wrap (value, 2,2) 'делает'% 0'. Может быть, 'upper_bound' действительно' upper_bound_plus_1'? Примечание: переполнение кода на 'wrap (значение, INT_MIN, 0/* или больше * /)' – chux

+0

@chux: Нет, это не по одному. Поэтому в диапазоне <нижняя, верхняя) левая «скобка» «острая» (что означает, что диапазон включает в себя более низкое значение), а справа - «круглый» (что означает, что звонок был до, но НЕ ВКЛЮЧАЕТ верхнее значение). Таким образом, рассчитанное значение точно: lower <= value nsilent22

+0

С чтением дополнительных имен переменных 'lower_bound, upper_bound', хотя граница была симметричной. Я вижу, что в ваших комментариях это не так. – chux