如何包裹范围

时间:2022-11-29 11:51:33

Angles in my program are expressed in 0 to 2pi. I want a way to add two angles and have it wrap around the 2pi to 0 if the result is higher than 2pi. Or if I subtracted an angle from an angle and it was below 0 it would wrap around 2pi.

程序中的角度以0到2pi表示。我想要一种方法来添加两个角度,如果结果高于2pi,则将它包围在2pi到0之间。或者,如果我从一个角度减去一个角度并且它低于0,它将环绕2pi。

Is there a way to do this?

有没有办法做到这一点?

Thanks.

6 个解决方案

#1


33  

What you are looking for is the modulus. The fmod function will not work because it calculates the remainder and not the arithmetic modulus. Something like this should work:

你要找的是模数。 fmod函数不起作用,因为它计算余数而不是算术模数。像这样的东西应该工作:

inline double wrapAngle( double angle )
{
    double twoPi = 2.0 * 3.141592865358979;
    return angle - twoPi * floor( angle / twoPi );
}

Edit:

The remainder is commonly defined as what is left over after long division (eg. the remainder of 18/4 is 2, because 18 = 4 * 4 + 2). This gets hairy when you have negative numbers. The common way to find the remainder of a signed division is for the remainder to have the same sign as the result (eg. the remainder of -18/4 is -2, because -18 = -4 * 4 + -2).

剩余部分通常被定义为长分割后遗留的内容(例如,18/4的剩余部分为2,因为18 = 4 * 4 + 2)。当你有负数时,这会变得毛茸茸。找到有符号除法的余数的常用方法是使余数具有与结果相同的符号(例如,-18 / 4的余数为-2,因为-18 = -4 * 4 + -2)。

The definition of x modulus y is the smallest positive value of m in the equation x=y*c+m, given c is an integer. So 18 mod 4 would be 2 (where c=4), however -18 mod 4 would also be 2 (where c=-5).

如果c是整数,则x模数y的定义是等式x = y * c + m中m的最小正值。因此18 mod 4将是2(其中c = 4),然而-18 mod 4也将是2(其中c = -5)。

The simplest calculation of x mod y is x-y*floor(x/y), where floor is the largest integer that is less than or equal to the input.

x mod y的最简单计算是x-y * floor(x / y),其中floor是小于或等于输入的最大整数。

#2


15  

angle = fmod(angle, 2.0 * pi);
if (angle < 0.0)
   angle += 2.0 * pi;

