Все още съм доста нов в използването на SSE и се опитвам да внедря модул от 2*Pi
за входове с двойна точност от порядъка 1e8
(резултатът от който ще бъде въведен в някои векторизирани тригонометрични изчисления).
Сегашният ми опит за кода се основава на идеята, че mod(x, 2*Pi) = x - floor(x/(2*Pi))*2*Pi
и изглежда така:
#define _PD_CONST(Name, Val) \
static const double _pd_##Name[2] __attribute__((aligned(16))) = { Val, Val }
_PD_CONST(2Pi, 6.283185307179586); /* = 2*pi */
_PD_CONST(recip_2Pi, 0.159154943091895); /* = 1/(2*pi) */
void vec_mod_2pi(const double * vec, int Size, double * modAns)
{
__m128d sse_a, sse_b, sse_c;
int i;
int k = 0;
double t = 0;
unsigned int initial_mode;
initial_mode = _MM_GET_ROUNDING_MODE();
_MM_SET_ROUNDING_MODE(_MM_ROUND_DOWN);
for (i = 0; i < Size; i += 2)
{
sse_a = _mm_loadu_pd(vec+i);
sse_b = _mm_mul_pd( _mm_cvtepi32_pd( _mm_cvtpd_epi32( _mm_mul_pd(sse_a, *(__m128d*)_pd_recip_2Pi) ) ), *(__m128d*)_pd_2Pi);
sse_c = _mm_sub_pd(sse_a, sse_b);
_mm_storeu_pd(modAns+i,sse_c);
}
k = i-2;
for (i = 0; i < Size%2; i++)
{
t = (double)((int)(vec[k+i] * 0.159154943091895)) * 6.283185307179586;
modAns[k+i] = vec[k+i] - t;
}
_MM_SET_ROUNDING_MODE(initial_mode);
}
За съжаление, това в момента връща много NaN
с няколко отговора от 1.128e119
също (донякъде извън диапазона от 0
-> 2*Pi
, към който се стремях!). Подозирам, че там, където греша, е преобразуването от двойно към int-към-double, което се опитвам да използвам, за да направя floor
.
Може ли някой да ми предложи къде съм сбъркал и как да го подобря?
P.S. съжалявам за формата на този код, за първи път публикувам въпрос тук и не мога да го накарам да ми даде празни редове в кодовия блок, за да го направя четим.
_mm_set1_pd(6.283185307179586)
като нормален човек; компилаторът вече е достатъчно интелигентен, за да превърне това в еквивалент наstatic const double[2]
, но с елиминиране на дублиране (като за дублиращи се низови литерали). Второ, можете да използвате_mm_cvttpd_epi32
(обърнете внимание на допълнителното t за отрязване), за да конвертирате в цяло число с отрязване вместо текущия режим на закръгляване. Ако вашите числа са положителни, това е същото катоfloor
. Ако имате SSE4.1, можете да използвате_mm_floor_pd
(roundpd
), за да закръглите директно до цяло число, вместо да конвертирате в int32_t със знак и обратно. - person Peter Cordes   schedule 29.04.2018-2^31..2^31-1
ще ви даде0x80000000
(т.е. -2^31, най-отрицателното цяло число), което Intel използва като целочислена неопределена стойност. Конвертирането обратно вdouble
трябва да успее. Това може да са вашите1.128e119
резултати? Очаква се да получите катастрофални грешки при анулиране, особено след като вашето2Pi * recip_2Pi
вероятно не е точно 1.0, защото грешките при закръгляването ще бъдат различни. (Всъщност1.0- 2Pi*recip_2Pi
сgcc -O0
е само ~2,1E-15, така че това е няколко порядъка по-малко от вашите входове.) - person Peter Cordes   schedule 29.04.2018