我有一个跨平台的应用程序,这是一个音频应用程序,因此使用了很多正弦波以及std::sin()
和其他测角功能。
我注意到,特别是在iOS平台上,std::sin()
的精度非常差。我写了以下测试:
void TestSineZeroCrossings()
{
const static float kTwoPi = 6.28318530718f;
const static float epsilon = 1e-5f;
for (int ii = 0; ii < 10000; ++ii)
{
const float difference = std::abs(std::sin(kTwoPi * static_cast<float>(ii)));
if (difference > epsilon)
printf("Zero crossing fail, difference: %f\n", difference);
}
}
在Windows和MaxOSX上通过(即没有打印输出),但是在iOS上几乎每次迭代都失败。实际上,只有使用epsilon > 0.004f
才能成功。这会在我的应用程序中产生明显的可听噪声。
有没有办法告诉编译器使用更好的实现,而不是有损的?
答案 0 :(得分:2)
我认为实施非常准确。
您的实际问题是kTwoPi * static_cast<float>(ii)
被舍入到下一个float
。例如,对于ii=10000
,该值为(如果我没有误算的话):62831.8515625
如果从精确的数学运算中减去10000*2*pi
,您将得到大约:-0.001509...
并且该值的正弦近似相同(而不是0
)。它“相对”接近零,但与您想要的10e-6
“准确性”相距甚远。
如果您想获得sin(x*pi)
的更准确值,请查看boost::math::sin_pi
:
https://www.boost.org/doc/libs/1_69_0/libs/math/doc/html/math_toolkit/powers/sin_pi.html
答案 1 :(得分:0)
如果要提高精度,请使用double
或long double
而不是float
。
例如,
替换
const static float kTwoPi = 6.28318530718f;
const static float epsilon = 10e-6f;
使用
const static double kTwoPi = 6.28318530718;
const static double epsilon = 10e-6;
和
const float difference = std::abs(std::sin(kTwoPi * static_cast<float>(ii)));
使用
const double difference = std::abs(std::sin(kTwoPi * ii));
答案 2 :(得分:0)
冒着重复的危险,您的问题显然是使用float而不是double或long double。
您可以通过
进行验证cout << kTwoPi << endl ;
并查看打印出多少位数字,以及它们与原始值的比较。
const静态浮点数kTwoPi = 6.28318530718f;
大致等于
const static float kTwoPi = 6.283185 ;
在许多(大多数?)系统上对于单个精度值,您的增量太小了。浮点数由于通常缺乏精度而对大多数应用程序无用。