Edit: After re-reading this (and looking at Jonathan Leffler's answer) I was a bit surprised by his conclusion, so I rewrote the code to what I considered a somewhat more suitable form (e.g., printing out a result from the computation to ensure the compiler couldn't just discard the computation completely because it was never used). I also changed it to use the Windows performance counter (since he didn't include his timer class, and the std::chrono::high_resolution_timer is completely broken in both the compilers I have handy right now).

编辑:在重新阅读之后(并查看Jonathan Leffler的回答)我对他的结论感到有些惊讶,所以我将代码改写为我认为更合适的形式(例如,打印出计算结果以确保编译器不能完全丢弃计算,因为它从未使用过)。我还改为使用Windows性能计数器(因为他没有包含他的计时器类,并且std :: chrono :: high_resolution_timer在我现在使用的两个编译器中完全被破坏了)。

I also did a bit of general code cleanup (this is tagged C++, not C), to get this:

我还做了一些通用代码清理(这是标记的C ++,而不是C),得到这个:

#include <math.h>
#include <iostream>
#include <vector>
#include <chrono>
#include <windows.h>

static const double PI = 3.14159265358979323844;

static double r1(double angle)
{
    while (angle > 2.0 * PI)
        angle -= 2.0 * PI;
    while (angle < 0)
        angle += 2.0 * PI;
    return angle;
}

static double r2(double angle)
{
    angle = fmod(angle, 2.0 * PI);
    if (angle < 0.0)
        angle += 2.0 * PI;
    return angle;
}

static double r3(double angle)
{
    double twoPi = 2.0 * PI;
    return angle - twoPi * floor(angle / twoPi);
}

struct result {
    double sum;
    long long clocks;
    result(double d, long long c) : sum(d), clocks(c) {}

    friend std::ostream &operator<<(std::ostream &os, result const &r) {
        return os << "sum: " << r.sum << "\tticks: " << r.clocks;
    }
};

result operator+(result const &a, result const &b) {
    return result(a.sum + b.sum, a.clocks + b.clocks);
}

struct TestSet { double start, end, increment; };

template <class F>
result tester(F f, TestSet const &test, int count = 5)
{
    LARGE_INTEGER start, stop;

    double sum = 0.0;

    QueryPerformanceCounter(&start);

    for (int i = 0; i < count; i++) {
        for (double angle = test.start; angle < test.end; angle += test.increment)
            sum += f(angle);
    }
    QueryPerformanceCounter(&stop);

    return result(sum, stop.QuadPart - start.QuadPart);
}

int main() {

    std::vector<TestSet> tests {
        { -6.0 * PI, +6.0 * PI, 0.01 },
        { -600.0 * PI, +600.0 * PI, 3.00 }
    };


    std::cout << "Small angles:\n";
    std::cout << "loop subtraction: " << tester(r1, tests[0]) << "\n";
    std::cout << "            fmod: " << tester(r2, tests[0]) << "\n";
    std::cout << "           floor: " << tester(r3, tests[0]) << "\n";
    std::cout << "\nLarge angles:\n";
    std::cout << "loop subtraction: " << tester(r1, tests[1]) << "\n";
    std::cout << "            fmod: " << tester(r2, tests[1]) << "\n";
    std::cout << "           floor: " << tester(r3, tests[1]) << "\n";

}

The results I got were as follows:

我得到的结果如下:

Small angles:
loop subtraction: sum: 59196    ticks: 684
            fmod: sum: 59196    ticks: 1409
           floor: sum: 59196    ticks: 1885

Large angles:
loop subtraction: sum: 19786.6  ticks: 12516
            fmod: sum: 19755.2  ticks: 464
           floor: sum: 19755.2  ticks: 649

At least to me, the results seem to support a rather different conclusion than Jonathon reached. Looking at the version that does subtraction in a loop, we see two points: for the large angles test it produces a sum that's different from the other two (i.e., it's inaccurate) and second, it's horribly slow. Unless you know for certain that your inputs always start out nearly normalized, this is basically just unusable.

至少在我看来,结果似乎支持了一个与乔纳森达成的完全不同的结论。看一下在循环中进行减法的版本,我们看到两点:对于大角度测试,它产生的总和与其他两个不同(即,它是不准确的),其次,它非常慢。除非你确定你的输入总是开始接近标准化,否则这基本上是无法使用的。

Between the fmod version and the floor version there seems to be no room for argument--they both produce accurate results, but the fmod version is faster in both the small angle and large angle tests.

在fmod版本和地板版本之间似乎没有争论的余地 - 它们都能产生准确的结果,但fmod版本在小角度和大角度测试中都更快。

I did a bit more testing, experimenting with increasing the number of repetitions and decreasing the step sizes in the large angles test. Although I suppose it's possible it's simply due to a difference in platform or compiler, I was unable to find any circumstance or situation that even came close to upholding Jonathan's results or conclusion.

我做了一些测试,尝试增加重复次数并减小大角度测试中的步长。虽然我认为这可能仅仅是由于平台或编译器的差异,但我无法找到任何甚至接近维护Jonathan结果或结论的情况或情况。

Bottom line: if you have a lot of prior knowledge about your input, and know it'll always be nearly normalized before you normalize it, then you might be able to get away with doing subtraction in a loop. Under any other circumstance, fmod is the clear choice. There seems to be no circumstance in which the floor version makes any sense at all.

结论:如果你有很多关于输入的先验知识,并且知道在你将其标准化之前它总是接近标准化,那么你可能能够在循环中进行减法。在任何其他情况下,fmod是明确的选择。似乎没有任何情况下地板版本有任何意义。

Oh, for what it's worth:
OS: Windows 7 ultimate
Compiler: g++ 4.9.1
Hardware: AMD A6-6400K

#3


8  

Out of curiosity, I experimented with three algorithms in other answers, timing them.

出于好奇,我在其他答案中尝试了三种算法,对它们进行计时。

When the values to be normalized are close to the range 0..2π, then the while algorithm is quickest; the algorithm using fmod() is slowest, and the algorithm using floor() is in between.

当要归一化的值接近0..2π时,则while算法最快;使用fmod()的算法最慢,而使用floor()的算法介于两者之间。

