嗨,我想在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 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不能用它们计算?
答案 0 :(得分:3)
更新:according to a comment,您输入的所有输入数据都是错误的,因此您可能只是在执行公式时遇到常规错误,而不是FP特定的舍入错误或数值准确性/稳定型问题。在调试器中将您的函数单步执行,以获得给出错误答案的输入,并查看寄存器值。
或者更好的是,使用标量AVX指令重写它,因为标量AVX比x87和you eventually want a vectorized AVX implementation anyway更容易,因此工作标量实现是一个更好的起点。对于sin()
,请调用矢量化sin()
实现,或者让gcc使用-O3 -ffast-math
自动向量化您的函数。
(参见https://sourceware.org/glibc/wiki/libmvec:glibc具有矢量化数学库函数。)
如果您最终想要快速运行的东西,那么使用慢速fsin
指令从标量x87实现开始可能是最不实用的起点。对于你甚至不会使用的指令集,良好的干净C会比草率的asm实现更好。 (对于最终的优化版本,在大多数情况下,带有内在函数的C比手写的asm更有意义)。请参阅http://agner.org/optimize/中的x86 tag wiki和其他链接。
将方向存储为[x,y]
个向量,而不是以弧度为单位的角度。 (或学位)。使用归一化的xy
向量,添加两个角度成为2x2矩阵乘法(通过旋转矩阵)。但是sin
变得微不足道:如果你将矢量标准化(x^2 + y^2 = 1.0
),那么sin(angle)
= angle.y
。
尽可能避免使用实际角度,而是使用标准化向量。您有时需要atan2
,但通常很少,您只能使用普通库版本。
如果以数组结构格式存储xy对,它将是SIMD友好的,并且您可以轻松地执行8个浮点x
值和8个浮点匹配y
值的内容。使用打包到单个SIMD向量中的方向向量进行操作通常 NOT 最优;不要被“#34; vector"”这个词所迷惑。
另请参阅 https://stackoverflow.com/tags/sse/info,尤其是SIMD at Insomniac Games (GDC 2015)。这将有助于您了解如何设计程序,以便以后可以在有价值的地方使用SIMD对其进行优化。 (您不必 在开始时对所有内容进行矢量化,但更改数据布局通常需要做很多工作,因此请考虑首先使数据与SIMD保持友好关系。)
数字错误的可能来源(原来这不是真正的问题)
一个可能的原因:The worst-case error for the fsin
instruction for small inputs is actually about 1.37 quintillion units in the last place, leaving fewer than four bits correct.。大多数现代数学图书馆都没有使用fsin
指令来计算sin
函数,因为它不快并且对某些输入的准确性较差。
此外,根据您构建代码的方式,某些内容(如MSVCRT启动,如果您使用的是Windows并使用旧版本)may have set the x87 FPU to less than 80-bit precision(64位尾数)。
你为什么要在asm中写这个?您是否想要如何提高效率的建议?您应该在float
中返回st0
作为返回值,而不是通过指针arg存储。另外,请勿使用finit
。我认为你只是这样做,因为你在加载东西后没有平衡x87堆栈和pop,所以在反复调用之后你会从x87堆栈溢出中获得NaNs。您仍然在返回void
的函数中返回x87堆栈非空,因此您仍然做错了并且可能会破坏调用者。
使用fstp
或fmulp
让堆栈保持平衡。使用fld st0
代替其他负载。使用fmul qword [grav_zrychleni]
代替单独的fld
。
或者更好,使用SSE2或AVX进行标量双精度数学运算。除非你真的想要80位long double
。