在我的程序中,Angular 是用0到2pi来表示的。我想知道一种方法,如果两个Angular 相加的结果大于2pi,那么它会绕2pi到0。或者,如果我从一个Angular 减去一个Angular ,而结果小于0,那么它会绕2pi。有没有办法做到这一点?谢谢.
li9yvcax1#
你要找的是模数。fmod函数不会工作,因为它计算余数而不是算术模数。类似这样的东西应该可以工作:
inline double wrapAngle( double angle ) { double twoPi = 2.0 * 3.141592865358979; return angle - twoPi * floor( angle / twoPi ); }
字符串编辑:余数通常被定义为长除法后的余数(例如,18/4的余数是2,因为18 = 4 * 4 + 2)。当你有负数时,这会变得很麻烦。找到有符号除法的余数的常用方法是让余数的符号与结果相同。(例如,-18/4的余数是-2,因为**-18 = -4 * 4 + -2**)。x模y的定义是在方程x=y*c+m中m的最小正值,给定c是整数。因此18 mod 4将是2(其中c=4),然而**-18 mod 4**也将是2(其中c=-5)。
x mod y的最简单计算是x-y*floor(x/y),其中floor是小于或等于输入的最大整数。
lokaqttq2#
出于好奇,我在其他答案中尝试了三种算法,并对它们进行了计时。当要归一化的值接近0..2π时,while算法最快;使用fmod()的算法最慢,使用floor()的算法介于两者之间。当要归一化的值不接近0..2π时,则while算法最慢,使用floor()的算法最快,使用fmod()的算法介于两者之间。所以,我的结论是:
while
fmod()
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
字符串
测试代码使用了PI的值。C标准没有定义π的值,但是POSIX定义了M_PI和一些相关的常数,所以我可以使用M_PI而不是PI来编写代码。
PI
M_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); }
型使用标准/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))在Mac OS X 10.7.4上进行测试。显示了“接近标准化”的测试代码;“远未标准化”的测试数据是通过取消注解测试数据中的//注解而创建的。自制GCC 4.7.1的时间是类似的(可以得出相同的结论):
/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)
//
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
型
doinxwow3#
你可以使用这样的东西:
while (angle > 2pi) angle -= 2pi; while (angle < 0) angle += 2pi;
字符串基本上,你必须改变Angular 2 π,直到你确信它不超过2 π或低于0。
jogvjijk4#
根据你的用例,如果你可以选择自己的Angular 表示,那么会有一些有效的特殊情况(注意,将0作为下限已经是一个考虑到效率的特殊情况)。
如果你能够将Angular 表示为[0和1)之间的值,而不是[0和2π),那么你只需要取小数部分:
float wrap(float angle) { // wrap between [0 and 2*PI) return angle - floor(angle); }
字符串负Angular 正好。您还可以规格化, Package ,然后缩放回弧度,但会损失一些精度和效率。这在类似于许多着色器代码的代码中非常有用,特别是在“一切都是单位向量”的环境中。
如果你能够将Angular 表示为[0和2^n)之间的值,而不是[0和2π),那么你可以用按位与操作将它们包裹在2的幂内:
unsigned int wrap(unsigned int angle) { // wrap between [0 and 65,535) return angle & 0xffff; }
型更好的是,如果你可以选择一个2的幂,它等于一个整数类型的大小,数字就可以自然地换行。一个uint16_t总是在[0和2^16)之间换行,而一个uint32_t总是在[0和2^32)之间换行。65000个标题对任何人来说都应该足够了,对吗?(-:我在8位时代的游戏和演示中使用了它,甚至在3D显卡之前的纹理Map中也使用了它。我想它在模拟器和复古游戏的代码中仍然有用,但也许甚至在微型微控制器上也有用?
uint16_t
uint32_t
zzlelutf5#
简单的技巧:在执行fmod()之前,只需添加一个偏移量,该偏移量必须是2 pi的multiple,以将结果带入正范围。fmod()将自动将其带回范围[0,2 pi)。只要您事先知道可能获得的可能输入范围,就可以使用此方法(你经常这样做)。你应用的偏移量越大,你失去的FP精度就越多,所以你可能不想增加,比如说,20000 pi,尽管在处理非常大的越界输入方面,这肯定会“更安全”。假设没有人会传递总和在相当疯狂的范围[-8pi,+inf)之外的输入Angular ,我们将在fmod()ing之前添加8 pi。
multiple
double add_angles(float a, float b) { ASSERT(a + b >= -8.0f*PI); return fmod(a + b + 8.0f*PI, 2.0f*PI); }
cwtwac6a6#
同样,如果你想在-2pi到2 pi的范围内,那么fmod工作得很好。
3xiyfsfu7#
这样做很好,也很快:
#ifndef M_PI #define M_PI 3.14159265358979323846 #endif // !M_PI #ifndef M_2PI #define M_2PI (2.0 * M_PI) #endif // !M_2PI #define to0_2pi(x) ((x) - ((int)((x) / (M_2PI)) - signbit(x)) * (M_2PI))
7条答案
按热度按时间li9yvcax1#
你要找的是模数。fmod函数不会工作,因为它计算余数而不是算术模数。类似这样的东西应该可以工作:
字符串
编辑:
余数通常被定义为长除法后的余数(例如,18/4的余数是2,因为18 = 4 * 4 + 2)。当你有负数时,这会变得很麻烦。找到有符号除法的余数的常用方法是让余数的符号与结果相同。(例如,-18/4的余数是-2,因为**-18 = -4 * 4 + -2**)。
x模y的定义是在方程x=y*c+m中m的最小正值,给定c是整数。因此18 mod 4将是2(其中c=4),然而**-18 mod 4**也将是2(其中c=-5)。
x mod y的最简单计算是x-y*floor(x/y),其中floor是小于或等于输入的最大整数。
lokaqttq2#
出于好奇,我在其他答案中尝试了三种算法,并对它们进行了计时。
当要归一化的值接近0..2π时,
while
算法最快;使用fmod()
的算法最慢,使用floor()
的算法介于两者之间。当要归一化的值不接近0..2π时,则
while
算法最慢,使用floor()
的算法最快,使用fmod()
的算法介于两者之间。所以,我的结论是:
while
算法。floor()
算法。测试结果:
r1 =
while
,r2 =fmod()
,r3 =floor()
字符串
测试代码:
测试代码使用了
PI
的值。C标准没有定义π的值,但是POSIX定义了M_PI
和一些相关的常数,所以我可以使用M_PI
而不是PI
来编写代码。型
使用标准
/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)
)在Mac OS X 10.7.4上进行测试。显示了“接近标准化”的测试代码;“远未标准化”的测试数据是通过取消注解测试数据中的//
注解而创建的。自制GCC 4.7.1的时间是类似的(可以得出相同的结论):
型
doinxwow3#
你可以使用这样的东西:
字符串
基本上,你必须改变Angular 2 π,直到你确信它不超过2 π或低于0。
jogvjijk4#
根据你的用例,如果你可以选择自己的Angular 表示,那么会有一些有效的特殊情况(注意,将0作为下限已经是一个考虑到效率的特殊情况)。
将Angular 表示为单位向量
如果你能够将Angular 表示为[0和1)之间的值,而不是[0和2π),那么你只需要取小数部分:
字符串
负Angular 正好。
您还可以规格化, Package ,然后缩放回弧度,但会损失一些精度和效率。
这在类似于许多着色器代码的代码中非常有用,特别是在“一切都是单位向量”的环境中。
将Angular 表示为限制在2的幂的无符号整数
如果你能够将Angular 表示为[0和2^n)之间的值,而不是[0和2π),那么你可以用按位与操作将它们包裹在2的幂内:
型
更好的是,如果你可以选择一个2的幂,它等于一个整数类型的大小,数字就可以自然地换行。一个
uint16_t
总是在[0和2^16)之间换行,而一个uint32_t
总是在[0和2^32)之间换行。65000个标题对任何人来说都应该足够了,对吗?(-:我在8位时代的游戏和演示中使用了它,甚至在3D显卡之前的纹理Map中也使用了它。我想它在模拟器和复古游戏的代码中仍然有用,但也许甚至在微型微控制器上也有用?
zzlelutf5#
简单的技巧:在执行fmod()之前,只需添加一个偏移量,该偏移量必须是2 pi的
multiple
,以将结果带入正范围。fmod()将自动将其带回范围[0,2 pi)。只要您事先知道可能获得的可能输入范围,就可以使用此方法(你经常这样做)。你应用的偏移量越大,你失去的FP精度就越多,所以你可能不想增加,比如说,20000 pi,尽管在处理非常大的越界输入方面,这肯定会“更安全”。假设没有人会传递总和在相当疯狂的范围[-8pi,+inf)之外的输入Angular ,我们将在fmod()ing之前添加8 pi。字符串
cwtwac6a6#
同样,如果你想在-2pi到2 pi的范围内,那么fmod工作得很好。
3xiyfsfu7#
这样做很好,也很快:
字符串