When the values to be normalized are not close to the range 0..2π, then the while algorithm is slowest, the algorithm using floor() is quickest, and the algorithm using fmod() is in between.

当要归一化的值不接近0..2π的范围时,则while算法最慢,使用floor()的算法最快,并且使用fmod()的算法介于两者之间。

So, I conclude that:

所以,我的结论是:

  • If the angles are (generally) close to normalized, the while algorithm is the one to use.
  • 如果角度(通常)接近标准化,则while算法是要使用的算法。

  • If the angles are not close to normalized, then the floor() algorithm is the one to use.
  • 如果角度不接近标准化,则floor()算法是要使用的算法。

Test results:

r1 = while, r2 = fmod(), r3 = floor()

r1 = while,r2 = fmod(),r3 = floor()

Near Normal     Far From Normal
r1 0.000020     r1 0.000456
r2 0.000078     r2 0.000085
r3 0.000058     r3 0.000065
r1 0.000032     r1 0.000406
r2 0.000085     r2 0.000083
r3 0.000057     r3 0.000063
r1 0.000033     r1 0.000406
r2 0.000085     r2 0.000085
r3 0.000058     r3 0.000065
r1 0.000033     r1 0.000407
r2 0.000086     r2 0.000083
r3 0.000058     r3 0.000063

Test code:

The test code used the value shown for PI. The C standard does not define a value for π, but POSIX does define M_PI and a number of related constants, so I could have written my code using M_PI instead of PI.

测试代码使用PI显示的值。 C标准没有定义π的值,但POSIX确实定义了M_PI和许多相关的常量,所以我可以使用M_PI而不是PI编写我的代码。

#include <math.h>
#include <stdio.h>
#include "timer.h"

static const double PI = 3.14159265358979323844;

static double r1(double angle)
{
    while (angle > 2.0 * PI)
        angle -= 2.0 * PI;
    while (angle < 0)
        angle += 2.0 * PI;
    return angle;
}

static double r2(double angle)
{
    angle = fmod(angle, 2.0 * PI);
    if (angle < 0.0)
        angle += 2.0 * PI;
    return angle;
}

static double r3(double angle)
{
    double twoPi = 2.0 * PI;
    return angle - twoPi * floor( angle / twoPi );
}

static void tester(const char * tag, double (*test)(double), int noisy)
{
    typedef struct TestSet { double start, end, increment; } TestSet;
    static const TestSet tests[] =
    {
        {   -6.0 * PI,   +6.0 * PI, 0.01 },
    //  { -600.0 * PI, +600.0 * PI, 3.00 },
    };
    enum { NUM_TESTS = sizeof(tests) / sizeof(tests[0]) };
    Clock clk;
    clk_init(&clk);
    clk_start(&clk);
    for (int i = 0; i < NUM_TESTS; i++)
    {
        for (double angle = tests[i].start; angle < tests[i].end; angle += tests[i].increment)
        {
            double result = (*test)(angle);
            if (noisy)
                printf("%12.8f : %12.8f\n", angle, result);
        }
    }
    clk_stop(&clk);
    char buffer[32];
    printf("%s %s\n", tag, clk_elapsed_us(&clk, buffer, sizeof(buffer)));
}

int main(void)
{
    tester("r1", r1, 0);
    tester("r2", r2, 0);
    tester("r3", r3, 0);
    tester("r1", r1, 0);
    tester("r2", r2, 0);
    tester("r3", r3, 0);
    tester("r1", r1, 0);
    tester("r2", r2, 0);
    tester("r3", r3, 0);
    tester("r1", r1, 0);
    tester("r2", r2, 0);
    tester("r3", r3, 0);
    return(0);
}

Testing on Mac OS X 10.7.4 with the standard /usr/bin/gcc (i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.9.00)). The 'close to normalized' test code is shown; the 'far from normalized' test data was created by uncommenting the // comment in the test data.

在Mac OS X 10.7.4上使用标准/ usr / bin / gcc进行测试(i686-apple-darwin11-llvm-gcc-4.2(GCC)4.2.1(基于Apple Inc. build 5658)(LLVM build 2336.9.00) ))。显示“接近标准化”的测试代码;通过取消注释测试数据中的//注释来创建“远离标准化”的测试数据。

Timing with a home-built GCC 4.7.1 is similar (the same conclusions would be drawn):

