C++ 如何提高iOS上std::sin函数的精度

C++ 如何提高iOS上std::sin函数的精度,c++,ios,precision,C++,Ios,Precision,我有一个跨平台的应用程序,它是一个音频应用程序,因此经常使用正弦波,还有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;

我有一个跨平台的应用程序,它是一个音频应用程序,因此经常使用正弦波,还有
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);
   }
}
void testsinezorcrossings()
{
常数静态浮点数kTwoPi=6.28318530718f;
常量静态浮点ε=1e-5f;
对于(int ii=0;ii<10000;++ii)
{
常量浮点数差=标准值::绝对值(标准值::正弦值(kTwoPi*静态值(ii));
if(差>ε)
printf(“过零失败,差异:%f\n”,差异);
}
}
在Windows和MaxOSX上,这是通过的(即没有打印输出),但在iOS上,几乎每次迭代都会失败。事实上,只有使用
epsilon>0.004f才能成功。这在我的应用程序中会产生清晰的声音噪音


有没有办法告诉编译器使用一个更好的实现,而不是有损的实现?

我认为这个实现是非常准确的

您实际的问题是
kTwoPi*static_cast(ii)
四舍五入到下一个
float
。例如,对于
ii=10000
,该值为(如果我没有计算错误):
62831.8515625
如果在精确数学中减去
10000*2*pi
,则得到近似值:
-0.001509…
,该值的正弦值大致相同(而不是
0
)。它“相对”接近于零,但远离您所期望的
10e-6
“精度”

如果您想获得更精确的sin(x*pi)
,请查看
boost::math::sin\u pi

如果您想要更高的精度,请使用
double
long double
而不是
float

比如说,

替换

   const static float kTwoPi = 6.28318530718f;
   const static float epsilon = 10e-6f;


冒着重复的风险,您的问题显然是使用float而不是double或longduble

您可以通过执行以下操作来验证这一点

cout << kTwoPi << endl ;

在许多(大多数?)系统上。对于单个精度值,您的增量太小。浮点对于大多数应用程序来说都是无用的,因为它通常缺乏精度。

可能是CPU问题吗?x86处理器使用80位浮点,谷歌快速搜索表明ARM只有64位?请参阅GCC选项,其中大多数选项可能在旁注中提供,编写
10e-6
的“正常”方式是
1e-5
@SidS——工程符号有时使用10的幂,即3的倍数。这样,它们就与相应的前缀kilo-、mega-、mili-、micro-等对齐了。@PeteBecker,我知道这是多么有用。我发现我指的是所谓的。谢谢你的回答。问题不在于双精度当然比浮点更精确,而在于在某些CPU(即我的Windows one)上,它的性能与我的iOS one不同。它不一定基于CPU。我为Win32编译了您的代码,并在64位Windows 10上运行它,导致了与您在iOS上提到的问题类似的问题。当您在Windows上运行代码时,代码似乎可以工作的原因很可能是,当您编译64位
浮点值
双精度
时,它们是相同的。因此,使用
double
而不是
float
在代码工作的平台上没有额外的成本,但它确实解决了那些不工作的平台的问题。因此,使用
double
而不是
float
在代码工作的平台上没有额外的成本,但它确实解决了那些不起作用的平台的问题。我认为你应该重新考虑谁给了你最好的答案。是的,当使用x87数学(这是x86-32位的默认值)时,所有浮点变量通常以扩展精度(即64位尾数)计算,至少只要它们在FPU堆栈上。在x86-64位上,所有浮点操作(默认情况下)都使用SSE寄存器,因此单精度浮点实际上被舍入为23位尾数。如果(问题所暗示的)实际目标是计算
sin(x*twopi)
,那么诀窍是在计算最终结果之前将
x
减少到
[-0.5,0.5]
范围(这就是
sin\u pi
的基本功能)。“Float对于大多数应用程序来说是无用的[…]”是一个相当大胆的说法。当然,我们需要更加注意避免舍入问题,但在许多情况下,使用
double
只会隐藏使用条件恶劣的算法的问题。使用SIMD时,单精度与双精度的吞吐量是原来的两倍。在大多数GPU上,增益甚至更高(一些较旧的GPU甚至不支持双精度数学)。
const float difference = std::abs(std::sin(kTwoPi * static_cast<float>(ii)));
const double difference = std::abs(std::sin(kTwoPi * ii));
cout << kTwoPi << endl ;
const static float kTwoPi = 6.283185 ;