Mod 最接近于零

Mod Closest to Zero

我有一个角度,我需要return一个在[-180:180]范围内的代表性角度。

我已经编写了一个函数来执行此操作,但这似乎是一个简单的过程,我想知道是否已经有一个运算符或函数可以执行此操作:

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

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

我已经制作了一个 live example 用于测试预期功能。

我不知道标准运算符或函数,但您可以在单个表达式中完成:

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

注意:我原来的回答使用了下面的表达方式:

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

这要简洁得多,但依赖于负数的模数为正数。某些语言(例如 Python)具有这些语义,但 C 和 C++ 通常没有。上面的表达式通过添加额外的 360 度偏移来解决这个问题。

代码是最优的或至少接近最优。某些平台可能会因一些变化而更好地工作。

没有一个 C 整数运算符可以处理这个问题。

挑战在于结果的范围是 [-180:180],这是 361 个不同的值。不清楚是否允许有func(180) return -180.

下一个挑战是让代码在整个 [INT_MIN...INT_MAX] 范围内工作,因为 angle + 180 可能会溢出。 angle %= 360; 负责。

以下是 OP 代码的有效变体,在流水线机器上可能 运行 更快。它只执行一个 % 操作——可以想象是最昂贵的。正 angle returns [-179:180] 和负 angle returns [-180:179]

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

以下是 return 值 [-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 开始可用。)它将 return FP 值 [-180:180]。注意:int 的整数范围可能超过 double 可以精确表示的范围。

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

你需要的很简单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;
}