使用自制GCC 4.7.1的时间安排类似(将得出相同的结论):

Near Normal     Far From Normal
r1 0.000029     r1 0.000321
r2 0.000075     r2 0.000094
r3 0.000054     r3 0.000065
r1 0.000028     r1 0.000327
r2 0.000075     r2 0.000096
r3 0.000053     r3 0.000068
r1 0.000025     r1 0.000327
r2 0.000075     r2 0.000101
r3 0.000053     r3 0.000070
r1 0.000028     r1 0.000332
r2 0.000076     r2 0.000099
r3 0.000050     r3 0.000065

#4


4  

You can use something like this:

你可以使用这样的东西:

while (angle > 2pi)
    angle -= 2pi;

while (angle < 0)
    angle += 2pi;

Basically, you have to change the angle by 2pi until you are assured that it doesn't exceed 2pi or fall below 0.

基本上,你必须将角度改变2pi,直到你确信它不超过2pi或低于0。

#5


1  

Simple trick: Just add an offset, which must be a multiple of 2pi, to bring the result into a positive range prior to doing the fmod(). The fmod() will bring it back into the range [0, 2pi) automagically. This works as long as you know a priori the range of possible inputs you might get (which you often do). The larger the offset you apply, the more bits of FP precision you loose, so you probably don't want to add, say, 20000pi, although that would certainly be "safer" in terms of handling very large out-of-bounds inputs. Presuming no one would ever pass input angles that sum outside the rather crazy range [-8pi, +inf), we'll just add 8pi before fmod()ing.

简单的技巧:只需添加一个偏移量,它必须是2pi的倍数,以便在执行fmod()之前将结果置于正范围内。 fmod()会自动将其恢复到[0,2pi]范围内。只要您事先了解可能获得的输入范围(您经常这样做),这种方法就有效。你应用的偏移越大,你松散的FP精度就越多,所以你可能不想添加20000pi,尽管在处理非常大的越界输入方面肯定会“更安全” 。假设没有人会传递超出相当疯狂范围[-8pi,+ inf]之外的输入角度,我们只需在fmod()之前添加8pi。

double add_angles(float a, float b)
{
    ASSERT(a + b >= -8.0f*PI);
    return fmod(a + b + 8.0f*PI, 2.0f*PI);
}

#6


0  

Likewise if you want to be in the range -2pi to 2pi then fmod works great

同样,如果你想在-2pi到2pi的范围内,那么fmod效果很好

#1


33  

What you are looking for is the modulus. The fmod function will not work because it calculates the remainder and not the arithmetic modulus. Something like this should work:

你要找的是模数。 fmod函数不起作用,因为它计算余数而不是算术模数。像这样的东西应该工作:

inline double wrapAngle( double angle )
{
    double twoPi = 2.0 * 3.141592865358979;
    return angle - twoPi * floor( angle / twoPi );
}

Edit:

The remainder is commonly defined as what is left over after long division (eg. the remainder of 18/4 is 2, because 18 = 4 * 4 + 2). This gets hairy when you have negative numbers. The common way to find the remainder of a signed division is for the remainder to have the same sign as the result (eg. the remainder of -18/4 is -2, because -18 = -4 * 4 + -2).

剩余部分通常被定义为长分割后遗留的内容(例如,18/4的剩余部分为2,因为18 = 4 * 4 + 2)。当你有负数时,这会变得毛茸茸。找到有符号除法的余数的常用方法是使余数具有与结果相同的符号(例如,-18 / 4的余数为-2,因为-18 = -4 * 4 + -2)。

The definition of x modulus y is the smallest positive value of m in the equation x=y*c+m, given c is an integer. So 18 mod 4 would be 2 (where c=4), however -18 mod 4 would also be 2 (where c=-5).

如果c是整数,则x模数y的定义是等式x = y * c + m中m的最小正值。因此18 mod 4将是2(其中c = 4),然而-18 mod 4也将是2(其中c = -5)。

The simplest calculation of x mod y is x-y*floor(x/y), where floor is the largest integer that is less than or equal to the input.

x mod y的最简单计算是x-y * floor(x / y),其中floor是小于或等于输入的最大整数。

#2


15  

angle = fmod(angle, 2.0 * pi);
if (angle < 0.0)
   angle += 2.0 * pi;

