C++ fpu中的小数值计算错误?
嗨,我想用fpu做这个公式C++ fpu中的小数值计算错误?,c++,assembly,x86,fpu,x87,C++,Assembly,X86,Fpu,X87,嗨,我想用fpu做这个公式 (y = v*t*sin(a) - 0.5*g*t^2) 我的C++代码是: typedef void(*Ipa_algorithm2)(double t, double alfa, double *return_value); Ipa_algorithm2 count_y; count_y = (Ipa_algorithm2)GetProcAddress(hInstLibrary, "ipa_algorithm"); t = t + 0.01; //going
(y = v*t*sin(a) - 0.5*g*t^2)
<>我的C++代码是:
typedef void(*Ipa_algorithm2)(double t, double alfa, double *return_value);
Ipa_algorithm2 count_y;
count_y = (Ipa_algorithm2)GetProcAddress(hInstLibrary, "ipa_algorithm");
t = t + 0.01; //going from cas = 0
(*count_y)(t,camera.angleY, &y); //t = cas;
我在asm中的代码是:
section .data
help_var dq 0
speed dq 40.0 ;v = rychlost
number dq 180.0
grav dq 4.906865 ;grav= 0,5*g
ipa_algorithm2:
push ebp
mov ebp, esp
finit
fld qword [speed]
fld qword [ebp+8]
fmul st1
fstp qword [help_var] ;v pomocny je v*t
fldpi
fld qword [ebp+16] ;na st0 je uhel a na st1 3,14
fmul st1 ;na st0 je uhel * 3,14
fld qword [number]
fxch st1 ;na st0 je uhel*3,14 na st1 je 180
fdiv st1 ;na st0 je uhel v radianech
fsin
fld qword [help_var]
fmul st1 ;na st0 je v*t*sin uhlu
fst qword [help_var]
finit
fld qword [ebp+8]
fld qword [ebp+8]
fmul st1
fld qword [grav]
fmul st1
fld qword [help_var]
fxch st1
fsub st1
mov eax,[ebp+24]
fstp qword [eax]
mov esp, ebp
pop ebp
ret 0
问题是,函数ipa_algorithm2从一开始就给了我正确的数字(与用C语言执行相同操作的程序的输出相比),但经过几个步骤后,结果开始变得越来越差。我花了3个小时检查代码,没有发现任何错误。我正在计算的数字可能太小以至于fpu无法计算吗?更新:,您在整个输入范围内都得到了错误的数字,因此您可能只是在实现公式时有一个常规错误,而不是FP特定的舍入错误或数值精度/稳定性类型的问题。在调试器中单步执行函数,查找给出错误答案的输入,并查看寄存器值
或者更好的是,用标量AVX指令重写它,因为标量AVX比x87更容易,所以工作标量实现是更好的起点。对于sin()
,调用向量化的sin()
实现,或者让gcc使用-O3-ffast math
自动向量化函数。
(请参阅:glibc具有矢量化的数学库函数。)
如果您最终想要快速运行,那么使用slowfsin
指令从标量x87实现开始可能是最不有用的起点。对于您甚至不打算使用的指令集,好的干净的C语言比草率的asm实现要好。(对于最终的优化版本,在大多数情况下,带有intrinsic的C比手写asm更有意义)。请参阅,以及中的其他链接
在游戏中存储角度 将方向存储为
[x,y]
矢量,而不是以弧度表示的角度。(或学位)。使用标准化的xy
矢量,将两个角度相加将成为2x2矩阵乘法(通过旋转矩阵)。但是sin
变得微不足道:如果保持向量的规格化(x^2+y^2=1.0
),那么sin(角度)
=angle.y
尽可能避免使用实际角度,而是使用标准化向量。您有时需要atan2
,但通常很少使用普通库版本
如果您以数组结构格式存储xy对,它将是SIMD友好型的,并且您可以轻松使用8个浮点x
值和8个浮点匹配y
值。将方向向量压缩到单个SIMD向量中通常不是最优的;不要被“向量”这个词所愚弄
另请参见,尤其是。这将帮助您了解如何设计程序,以便以后可以在值得的地方使用SIMD对其进行优化。(在开始的时候,你不必矢量化所有的东西,但是改变你的数据布局通常是很多工作,所以首先考虑让你的数据SIMD友好。)< /P>
数值误差的可能来源(事实证明这不是真正的问题) 一个可能的原因:。大多数现代数学库都不使用
fsin
指令来计算sin
函数,因为它速度不快,而且某些输入的精度很差
此外,根据您构建代码的方式,还有一些(如MSVCRT启动,如果您在Windows上使用的是旧版本)(64位尾数)
你为什么用asm写这个?您是否需要有关如何提高效率的建议?您应该在
st0
中返回float
作为返回值,而不是通过指针arg存储。另外,不要使用finit
。我认为您这样做只是因为在加载内容之后,您没有平衡x87堆栈与POP,因此在重复调用之后,您可能会从x87堆栈溢出中得到NAN。在返回void
的函数中,您仍然返回非空的x87堆栈,因此您仍然做得不对,可能会中断调用方
使用fstp
或fmulp
使堆栈保持平衡。使用fld st0
代替另一个加载。使用fmul qword[grav_zrychleni]
代替单独的fld
或者更好,使用SSE2或AVX进行标量双精度数学。除非您真的想要80位长双精度更新:,否则您会在整个输入范围内得到错误的数字,因此您可能只是在实现公式时有一个常规错误,而不是FP特定的舍入错误或数值精度/稳定性类型的问题。在调试器中单步执行函数,查找给出错误答案的输入,并查看寄存器值
或者更好的是,用标量AVX指令重写它,因为标量AVX比x87更容易,所以工作标量实现是更好的起点。对于sin()
,调用向量化的sin()
实现,或者让gcc使用-O3-ffast math
自动向量化函数。
(请参阅:glibc具有矢量化的数学库函数。)
如果您最终想要快速运行,那么使用slowfsin
指令从标量x87实现开始可能是最不有用的起点。对于您甚至不打算使用的指令集,好的干净的C语言比草率的asm实现要好。(对于最终的优化版本,在大多数情况下,带有intrinsic的C比手写asm更有意义)。请参阅,以及中的其他链接
在游戏中存储角度
将方向存储为[x,y]
矢量,而不是以弧度表示的角度。(或学位)。使用标准化的xy
矢量,将两个角度相加将成为2x2矩阵乘法(通过旋转矩阵)。但是sin
变得微不足道:如果保持向量的规格化(x^2+y^2=1.0
),那么sin(角度)
=angle.y