Как говорится в комментариях, похоже, что в этом вы можете сделать это просто замаскированным вычитанием со сравнением + andpd
.Это работает до тех пор, пока вы никогда не сможете отойти от желаемого диапазона более чем на одно вычитание.
Как
const __m128d v2pi = _mm_set1_pd(TWOPI);
__m128d needs_range_reduction = _mm_cmpge_pd(vphase, v2pi);
__m128d offset = _mm_and_pd(needs_range_reduction, v2pi); // 0.0 or 2*Pi
vphase = _mm_sub_pd(vphase, offset);
Для реализации фактического (медленного) fmod
не слишком заботясь о последних нескольких битах значения, вы должны сделать integer_quotient = floor(x/y)
(или, может быть, rint(x/y)
или ceil
), а затем x - y * integer_quotient
.floor
/ rint
/ ceil
дешевы с SSE4.1 _mm_round_pd
или _mm_floor_pd()
.Это даст вам остаток, который может быть отрицательным, как при целочисленном делении.
Я уверен, что существуют численные методы, которые лучше избегают ошибок округления, прежде чем катастрофическая отмена вычитает два соседних числа.Если вы заботитесь о точности, проверьте.(Использование double
векторов, когда вас не очень заботит точность, довольно глупо; можно также использовать float
и получать вдвое больше работы на каждый вектор).Если входное значение намного больше, чем модуль, неизбежна потеря точности, и минимизация ошибки округления во временной области, вероятно, очень важна.Но в противном случае точность будет проблемой только в том случае, если вы не заботитесь об относительной ошибке в результатах, очень близких к нулю, когда x
является почти точным кратным y
.(Результат, близкий к нулю, только немногие нижние биты значения и значения оставлены для точности.)
Без SSE4.1 существуют хитрости, такие как сложение и вычитание достаточно большого числа.Преобразование в целое число и обратно еще хуже для pd
, потому что инструкция упакованного преобразования также декодирует некоторые случайные операции.Не говоря уже о том, что 32-разрядное целое число не покрывает весь диапазон double
, но вам не хватает точности уменьшения диапазона, если ваш ввод был таким огромным.
Если у вас FMA , вы можете избежать ошибки округления в y * integer_quotient
части умножения и подпункта._mm_fmsub_pd
.