Edit: After re-reading this (and looking at Jonathan Leffler's answer) I was a bit surprised by his conclusion, so I rewrote the code to what I considered a somewhat more suitable form (e.g., printing out a result from the computation to ensure the compiler couldn't just discard the computation completely because it was never used). I also changed it to use the Windows performance counter (since he didn't include his timer class, and the std::chrono::high_resolution_timer is completely broken in both the compilers I have handy right now).

编辑:在重新阅读之后(并查看Jonathan Leffler的回答)我对他的结论感到有些惊讶,所以我将代码改写为我认为更合适的形式(例如,打印出计算结果以确保编译器不能完全丢弃计算,因为它从未使用过)。我还改为使用Windows性能计数器(因为他没有包含他的计时器类,并且std :: chrono :: high_resolution_timer在我现在使用的两个编译器中完全被破坏了)。

I also did a bit of general code cleanup (this is tagged C++, not C), to get this:

我还做了一些通用代码清理(这是标记的C ++,而不是C),得到这个:

#include <math.h>
#include <iostream>
#include <vector>
#include <chrono>
#include <windows.h>

static const double PI = 3.14159265358979323844;

static double r1(double angle)
{
    while (angle > 2.0 * PI)
        angle -= 2.0 * PI;
    while (angle < 0)
        angle += 2.0 * PI;
    return angle;
}

static double r2(double angle)
{
    angle = fmod(angle, 2.0 * PI);
    if (angle < 0.0)
        angle += 2.0 * PI;
    return angle;
}

static double r3(double angle)
{
    double twoPi = 2.0 * PI;
    return angle - twoPi * floor(angle / twoPi);
}

struct result {
    double sum;
    long long clocks;
    result(double d, long long c) : sum(d), clocks(c) {}

    friend std::ostream &operator<<(std::ostream &os, result const &r) {
        return os << "sum: " << r.sum << "\tticks: " << r.clocks;
    }
};

result operator+(result const &a, result const &b) {
    return result(a.sum + b.sum, a.clocks + b.clocks);
}

struct TestSet { double start, end, increment; };

template <class F>
result tester(F f, TestSet const &test, int count = 5)
{
    LARGE_INTEGER start, stop;

    double sum = 0.0;

    QueryPerformanceCounter(&start);

    for (int i = 0; i < count; i++) {
        for (double angle = test.start; angle < test.end; angle += test.increment)
            sum += f(angle);
    }
    QueryPerformanceCounter(&stop);

    return result(sum, stop.QuadPart - start.QuadPart);
}

int main() {

    std::vector<TestSet> tests {
        { -6.0 * PI, +6.0 * PI, 0.01 },
        { -600.0 * PI, +600.0 * PI, 3.00 }
    };


    std::cout << "Small angles:\n";
    std::cout << "loop subtraction: " << tester(r1, tests[0]) << "\n";
    std::cout << "            fmod: " << tester(r2, tests[0]) << "\n";
    std::cout << "           floor: " << tester(r3, tests[0]) << "\n";
    std::cout << "\nLarge angles:\n";
    std::cout << "loop subtraction: " << tester(r1, tests[1]) << "\n";
    std::cout << "            fmod: " << tester(r2, tests[1]) << "\n";
    std::cout << "           floor: " << tester(r3, tests[1]) << "\n";

}

The results I got were as follows:

我得到的结果如下:

Small angles:
loop subtraction: sum: 59196    ticks: 684
            fmod: sum: 59196    ticks: 1409
           floor: sum: 59196    ticks: 1885

Large angles:
loop subtraction: sum: 19786.6  ticks: 12516
            fmod: sum: 19755.2  ticks: 464
           floor: sum: 19755.2  ticks: 649

At least to me, the results seem to support a rather different conclusion than Jonathon reached. Looking at the version that does subtraction in a loop, we see two points: for the large angles test it produces a sum that's different from the other two (i.e., it's inaccurate) and second, it's horribly slow. Unless you know for certain that your inputs always start out nearly normalized, this is basically just unusable.

至少在我看来,结果似乎支持了一个与乔纳森达成的完全不同的结论。看一下在循环中进行减法的版本,我们看到两点:对于大角度测试,它产生的总和与其他两个不同(即,它是不准确的),其次,它非常慢。除非你确定你的输入总是开始接近标准化,否则这基本上是无法使用的。

Between the fmod version and the floor version there seems to be no room for argument--they both produce accurate results, but the fmod version is faster in both the small angle and large angle tests.

在fmod版本和地板版本之间似乎没有争论的余地 - 它们都能产生准确的结果,但fmod版本在小角度和大角度测试中都更快。

I did a bit more testing, experimenting with increasing the number of repetitions and decreasing the step sizes in the large angles test. Although I suppose it's possible it's simply due to a difference in platform or compiler, I was unable to find any circumstance or situation that even came close to upholding Jonathan's results or conclusion.

我做了一些测试,尝试增加重复次数并减小大角度测试中的步长。虽然我认为这可能仅仅是由于平台或编译器的差异,但我无法找到任何甚至接近维护Jonathan结果或结论的情况或情况。

Bottom line: if you have a lot of prior knowledge about your input, and know it'll always be nearly normalized before you normalize it, then you might be able to get away with doing subtraction in a loop. Under any other circumstance, fmod is the clear choice. There seems to be no circumstance in which the floor version makes any sense at all.

结论:如果你有很多关于输入的先验知识,并且知道在你将其标准化之前它总是接近标准化,那么你可能能够在循环中进行减法。在任何其他情况下,fmod是明确的选择。似乎没有任何情况下地板版本有任何意义。

Oh, for what it's worth:
OS: Windows 7 ultimate
Compiler: g++ 4.9.1
Hardware: AMD A6-6400K

#3


8  

Out of curiosity, I experimented with three algorithms in other answers, timing them.

出于好奇,我在其他答案中尝试了三种算法,对它们进行计时。

When the values to be normalized are close to the range 0..2π, then the while algorithm is quickest; the algorithm using fmod() is slowest, and the algorithm using floor() is in between.

当要归一化的值接近0..2π时,则while算法最快;使用fmod()的算法最慢,而使用floor()的算法介于两者之间。

When the values to be normalized are not close to the range 0..2π, then the while algorithm is slowest, the algorithm using floor() is quickest, and the algorithm using fmod() is in between.

当要归一化的值不接近0..2π的范围时,则while算法最慢,使用floor()的算法最快,并且使用fmod()的算法介于两者之间。

So, I conclude that:

所以,我的结论是:

  • If the angles are (generally) close to normalized, the while algorithm is the one to use.
  • 如果角度(通常)接近标准化,则while算法是要使用的算法。

  • If the angles are not close to normalized, then the floor() algorithm is the one to use.
  • 如果角度不接近标准化,则floor()算法是要使用的算法。

Test results:

r1 = while, r2 = fmod(), r3 = floor()

r1 = while,r2 = fmod(),r3 = floor()

Near Normal     Far From Normal
r1 0.000020     r1 0.000456
r2 0.000078     r2 0.000085
r3 0.000058     r3 0.000065
r1 0.000032     r1 0.000406
r2 0.000085     r2 0.000083
r3 0.000057     r3 0.000063
r1 0.000033     r1 0.000406
r2 0.000085     r2 0.000085
r3 0.000058     r3 0.000065
r1 0.000033     r1 0.000407
r2 0.000086     r2 0.000083
r3 0.000058     r3 0.000063

Test code:

The test code used the value shown for PI. The C standard does not define a value for π, but POSIX does define M_PI and a number of related constants, so I could have written my code using M_PI instead of PI.

测试代码使用PI显示的值。 C标准没有定义π的值,但POSIX确实定义了M_PI和许多相关的常量,所以我可以使用M_PI而不是PI编写我的代码。

#include <math.h>
#include <stdio.h>
#include "timer.h"

static const double PI = 3.14159265358979323844;

static double r1(double angle)
{
    while (angle > 2.0 * PI)
        angle -= 2.0 * PI;
    while (angle < 0)
        angle += 2.0 * PI;
    return angle;
}

static double r2(double angle)
{
    angle = fmod(angle, 2.0 * PI);
    if (angle < 0.0)
        angle += 2.0 * PI;
    return angle;
}

static double r3(double angle)
{
    double twoPi = 2.0 * PI;
    return angle - twoPi * floor( angle / twoPi );
}

static void tester(const char * tag, double (*test)(double), int noisy)
{
    typedef struct TestSet { double start, end, increment; } TestSet;
    static const TestSet tests[] =
    {
        {   -6.0 * PI,   +6.0 * PI, 0.01 },
    //  { -600.0 * PI, +600.0 * PI, 3.00 },
    };
    enum { NUM_TESTS = sizeof(tests) / sizeof(tests[0]) };
    Clock clk;
    clk_init(&clk);
    clk_start(&clk);
    for (int i = 0; i < NUM_TESTS; i++)
    {
        for (double angle = tests[i].start; angle < tests[i].end; angle += tests[i].increment)
        {
            double result = (*test)(angle);
            if (noisy)
                printf("%12.8f : %12.8f\n", angle, result);
        }
    }
    clk_stop(&clk);
    char buffer[32];
    printf("%s %s\n", tag, clk_elapsed_us(&clk, buffer, sizeof(buffer)));
}

int main(void)
{
    tester("r1", r1, 0);
    tester("r2", r2, 0);
    tester("r3", r3, 0);
    tester("r1", r1, 0);
    tester("r2", r2, 0);
    tester("r3", r3, 0);
    tester("r1", r1, 0);
    tester("r2", r2, 0);
    tester("r3", r3, 0);
    tester("r1", r1, 0);
    tester("r2", r2, 0);
    tester("r3", r3, 0);
    return(0);
}

Testing on Mac OS X 10.7.4 with the standard /usr/bin/gcc (i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (Based on Apple Inc. build 5658) (LLVM build 2336.9.00)). The 'close to normalized' test code is shown; the 'far from normalized' test data was created by uncommenting the // comment in the test data.

在Mac OS X 10.7.4上使用标准/ usr / bin / gcc进行测试(i686-apple-darwin11-llvm-gcc-4.2(GCC)4.2.1(基于Apple Inc. build 5658)(LLVM build 2336.9.00) ))。显示“接近标准化”的测试代码;通过取消注释测试数据中的//注释来创建“远离标准化”的测试数据。

Timing with a home-built GCC 4.7.1 is similar (the same conclusions would be drawn):

使用自制GCC 4.7.1的时间安排类似(将得出相同的结论):

Near Normal     Far From Normal
r1 0.000029     r1 0.000321
r2 0.000075     r2 0.000094
r3 0.000054     r3 0.000065
r1 0.000028     r1 0.000327
r2 0.000075     r2 0.000096
r3 0.000053     r3 0.000068
r1 0.000025     r1 0.000327
r2 0.000075     r2 0.000101
r3 0.000053     r3 0.000070
r1 0.000028     r1 0.000332
r2 0.000076     r2 0.000099
r3 0.000050     r3 0.000065

#4


4  

You can use something like this:

你可以使用这样的东西:

while (angle > 2pi)
    angle -= 2pi;

while (angle < 0)
    angle += 2pi;

Basically, you have to change the angle by 2pi until you are assured that it doesn't exceed 2pi or fall below 0.

基本上,你必须将角度改变2pi,直到你确信它不超过2pi或低于0。

#5


1  

Simple trick: Just add an offset, which must be a multiple of 2pi, to bring the result into a positive range prior to doing the fmod(). The fmod() will bring it back into the range [0, 2pi) automagically. This works as long as you know a priori the range of possible inputs you might get (which you often do). The larger the offset you apply, the more bits of FP precision you loose, so you probably don't want to add, say, 20000pi, although that would certainly be "safer" in terms of handling very large out-of-bounds inputs. Presuming no one would ever pass input angles that sum outside the rather crazy range [-8pi, +inf), we'll just add 8pi before fmod()ing.

简单的技巧:只需添加一个偏移量,它必须是2pi的倍数,以便在执行fmod()之前将结果置于正范围内。 fmod()会自动将其恢复到[0,2pi]范围内。只要您事先了解可能获得的输入范围(您经常这样做),这种方法就有效。你应用的偏移越大,你松散的FP精度就越多,所以你可能不想添加20000pi,尽管在处理非常大的越界输入方面肯定会“更安全” 。假设没有人会传递超出相当疯狂范围[-8pi,+ inf]之外的输入角度,我们只需在fmod()之前添加8pi。

double add_angles(float a, float b)
{
    ASSERT(a + b >= -8.0f*PI);
    return fmod(a + b + 8.0f*PI, 2.0f*PI);
}

#6


0  

Likewise if you want to be in the range -2pi to 2pi then fmod works great

同样,如果你想在-2pi到2pi的范围内,那么fmod效果很好