Пересобранный и видоизмененный conky для ОСГОС
This commit is contained in:
commit
3a48cf8c86
738 changed files with 217718 additions and 0 deletions
544
obj-x86_64-linux-gnu/3rdparty/Vc/avx_sorthelper_AVX.cpp
vendored
Normal file
544
obj-x86_64-linux-gnu/3rdparty/Vc/avx_sorthelper_AVX.cpp
vendored
Normal file
|
|
@ -0,0 +1,544 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2011-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
#include <Vc/avx/vector.h>
|
||||
#include <Vc/avx/debug.h>
|
||||
#include <Vc/avx/macros.h>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
#ifdef Vc_IMPL_AVX2
|
||||
template <>
|
||||
Vc_CONST AVX2::short_v sorted<CurrentImplementation::current()>(AVX2::short_v x_)
|
||||
{
|
||||
// ab cd ef gh ij kl mn op
|
||||
// ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑
|
||||
// ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮
|
||||
// ⎮ ╳ ⎮ ⎮ ╳ ⎮ ⎮ ╳ ⎮ ⎮ ╳ ⎮
|
||||
// ⎮⎛ ⎞⎮ ⎮⎛ ⎞⎮ ⎮⎛ ⎞⎮ ⎮⎛ ⎞⎮
|
||||
// <> <> <> <> <> <> <> <>
|
||||
// ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑
|
||||
// ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮
|
||||
// ⎮ o ⎮ ⎮ o ⎮ ⎮ o ⎮ ⎮ o ⎮
|
||||
// ⎮↓ ↑⎮ ⎮↓ ↑⎮ ⎮↓ ↑⎮ ⎮↓ ↑⎮
|
||||
// 01 23 01 23 01 23 01 23
|
||||
// ⎮⎮ ⎮⎮ ╳ ⎮⎮ ⎮⎮ ╳
|
||||
// 01 23 32 10 01 23 32 10
|
||||
// ⎮⎝ ⎮⎝ ⎠⎮ ⎠⎮ ⎮⎝ ⎮⎝ ⎠⎮ ⎠⎮
|
||||
// ⎮ ╲⎮ ╳ ⎮╱ ⎮ ⎮ ╲⎮ ╳ ⎮╱ ⎮
|
||||
// ⎮ ╲╱ ╲╱ ⎮ ⎮ ╲╱ ╲╱ ⎮
|
||||
// ⎮ ╱╲ ╱╲ ⎮ ⎮ ╱╲ ╱╲ ⎮
|
||||
// ⎮ ╱⎮ ╳ ⎮╲ ⎮ ⎮ ╱⎮ ╳ ⎮╲ ⎮
|
||||
// ⎮⎛ ⎮⎛ ⎞⎮ ⎞⎮ ⎮⎛ ⎮⎛ ⎞⎮ ⎞⎮
|
||||
// <> <> <> <> <> <> <> <>
|
||||
// ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑
|
||||
// ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮
|
||||
// ⎮ ╳ ⎮ ⎮ ╳ ⎮ ⎮ ╳ ⎮ ⎮ ╳ ⎮
|
||||
// ⎮⎛ ⎞⎮ ⎮⎛ ⎞⎮ ⎮⎛ ⎞⎮ ⎮⎛ ⎞⎮
|
||||
// <> <> <> <> <> <> <> <>
|
||||
// ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑
|
||||
// ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮
|
||||
// ⎮ o ⎮ ⎮ o ⎮ ⎮ o ⎮ ⎮ o ⎮
|
||||
// ⎮↓ ↑⎮ ⎮↓ ↑⎮ ⎮↓ ↑⎮ ⎮↓ ↑⎮
|
||||
// 01 23 01 23 01 23 01 23
|
||||
|
||||
// sort pairs (one min/max)
|
||||
auto x = AVX::lo128(x_.data());
|
||||
auto y = AVX::hi128(x_.data());
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
auto l = _mm_min_epi16(x, y);
|
||||
auto h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
|
||||
// merge left & right quads (two min/max)
|
||||
x = _mm_unpacklo_epi16(l, h);
|
||||
y = _mm_unpackhi_epi16(h, l);
|
||||
// Vc_DEBUG << "8x2 sorted xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::permuteLo<X1, X0, X3, X2>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(l, h));
|
||||
y = Mem::permuteHi<X5, X4, X7, X6>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(h, l));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
|
||||
// merge quads into octs (three min/max)
|
||||
x = _mm_unpacklo_epi16(h, l);
|
||||
y = _mm_unpackhi_epi16(l, h);
|
||||
// Vc_DEBUG << "4x4 sorted xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::permuteLo<X2, X3, X0, X1>(Mem::blend<X0, X1, Y2, Y3, X4, X5, Y6, Y7>(h, l));
|
||||
y = Mem::permuteHi<X6, X7, X4, X5>(Mem::blend<X0, X1, Y2, Y3, X4, X5, Y6, Y7>(l, h));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::permuteHi<X5, X4, X7, X6>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(l, h));
|
||||
y = Mem::permuteLo<X1, X0, X3, X2>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(h, l));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h) << " done?";
|
||||
|
||||
// merge octs into hexa (four min/max)
|
||||
x = _mm_unpacklo_epi16(l, h);
|
||||
y = _mm_unpackhi_epi16(h, l);
|
||||
// Vc_DEBUG << "2x8 sorted xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = _mm_unpacklo_epi64(l, h);
|
||||
y = _mm_unpackhi_epi64(l, h);
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = _mm_castps_si128(Mem::permute<X1, X0, X3, X2>(Mem::blend<X0, Y1, X2, Y3>(_mm_castsi128_ps(h), _mm_castsi128_ps(l))));
|
||||
y = _mm_castps_si128(Mem::blend<X0, Y1, X2, Y3>(_mm_castsi128_ps(l), _mm_castsi128_ps(h)));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(l, h);
|
||||
y = Mem::permuteLo<X1, X0, X3, X2>(
|
||||
Mem::permuteHi<X5, X4, X7, X6>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(h, l)));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = _mm_unpacklo_epi16(l, h);
|
||||
y = _mm_unpackhi_epi16(l, h);
|
||||
return AVX::concat(x, y);
|
||||
}
|
||||
|
||||
template <>
|
||||
Vc_CONST AVX2::ushort_v sorted<CurrentImplementation::current()>(AVX2::ushort_v x_)
|
||||
{
|
||||
// sort pairs (one min/max)
|
||||
auto x = AVX::lo128(x_.data());
|
||||
auto y = AVX::hi128(x_.data());
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
auto l = _mm_min_epu16(x, y);
|
||||
auto h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
|
||||
// merge left & right quads (two min/max)
|
||||
x = _mm_unpacklo_epi16(l, h);
|
||||
y = _mm_unpackhi_epi16(h, l);
|
||||
// Vc_DEBUG << "8x2 sorted xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::permuteLo<X1, X0, X3, X2>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(l, h));
|
||||
y = Mem::permuteHi<X5, X4, X7, X6>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(h, l));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
|
||||
// merge quads into octs (three min/max)
|
||||
x = _mm_unpacklo_epi16(h, l);
|
||||
y = _mm_unpackhi_epi16(l, h);
|
||||
// Vc_DEBUG << "4x4 sorted xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::permuteLo<X2, X3, X0, X1>(Mem::blend<X0, X1, Y2, Y3, X4, X5, Y6, Y7>(h, l));
|
||||
y = Mem::permuteHi<X6, X7, X4, X5>(Mem::blend<X0, X1, Y2, Y3, X4, X5, Y6, Y7>(l, h));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::permuteHi<X5, X4, X7, X6>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(l, h));
|
||||
y = Mem::permuteLo<X1, X0, X3, X2>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(h, l));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h) << " done?";
|
||||
|
||||
// merge octs into hexa (four min/max)
|
||||
x = _mm_unpacklo_epi16(l, h);
|
||||
y = _mm_unpackhi_epi16(h, l);
|
||||
// Vc_DEBUG << "2x8 sorted xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = _mm_unpacklo_epi64(l, h);
|
||||
y = _mm_unpackhi_epi64(l, h);
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = _mm_castps_si128(Mem::permute<X1, X0, X3, X2>(Mem::blend<X0, Y1, X2, Y3>(_mm_castsi128_ps(h), _mm_castsi128_ps(l))));
|
||||
y = _mm_castps_si128(Mem::blend<X0, Y1, X2, Y3>(_mm_castsi128_ps(l), _mm_castsi128_ps(h)));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(l, h);
|
||||
y = Mem::permuteLo<X1, X0, X3, X2>(
|
||||
Mem::permuteHi<X5, X4, X7, X6>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(h, l)));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = _mm_unpacklo_epi16(l, h);
|
||||
y = _mm_unpackhi_epi16(l, h);
|
||||
return AVX::concat(x, y);
|
||||
}
|
||||
|
||||
template <> Vc_CONST AVX2::int_v sorted<CurrentImplementation::current()>(AVX2::int_v x_)
|
||||
{
|
||||
using namespace AVX;
|
||||
const __m256i hgfedcba = x_.data();
|
||||
const __m128i hgfe = hi128(hgfedcba);
|
||||
const __m128i dcba = lo128(hgfedcba);
|
||||
__m128i l = _mm_min_epi32(hgfe, dcba); // ↓hd ↓gc ↓fb ↓ea
|
||||
__m128i h = _mm_max_epi32(hgfe, dcba); // ↑hd ↑gc ↑fb ↑ea
|
||||
|
||||
__m128i x = _mm_unpacklo_epi32(l, h); // ↑fb ↓fb ↑ea ↓ea
|
||||
__m128i y = _mm_unpackhi_epi32(l, h); // ↑hd ↓hd ↑gc ↓gc
|
||||
|
||||
l = _mm_min_epi32(x, y); // ↓(↑fb,↑hd) ↓hfdb ↓(↑ea,↑gc) ↓geca
|
||||
h = _mm_max_epi32(x, y); // ↑hfdb ↑(↓fb,↓hd) ↑geca ↑(↓ea,↓gc)
|
||||
|
||||
x = _mm_min_epi32(l, Reg::permute<X2, X2, X0, X0>(h)); // 2(hfdb) 1(hfdb) 2(geca) 1(geca)
|
||||
y = _mm_max_epi32(h, Reg::permute<X3, X3, X1, X1>(l)); // 4(hfdb) 3(hfdb) 4(geca) 3(geca)
|
||||
|
||||
__m128i b = Reg::shuffle<Y0, Y1, X0, X1>(y, x); // b3 <= b2 <= b1 <= b0
|
||||
__m128i a = _mm_unpackhi_epi64(x, y); // a3 >= a2 >= a1 >= a0
|
||||
|
||||
// _mm_extract_epi32 may return an unsigned int, breaking these comparisons.
|
||||
if (Vc_IS_UNLIKELY(static_cast<int>(_mm_extract_epi32(x, 2)) >= static_cast<int>(_mm_extract_epi32(y, 1)))) {
|
||||
return concat(Reg::permute<X0, X1, X2, X3>(b), a);
|
||||
} else if (Vc_IS_UNLIKELY(static_cast<int>(_mm_extract_epi32(x, 0)) >= static_cast<int>(_mm_extract_epi32(y, 3)))) {
|
||||
return concat(a, Reg::permute<X0, X1, X2, X3>(b));
|
||||
}
|
||||
|
||||
// merge
|
||||
l = _mm_min_epi32(a, b); // ↓a3b3 ↓a2b2 ↓a1b1 ↓a0b0
|
||||
h = _mm_max_epi32(a, b); // ↑a3b3 ↑a2b2 ↑a1b1 ↑a0b0
|
||||
|
||||
a = _mm_unpacklo_epi32(l, h); // ↑a1b1 ↓a1b1 ↑a0b0 ↓a0b0
|
||||
b = _mm_unpackhi_epi32(l, h); // ↑a3b3 ↓a3b3 ↑a2b2 ↓a2b2
|
||||
l = _mm_min_epi32(a, b); // ↓(↑a1b1,↑a3b3) ↓a1b3 ↓(↑a0b0,↑a2b2) ↓a0b2
|
||||
h = _mm_max_epi32(a, b); // ↑a3b1 ↑(↓a1b1,↓a3b3) ↑a2b0 ↑(↓a0b0,↓a2b2)
|
||||
|
||||
a = _mm_unpacklo_epi32(l, h); // ↑a2b0 ↓(↑a0b0,↑a2b2) ↑(↓a0b0,↓a2b2) ↓a0b2
|
||||
b = _mm_unpackhi_epi32(l, h); // ↑a3b1 ↓(↑a1b1,↑a3b3) ↑(↓a1b1,↓a3b3) ↓a1b3
|
||||
l = _mm_min_epi32(a, b); // ↓(↑a2b0,↑a3b1) ↓(↑a0b0,↑a2b2,↑a1b1,↑a3b3) ↓(↑(↓a0b0,↓a2b2) ↑(↓a1b1,↓a3b3)) ↓a0b3
|
||||
h = _mm_max_epi32(a, b); // ↑a3b0 ↑(↓(↑a0b0,↑a2b2) ↓(↑a1b1,↑a3b3)) ↑(↓a0b0,↓a2b2,↓a1b1,↓a3b3) ↑(↓a0b2,↓a1b3)
|
||||
|
||||
return concat(_mm_unpacklo_epi32(l, h), _mm_unpackhi_epi32(l, h));
|
||||
}
|
||||
|
||||
template <>
|
||||
Vc_CONST AVX2::uint_v sorted<CurrentImplementation::current()>(AVX2::uint_v x_)
|
||||
{
|
||||
using namespace AVX;
|
||||
const __m256i hgfedcba = x_.data();
|
||||
const __m128i hgfe = hi128(hgfedcba);
|
||||
const __m128i dcba = lo128(hgfedcba);
|
||||
__m128i l = _mm_min_epu32(hgfe, dcba); // ↓hd ↓gc ↓fb ↓ea
|
||||
__m128i h = _mm_max_epu32(hgfe, dcba); // ↑hd ↑gc ↑fb ↑ea
|
||||
|
||||
__m128i x = _mm_unpacklo_epi32(l, h); // ↑fb ↓fb ↑ea ↓ea
|
||||
__m128i y = _mm_unpackhi_epi32(l, h); // ↑hd ↓hd ↑gc ↓gc
|
||||
|
||||
l = _mm_min_epu32(x, y); // ↓(↑fb,↑hd) ↓hfdb ↓(↑ea,↑gc) ↓geca
|
||||
h = _mm_max_epu32(x, y); // ↑hfdb ↑(↓fb,↓hd) ↑geca ↑(↓ea,↓gc)
|
||||
|
||||
x = _mm_min_epu32(l, Reg::permute<X2, X2, X0, X0>(h)); // 2(hfdb) 1(hfdb) 2(geca) 1(geca)
|
||||
y = _mm_max_epu32(h, Reg::permute<X3, X3, X1, X1>(l)); // 4(hfdb) 3(hfdb) 4(geca) 3(geca)
|
||||
|
||||
__m128i b = Reg::shuffle<Y0, Y1, X0, X1>(y, x); // b3 <= b2 <= b1 <= b0
|
||||
__m128i a = _mm_unpackhi_epi64(x, y); // a3 >= a2 >= a1 >= a0
|
||||
|
||||
if (Vc_IS_UNLIKELY(extract_epu32<2>(x) >= extract_epu32<1>(y))) {
|
||||
return concat(Reg::permute<X0, X1, X2, X3>(b), a);
|
||||
} else if (Vc_IS_UNLIKELY(extract_epu32<0>(x) >= extract_epu32<3>(y))) {
|
||||
return concat(a, Reg::permute<X0, X1, X2, X3>(b));
|
||||
}
|
||||
|
||||
// merge
|
||||
l = _mm_min_epu32(a, b); // ↓a3b3 ↓a2b2 ↓a1b1 ↓a0b0
|
||||
h = _mm_max_epu32(a, b); // ↑a3b3 ↑a2b2 ↑a1b1 ↑a0b0
|
||||
|
||||
a = _mm_unpacklo_epi32(l, h); // ↑a1b1 ↓a1b1 ↑a0b0 ↓a0b0
|
||||
b = _mm_unpackhi_epi32(l, h); // ↑a3b3 ↓a3b3 ↑a2b2 ↓a2b2
|
||||
l = _mm_min_epu32(a, b); // ↓(↑a1b1,↑a3b3) ↓a1b3 ↓(↑a0b0,↑a2b2) ↓a0b2
|
||||
h = _mm_max_epu32(a, b); // ↑a3b1 ↑(↓a1b1,↓a3b3) ↑a2b0 ↑(↓a0b0,↓a2b2)
|
||||
|
||||
a = _mm_unpacklo_epi32(l, h); // ↑a2b0 ↓(↑a0b0,↑a2b2) ↑(↓a0b0,↓a2b2) ↓a0b2
|
||||
b = _mm_unpackhi_epi32(l, h); // ↑a3b1 ↓(↑a1b1,↑a3b3) ↑(↓a1b1,↓a3b3) ↓a1b3
|
||||
l = _mm_min_epu32(a, b); // ↓(↑a2b0,↑a3b1) ↓(↑a0b0,↑a2b2,↑a1b1,↑a3b3) ↓(↑(↓a0b0,↓a2b2) ↑(↓a1b1,↓a3b3)) ↓a0b3
|
||||
h = _mm_max_epu32(a, b); // ↑a3b0 ↑(↓(↑a0b0,↑a2b2) ↓(↑a1b1,↑a3b3)) ↑(↓a0b0,↓a2b2,↓a1b1,↓a3b3) ↑(↓a0b2,↓a1b3)
|
||||
|
||||
return concat(_mm_unpacklo_epi32(l, h), _mm_unpackhi_epi32(l, h));
|
||||
}
|
||||
#endif // AVX2
|
||||
|
||||
template <>
|
||||
Vc_CONST AVX2::float_v sorted<CurrentImplementation::current()>(AVX2::float_v x_)
|
||||
{
|
||||
__m256 hgfedcba = x_.data();
|
||||
const __m128 hgfe = AVX::hi128(hgfedcba);
|
||||
const __m128 dcba = AVX::lo128(hgfedcba);
|
||||
__m128 l = _mm_min_ps(hgfe, dcba); // ↓hd ↓gc ↓fb ↓ea
|
||||
__m128 h = _mm_max_ps(hgfe, dcba); // ↑hd ↑gc ↑fb ↑ea
|
||||
|
||||
__m128 x = _mm_unpacklo_ps(l, h); // ↑fb ↓fb ↑ea ↓ea
|
||||
__m128 y = _mm_unpackhi_ps(l, h); // ↑hd ↓hd ↑gc ↓gc
|
||||
|
||||
l = _mm_min_ps(x, y); // ↓(↑fb,↑hd) ↓hfdb ↓(↑ea,↑gc) ↓geca
|
||||
h = _mm_max_ps(x, y); // ↑hfdb ↑(↓fb,↓hd) ↑geca ↑(↓ea,↓gc)
|
||||
|
||||
x = _mm_min_ps(l, Reg::permute<X2, X2, X0, X0>(h)); // 2(hfdb) 1(hfdb) 2(geca) 1(geca)
|
||||
y = _mm_max_ps(h, Reg::permute<X3, X3, X1, X1>(l)); // 4(hfdb) 3(hfdb) 4(geca) 3(geca)
|
||||
|
||||
__m128 a = _mm_castpd_ps(_mm_unpackhi_pd(_mm_castps_pd(x), _mm_castps_pd(y))); // a3 >= a2 >= a1 >= a0
|
||||
__m128 b = Reg::shuffle<Y0, Y1, X0, X1>(y, x); // b3 <= b2 <= b1 <= b0
|
||||
|
||||
// merge
|
||||
l = _mm_min_ps(a, b); // ↓a3b3 ↓a2b2 ↓a1b1 ↓a0b0
|
||||
h = _mm_max_ps(a, b); // ↑a3b3 ↑a2b2 ↑a1b1 ↑a0b0
|
||||
|
||||
a = _mm_unpacklo_ps(l, h); // ↑a1b1 ↓a1b1 ↑a0b0 ↓a0b0
|
||||
b = _mm_unpackhi_ps(l, h); // ↑a3b3 ↓a3b3 ↑a2b2 ↓a2b2
|
||||
l = _mm_min_ps(a, b); // ↓(↑a1b1,↑a3b3) ↓a1b3 ↓(↑a0b0,↑a2b2) ↓a0b2
|
||||
h = _mm_max_ps(a, b); // ↑a3b1 ↑(↓a1b1,↓a3b3) ↑a2b0 ↑(↓a0b0,↓a2b2)
|
||||
|
||||
a = _mm_unpacklo_ps(l, h); // ↑a2b0 ↓(↑a0b0,↑a2b2) ↑(↓a0b0,↓a2b2) ↓a0b2
|
||||
b = _mm_unpackhi_ps(l, h); // ↑a3b1 ↓(↑a1b1,↑a3b3) ↑(↓a1b1,↓a3b3) ↓a1b3
|
||||
l = _mm_min_ps(a, b); // ↓(↑a2b0,↑a3b1) ↓(↑a0b0,↑a2b2,↑a1b1,↑a3b3) ↓(↑(↓a0b0,↓a2b2),↑(↓a1b1,↓a3b3)) ↓a0b3
|
||||
h = _mm_max_ps(a, b); // ↑a3b0 ↑(↓(↑a0b0,↑a2b2),↓(↑a1b1,↑a3b3)) ↑(↓a0b0,↓a2b2,↓a1b1,↓a3b3) ↑(↓a0b2,↓a1b3)
|
||||
|
||||
return AVX::concat(_mm_unpacklo_ps(l, h), _mm_unpackhi_ps(l, h));
|
||||
}
|
||||
|
||||
#if 0
|
||||
template<> void SortHelper<double>::sort(__m256d &Vc_RESTRICT x, __m256d &Vc_RESTRICT y)
|
||||
{
|
||||
__m256d l = _mm256_min_pd(x, y); // ↓x3y3 ↓x2y2 ↓x1y1 ↓x0y0
|
||||
__m256d h = _mm256_max_pd(x, y); // ↑x3y3 ↑x2y2 ↑x1y1 ↑x0y0
|
||||
x = _mm256_unpacklo_pd(l, h); // ↑x2y2 ↓x2y2 ↑x0y0 ↓x0y0
|
||||
y = _mm256_unpackhi_pd(l, h); // ↑x3y3 ↓x3y3 ↑x1y1 ↓x1y1
|
||||
l = _mm256_min_pd(x, y); // ↓(↑x2y2,↑x3y3) ↓x3x2y3y2 ↓(↑x0y0,↑x1y1) ↓x1x0y1y0
|
||||
h = _mm256_max_pd(x, y); // ↑x3x2y3y2 ↑(↓x2y2,↓x3y3) ↑x1x0y1y0 ↑(↓x0y0,↓x1y1)
|
||||
x = _mm256_unpacklo_pd(l, h); // ↑(↓x2y2,↓x3y3) ↓x3x2y3y2 ↑(↓x0y0,↓x1y1) ↓x1x0y1y0
|
||||
y = _mm256_unpackhi_pd(h, l); // ↓(↑x2y2,↑x3y3) ↑x3x2y3y2 ↓(↑x0y0,↑x1y1) ↑x1x0y1y0
|
||||
l = _mm256_min_pd(x, y); // ↓(↑(↓x2y2,↓x3y3) ↓(↑x2y2,↑x3y3)) ↓x3x2y3y2 ↓(↑(↓x0y0,↓x1y1) ↓(↑x0y0,↑x1y1)) ↓x1x0y1y0
|
||||
h = _mm256_max_pd(x, y); // ↑(↑(↓x2y2,↓x3y3) ↓(↑x2y2,↑x3y3)) ↑x3x2y3y2 ↑(↑(↓x0y0,↓x1y1) ↓(↑x0y0,↑x1y1)) ↑x1x0y1y0
|
||||
__m256d a = Reg::permute<X2, X3, X1, X0>(Reg::permute128<X0, X1>(h, h)); // h0 h1 h3 h2
|
||||
__m256d b = Reg::permute<X2, X3, X1, X0>(l); // l2 l3 l1 l0
|
||||
|
||||
// a3 >= a2 >= b1 >= b0
|
||||
// b3 <= b2 <= a1 <= a0
|
||||
|
||||
// merge
|
||||
l = _mm256_min_pd(a, b); // ↓a3b3 ↓a2b2 ↓a1b1 ↓a0b0
|
||||
h = _mm256_min_pd(a, b); // ↑a3b3 ↑a2b2 ↑a1b1 ↑a0b0
|
||||
|
||||
x = _mm256_unpacklo_pd(l, h); // ↑a2b2 ↓a2b2 ↑a0b0 ↓a0b0
|
||||
y = _mm256_unpackhi_pd(l, h); // ↑a3b3 ↓a3b3 ↑a1b1 ↓a1b1
|
||||
l = _mm256_min_pd(x, y); // ↓(↑a2b2,↑a3b3) ↓a2b3 ↓(↑a0b0,↑a1b1) ↓a1b0
|
||||
h = _mm256_min_pd(x, y); // ↑a3b2 ↑(↓a2b2,↓a3b3) ↑a0b1 ↑(↓a0b0,↓a1b1)
|
||||
|
||||
x = Reg::permute128<Y0, X0>(l, h); // ↑a0b1 ↑(↓a0b0,↓a1b1) ↓(↑a0b0,↑a1b1) ↓a1b0
|
||||
y = Reg::permute128<Y1, X1>(l, h); // ↑a3b2 ↑(↓a2b2,↓a3b3) ↓(↑a2b2,↑a3b3) ↓a2b3
|
||||
l = _mm256_min_pd(x, y); // ↓(↑a0b1,↑a3b2) ↓(↑(↓a0b0,↓a1b1) ↑(↓a2b2,↓a3b3)) ↓(↑a0b0,↑a1b1,↑a2b2,↑a3b3) ↓b0b3
|
||||
h = _mm256_min_pd(x, y); // ↑a0a3 ↑(↓a0b0,↓a1b1,↓a2b2,↓a3b3) ↑(↓(↑a0b0,↑a1b1) ↓(↑a2b2,↑a3b3)) ↑(↓a1b0,↓a2b3)
|
||||
|
||||
x = _mm256_unpacklo_pd(l, h); // h2 l2 h0 l0
|
||||
y = _mm256_unpackhi_pd(l, h); // h3 l3 h1 l1
|
||||
}
|
||||
#endif
|
||||
template <>
|
||||
Vc_CONST AVX2::double_v sorted<CurrentImplementation::current()>(AVX2::double_v x_)
|
||||
{
|
||||
__m256d dcba = x_.data();
|
||||
/*
|
||||
* to find the second largest number find
|
||||
* max(min(max(ab),max(cd)), min(max(ad),max(bc)))
|
||||
* or
|
||||
* max(max(min(ab),min(cd)), min(max(ab),max(cd)))
|
||||
*
|
||||
const __m256d adcb = avx_cast<__m256d>(AVX::concat(_mm_alignr_epi8(avx_cast<__m128i>(dc), avx_cast<__m128i>(ba), 8), _mm_alignr_epi8(avx_cast<__m128i>(ba), avx_cast<__m128i>(dc), 8)));
|
||||
const __m256d l = _mm256_min_pd(dcba, adcb); // min(ad cd bc ab)
|
||||
const __m256d h = _mm256_max_pd(dcba, adcb); // max(ad cd bc ab)
|
||||
// max(h3, h1)
|
||||
// max(min(h0,h2), min(h3,h1))
|
||||
// min(max(l0,l2), max(l3,l1))
|
||||
// min(l3, l1)
|
||||
|
||||
const __m256d ll = _mm256_min_pd(h, Reg::permute128<X0, X1>(h, h)); // min(h3h1 h2h0 h1h3 h0h2)
|
||||
//const __m256d hh = _mm256_max_pd(h3 ll1_3 l1 l0, h1 ll0_2 l3 l2);
|
||||
const __m256d hh = _mm256_max_pd(
|
||||
Reg::permute128<X1, Y0>(_mm256_unpackhi_pd(ll, h), l),
|
||||
Reg::permute128<X0, Y1>(_mm256_blend_pd(h ll, 0x1), l));
|
||||
_mm256_min_pd(hh0, hh1
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// max(max(ac), max(bd))
|
||||
// max(max(min(ac),min(bd)), min(max(ac),max(bd)))
|
||||
// min(max(min(ac),min(bd)), min(max(ac),max(bd)))
|
||||
// min(min(ac), min(bd))
|
||||
__m128d l = _mm_min_pd(AVX::lo128(dcba), AVX::hi128(dcba)); // min(bd) min(ac)
|
||||
__m128d h = _mm_max_pd(AVX::lo128(dcba), AVX::hi128(dcba)); // max(bd) max(ac)
|
||||
__m128d h0_l0 = _mm_unpacklo_pd(l, h);
|
||||
__m128d h1_l1 = _mm_unpackhi_pd(l, h);
|
||||
l = _mm_min_pd(h0_l0, h1_l1);
|
||||
h = _mm_max_pd(h0_l0, h1_l1);
|
||||
return AVX::concat(
|
||||
_mm_min_pd(l, Reg::permute<X0, X0>(h)),
|
||||
_mm_max_pd(h, Reg::permute<X1, X1>(l))
|
||||
);
|
||||
// extract: 1 cycle
|
||||
// min/max: 4 cycles
|
||||
// unpacklo/hi: 2 cycles
|
||||
// min/max: 4 cycles
|
||||
// permute: 1 cycle
|
||||
// min/max: 4 cycles
|
||||
// insert: 1 cycle
|
||||
// ----------------------
|
||||
// total: 17 cycles
|
||||
|
||||
/*
|
||||
__m256d cdab = Reg::permute<X2, X3, X0, X1>(dcba);
|
||||
__m256d l = _mm256_min_pd(dcba, cdab);
|
||||
__m256d h = _mm256_max_pd(dcba, cdab);
|
||||
__m256d maxmin_ba = Reg::permute128<X0, Y0>(l, h);
|
||||
__m256d maxmin_dc = Reg::permute128<X1, Y1>(l, h);
|
||||
|
||||
l = _mm256_min_pd(maxmin_ba, maxmin_dc);
|
||||
h = _mm256_max_pd(maxmin_ba, maxmin_dc);
|
||||
|
||||
return _mm256_blend_pd(h, l, 0x55);
|
||||
*/
|
||||
|
||||
/*
|
||||
// a b c d
|
||||
// b a d c
|
||||
// sort pairs
|
||||
__m256d y, l, h;
|
||||
__m128d l2, h2;
|
||||
y = shuffle<X1, Y0, X3, Y2>(x, x);
|
||||
l = _mm256_min_pd(x, y); // min[ab ab cd cd]
|
||||
h = _mm256_max_pd(x, y); // max[ab ab cd cd]
|
||||
|
||||
// 1 of 2 is at [0]
|
||||
// 1 of 4 is at [1]
|
||||
// 1 of 4 is at [2]
|
||||
// 1 of 2 is at [3]
|
||||
|
||||
// don't be fooled by unpack here. It works differently for AVX pd than for SSE ps
|
||||
x = _mm256_unpacklo_pd(l, h); // l_ab h_ab l_cd h_cd
|
||||
l2 = _mm_min_pd(AVX::lo128(x), AVX::hi128(x)); // l_abcd l(h_ab hcd)
|
||||
h2 = _mm_max_pd(AVX::lo128(x), AVX::hi128(x)); // h(l_ab l_cd) h_abcd
|
||||
|
||||
// either it is:
|
||||
return AVX::concat(l2, h2);
|
||||
// or:
|
||||
// AVX::concat(_mm_unpacklo_pd(l2, h2), _mm_unpackhi_pd(l2, h2));
|
||||
|
||||
// I'd like to have four useful compares
|
||||
const __m128d dc = AVX::hi128(dcba);
|
||||
const __m128d ba = AVX::lo128(dcba);
|
||||
const __m256d adcb = avx_cast<__m256d>(AVX::concat(_mm_alignr_epi8(avx_cast<__m128i>(dc), avx_cast<__m128i>(ba), 8), _mm_alignr_epi8(avx_cast<__m128i>(ba), avx_cast<__m128i>(dc), 8)));
|
||||
|
||||
const int extraCmp = _mm_movemask_pd(_mm_cmpgt_pd(dc, ba));
|
||||
// 0x0: d <= b && c <= a
|
||||
// 0x1: d <= b && c > a
|
||||
// 0x2: d > b && c <= a
|
||||
// 0x3: d > b && c > a
|
||||
|
||||
switch (_mm256_movemask_pd(_mm256_cmpgt_pd(dcba, adcb))) {
|
||||
// impossible: 0x0, 0xf
|
||||
case 0x1: // a <= b && b <= c && c <= d && d > a
|
||||
// abcd
|
||||
return Reg::permute<X2, X3, X0, X1>(Reg::permute<X0, X1>(dcba, dcba));
|
||||
case 0x2: // a <= b && b <= c && c > d && d <= a
|
||||
// dabc
|
||||
return Reg::permute<X2, X3, X0, X1>(adcb);
|
||||
case 0x3: // a <= b && b <= c && c > d && d > a
|
||||
// a[bd]c
|
||||
if (extraCmp & 2) {
|
||||
// abdc
|
||||
return Reg::permute<X2, X3, X1, X0>(Reg::permute<X0, X1>(dcba, dcba));
|
||||
} else {
|
||||
// adbc
|
||||
return Reg::permute<X3, X2, X0, X1>(adcb);
|
||||
}
|
||||
case 0x4: // a <= b && b > c && c <= d && d <= a
|
||||
// cdab;
|
||||
return Reg::permute<X2, X3, X0, X1>(dcba);
|
||||
case 0x5: // a <= b && b > c && c <= d && d > a
|
||||
// [ac] < [bd]
|
||||
switch (extraCmp) {
|
||||
case 0x0: // d <= b && c <= a
|
||||
// cadb
|
||||
return shuffle<>(dcba, bcda);
|
||||
case 0x1: // d <= b && c > a
|
||||
case 0x2: // d > b && c <= a
|
||||
case 0x3: // d > b && c > a
|
||||
}
|
||||
case 0x6: // a <= b && b > c && c > d && d <= a
|
||||
// d[ac]b
|
||||
case 0x7: // a <= b && b > c && c > d && d > a
|
||||
// adcb;
|
||||
return permute<X1, X0, X3, X2>(permute128<X1, X0>(bcda, bcda));
|
||||
case 0x8: // a > b && b <= c && c <= d && d <= a
|
||||
return bcda;
|
||||
case 0x9: // a > b && b <= c && c <= d && d > a
|
||||
// b[ac]d;
|
||||
case 0xa: // a > b && b <= c && c > d && d <= a
|
||||
// [ac] > [bd]
|
||||
case 0xb: // a > b && b <= c && c > d && d > a
|
||||
// badc;
|
||||
return permute128<X1, X0>(dcba);
|
||||
case 0xc: // a > b && b > c && c <= d && d <= a
|
||||
// c[bd]a;
|
||||
case 0xd: // a > b && b > c && c <= d && d > a
|
||||
// cbad;
|
||||
return permute<X1, X0, X3, X2>(bcda);
|
||||
case 0xe: // a > b && b > c && c > d && d <= a
|
||||
return dcba;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
} // namespace Detail
|
||||
} // namespace Vc
|
||||
|
||||
// vim: foldmethod=marker
|
||||
544
obj-x86_64-linux-gnu/3rdparty/Vc/avx_sorthelper_AVX2+FMA+BMI2.cpp
vendored
Normal file
544
obj-x86_64-linux-gnu/3rdparty/Vc/avx_sorthelper_AVX2+FMA+BMI2.cpp
vendored
Normal file
|
|
@ -0,0 +1,544 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2011-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
#include <Vc/avx/vector.h>
|
||||
#include <Vc/avx/debug.h>
|
||||
#include <Vc/avx/macros.h>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
#ifdef Vc_IMPL_AVX2
|
||||
template <>
|
||||
Vc_CONST AVX2::short_v sorted<CurrentImplementation::current()>(AVX2::short_v x_)
|
||||
{
|
||||
// ab cd ef gh ij kl mn op
|
||||
// ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑
|
||||
// ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮
|
||||
// ⎮ ╳ ⎮ ⎮ ╳ ⎮ ⎮ ╳ ⎮ ⎮ ╳ ⎮
|
||||
// ⎮⎛ ⎞⎮ ⎮⎛ ⎞⎮ ⎮⎛ ⎞⎮ ⎮⎛ ⎞⎮
|
||||
// <> <> <> <> <> <> <> <>
|
||||
// ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑
|
||||
// ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮
|
||||
// ⎮ o ⎮ ⎮ o ⎮ ⎮ o ⎮ ⎮ o ⎮
|
||||
// ⎮↓ ↑⎮ ⎮↓ ↑⎮ ⎮↓ ↑⎮ ⎮↓ ↑⎮
|
||||
// 01 23 01 23 01 23 01 23
|
||||
// ⎮⎮ ⎮⎮ ╳ ⎮⎮ ⎮⎮ ╳
|
||||
// 01 23 32 10 01 23 32 10
|
||||
// ⎮⎝ ⎮⎝ ⎠⎮ ⎠⎮ ⎮⎝ ⎮⎝ ⎠⎮ ⎠⎮
|
||||
// ⎮ ╲⎮ ╳ ⎮╱ ⎮ ⎮ ╲⎮ ╳ ⎮╱ ⎮
|
||||
// ⎮ ╲╱ ╲╱ ⎮ ⎮ ╲╱ ╲╱ ⎮
|
||||
// ⎮ ╱╲ ╱╲ ⎮ ⎮ ╱╲ ╱╲ ⎮
|
||||
// ⎮ ╱⎮ ╳ ⎮╲ ⎮ ⎮ ╱⎮ ╳ ⎮╲ ⎮
|
||||
// ⎮⎛ ⎮⎛ ⎞⎮ ⎞⎮ ⎮⎛ ⎮⎛ ⎞⎮ ⎞⎮
|
||||
// <> <> <> <> <> <> <> <>
|
||||
// ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑
|
||||
// ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮
|
||||
// ⎮ ╳ ⎮ ⎮ ╳ ⎮ ⎮ ╳ ⎮ ⎮ ╳ ⎮
|
||||
// ⎮⎛ ⎞⎮ ⎮⎛ ⎞⎮ ⎮⎛ ⎞⎮ ⎮⎛ ⎞⎮
|
||||
// <> <> <> <> <> <> <> <>
|
||||
// ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑ ↓↑
|
||||
// ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮ ⎮⎝ ⎠⎮
|
||||
// ⎮ o ⎮ ⎮ o ⎮ ⎮ o ⎮ ⎮ o ⎮
|
||||
// ⎮↓ ↑⎮ ⎮↓ ↑⎮ ⎮↓ ↑⎮ ⎮↓ ↑⎮
|
||||
// 01 23 01 23 01 23 01 23
|
||||
|
||||
// sort pairs (one min/max)
|
||||
auto x = AVX::lo128(x_.data());
|
||||
auto y = AVX::hi128(x_.data());
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
auto l = _mm_min_epi16(x, y);
|
||||
auto h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
|
||||
// merge left & right quads (two min/max)
|
||||
x = _mm_unpacklo_epi16(l, h);
|
||||
y = _mm_unpackhi_epi16(h, l);
|
||||
// Vc_DEBUG << "8x2 sorted xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::permuteLo<X1, X0, X3, X2>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(l, h));
|
||||
y = Mem::permuteHi<X5, X4, X7, X6>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(h, l));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
|
||||
// merge quads into octs (three min/max)
|
||||
x = _mm_unpacklo_epi16(h, l);
|
||||
y = _mm_unpackhi_epi16(l, h);
|
||||
// Vc_DEBUG << "4x4 sorted xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::permuteLo<X2, X3, X0, X1>(Mem::blend<X0, X1, Y2, Y3, X4, X5, Y6, Y7>(h, l));
|
||||
y = Mem::permuteHi<X6, X7, X4, X5>(Mem::blend<X0, X1, Y2, Y3, X4, X5, Y6, Y7>(l, h));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::permuteHi<X5, X4, X7, X6>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(l, h));
|
||||
y = Mem::permuteLo<X1, X0, X3, X2>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(h, l));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h) << " done?";
|
||||
|
||||
// merge octs into hexa (four min/max)
|
||||
x = _mm_unpacklo_epi16(l, h);
|
||||
y = _mm_unpackhi_epi16(h, l);
|
||||
// Vc_DEBUG << "2x8 sorted xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = _mm_unpacklo_epi64(l, h);
|
||||
y = _mm_unpackhi_epi64(l, h);
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = _mm_castps_si128(Mem::permute<X1, X0, X3, X2>(Mem::blend<X0, Y1, X2, Y3>(_mm_castsi128_ps(h), _mm_castsi128_ps(l))));
|
||||
y = _mm_castps_si128(Mem::blend<X0, Y1, X2, Y3>(_mm_castsi128_ps(l), _mm_castsi128_ps(h)));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(l, h);
|
||||
y = Mem::permuteLo<X1, X0, X3, X2>(
|
||||
Mem::permuteHi<X5, X4, X7, X6>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(h, l)));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epi16(x, y);
|
||||
h = _mm_max_epi16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = _mm_unpacklo_epi16(l, h);
|
||||
y = _mm_unpackhi_epi16(l, h);
|
||||
return AVX::concat(x, y);
|
||||
}
|
||||
|
||||
template <>
|
||||
Vc_CONST AVX2::ushort_v sorted<CurrentImplementation::current()>(AVX2::ushort_v x_)
|
||||
{
|
||||
// sort pairs (one min/max)
|
||||
auto x = AVX::lo128(x_.data());
|
||||
auto y = AVX::hi128(x_.data());
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
auto l = _mm_min_epu16(x, y);
|
||||
auto h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
|
||||
// merge left & right quads (two min/max)
|
||||
x = _mm_unpacklo_epi16(l, h);
|
||||
y = _mm_unpackhi_epi16(h, l);
|
||||
// Vc_DEBUG << "8x2 sorted xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::permuteLo<X1, X0, X3, X2>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(l, h));
|
||||
y = Mem::permuteHi<X5, X4, X7, X6>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(h, l));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
|
||||
// merge quads into octs (three min/max)
|
||||
x = _mm_unpacklo_epi16(h, l);
|
||||
y = _mm_unpackhi_epi16(l, h);
|
||||
// Vc_DEBUG << "4x4 sorted xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::permuteLo<X2, X3, X0, X1>(Mem::blend<X0, X1, Y2, Y3, X4, X5, Y6, Y7>(h, l));
|
||||
y = Mem::permuteHi<X6, X7, X4, X5>(Mem::blend<X0, X1, Y2, Y3, X4, X5, Y6, Y7>(l, h));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::permuteHi<X5, X4, X7, X6>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(l, h));
|
||||
y = Mem::permuteLo<X1, X0, X3, X2>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(h, l));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h) << " done?";
|
||||
|
||||
// merge octs into hexa (four min/max)
|
||||
x = _mm_unpacklo_epi16(l, h);
|
||||
y = _mm_unpackhi_epi16(h, l);
|
||||
// Vc_DEBUG << "2x8 sorted xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = _mm_unpacklo_epi64(l, h);
|
||||
y = _mm_unpackhi_epi64(l, h);
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = _mm_castps_si128(Mem::permute<X1, X0, X3, X2>(Mem::blend<X0, Y1, X2, Y3>(_mm_castsi128_ps(h), _mm_castsi128_ps(l))));
|
||||
y = _mm_castps_si128(Mem::blend<X0, Y1, X2, Y3>(_mm_castsi128_ps(l), _mm_castsi128_ps(h)));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(l, h);
|
||||
y = Mem::permuteLo<X1, X0, X3, X2>(
|
||||
Mem::permuteHi<X5, X4, X7, X6>(Mem::blend<X0, Y1, X2, Y3, X4, Y5, X6, Y7>(h, l)));
|
||||
// Vc_DEBUG << "xy: " << AVX::addType<short>(x) << AVX::addType<short>(y);
|
||||
l = _mm_min_epu16(x, y);
|
||||
h = _mm_max_epu16(x, y);
|
||||
// Vc_DEBUG << "lh: " << AVX::addType<short>(l) << AVX::addType<short>(h);
|
||||
x = _mm_unpacklo_epi16(l, h);
|
||||
y = _mm_unpackhi_epi16(l, h);
|
||||
return AVX::concat(x, y);
|
||||
}
|
||||
|
||||
template <> Vc_CONST AVX2::int_v sorted<CurrentImplementation::current()>(AVX2::int_v x_)
|
||||
{
|
||||
using namespace AVX;
|
||||
const __m256i hgfedcba = x_.data();
|
||||
const __m128i hgfe = hi128(hgfedcba);
|
||||
const __m128i dcba = lo128(hgfedcba);
|
||||
__m128i l = _mm_min_epi32(hgfe, dcba); // ↓hd ↓gc ↓fb ↓ea
|
||||
__m128i h = _mm_max_epi32(hgfe, dcba); // ↑hd ↑gc ↑fb ↑ea
|
||||
|
||||
__m128i x = _mm_unpacklo_epi32(l, h); // ↑fb ↓fb ↑ea ↓ea
|
||||
__m128i y = _mm_unpackhi_epi32(l, h); // ↑hd ↓hd ↑gc ↓gc
|
||||
|
||||
l = _mm_min_epi32(x, y); // ↓(↑fb,↑hd) ↓hfdb ↓(↑ea,↑gc) ↓geca
|
||||
h = _mm_max_epi32(x, y); // ↑hfdb ↑(↓fb,↓hd) ↑geca ↑(↓ea,↓gc)
|
||||
|
||||
x = _mm_min_epi32(l, Reg::permute<X2, X2, X0, X0>(h)); // 2(hfdb) 1(hfdb) 2(geca) 1(geca)
|
||||
y = _mm_max_epi32(h, Reg::permute<X3, X3, X1, X1>(l)); // 4(hfdb) 3(hfdb) 4(geca) 3(geca)
|
||||
|
||||
__m128i b = Reg::shuffle<Y0, Y1, X0, X1>(y, x); // b3 <= b2 <= b1 <= b0
|
||||
__m128i a = _mm_unpackhi_epi64(x, y); // a3 >= a2 >= a1 >= a0
|
||||
|
||||
// _mm_extract_epi32 may return an unsigned int, breaking these comparisons.
|
||||
if (Vc_IS_UNLIKELY(static_cast<int>(_mm_extract_epi32(x, 2)) >= static_cast<int>(_mm_extract_epi32(y, 1)))) {
|
||||
return concat(Reg::permute<X0, X1, X2, X3>(b), a);
|
||||
} else if (Vc_IS_UNLIKELY(static_cast<int>(_mm_extract_epi32(x, 0)) >= static_cast<int>(_mm_extract_epi32(y, 3)))) {
|
||||
return concat(a, Reg::permute<X0, X1, X2, X3>(b));
|
||||
}
|
||||
|
||||
// merge
|
||||
l = _mm_min_epi32(a, b); // ↓a3b3 ↓a2b2 ↓a1b1 ↓a0b0
|
||||
h = _mm_max_epi32(a, b); // ↑a3b3 ↑a2b2 ↑a1b1 ↑a0b0
|
||||
|
||||
a = _mm_unpacklo_epi32(l, h); // ↑a1b1 ↓a1b1 ↑a0b0 ↓a0b0
|
||||
b = _mm_unpackhi_epi32(l, h); // ↑a3b3 ↓a3b3 ↑a2b2 ↓a2b2
|
||||
l = _mm_min_epi32(a, b); // ↓(↑a1b1,↑a3b3) ↓a1b3 ↓(↑a0b0,↑a2b2) ↓a0b2
|
||||
h = _mm_max_epi32(a, b); // ↑a3b1 ↑(↓a1b1,↓a3b3) ↑a2b0 ↑(↓a0b0,↓a2b2)
|
||||
|
||||
a = _mm_unpacklo_epi32(l, h); // ↑a2b0 ↓(↑a0b0,↑a2b2) ↑(↓a0b0,↓a2b2) ↓a0b2
|
||||
b = _mm_unpackhi_epi32(l, h); // ↑a3b1 ↓(↑a1b1,↑a3b3) ↑(↓a1b1,↓a3b3) ↓a1b3
|
||||
l = _mm_min_epi32(a, b); // ↓(↑a2b0,↑a3b1) ↓(↑a0b0,↑a2b2,↑a1b1,↑a3b3) ↓(↑(↓a0b0,↓a2b2) ↑(↓a1b1,↓a3b3)) ↓a0b3
|
||||
h = _mm_max_epi32(a, b); // ↑a3b0 ↑(↓(↑a0b0,↑a2b2) ↓(↑a1b1,↑a3b3)) ↑(↓a0b0,↓a2b2,↓a1b1,↓a3b3) ↑(↓a0b2,↓a1b3)
|
||||
|
||||
return concat(_mm_unpacklo_epi32(l, h), _mm_unpackhi_epi32(l, h));
|
||||
}
|
||||
|
||||
template <>
|
||||
Vc_CONST AVX2::uint_v sorted<CurrentImplementation::current()>(AVX2::uint_v x_)
|
||||
{
|
||||
using namespace AVX;
|
||||
const __m256i hgfedcba = x_.data();
|
||||
const __m128i hgfe = hi128(hgfedcba);
|
||||
const __m128i dcba = lo128(hgfedcba);
|
||||
__m128i l = _mm_min_epu32(hgfe, dcba); // ↓hd ↓gc ↓fb ↓ea
|
||||
__m128i h = _mm_max_epu32(hgfe, dcba); // ↑hd ↑gc ↑fb ↑ea
|
||||
|
||||
__m128i x = _mm_unpacklo_epi32(l, h); // ↑fb ↓fb ↑ea ↓ea
|
||||
__m128i y = _mm_unpackhi_epi32(l, h); // ↑hd ↓hd ↑gc ↓gc
|
||||
|
||||
l = _mm_min_epu32(x, y); // ↓(↑fb,↑hd) ↓hfdb ↓(↑ea,↑gc) ↓geca
|
||||
h = _mm_max_epu32(x, y); // ↑hfdb ↑(↓fb,↓hd) ↑geca ↑(↓ea,↓gc)
|
||||
|
||||
x = _mm_min_epu32(l, Reg::permute<X2, X2, X0, X0>(h)); // 2(hfdb) 1(hfdb) 2(geca) 1(geca)
|
||||
y = _mm_max_epu32(h, Reg::permute<X3, X3, X1, X1>(l)); // 4(hfdb) 3(hfdb) 4(geca) 3(geca)
|
||||
|
||||
__m128i b = Reg::shuffle<Y0, Y1, X0, X1>(y, x); // b3 <= b2 <= b1 <= b0
|
||||
__m128i a = _mm_unpackhi_epi64(x, y); // a3 >= a2 >= a1 >= a0
|
||||
|
||||
if (Vc_IS_UNLIKELY(extract_epu32<2>(x) >= extract_epu32<1>(y))) {
|
||||
return concat(Reg::permute<X0, X1, X2, X3>(b), a);
|
||||
} else if (Vc_IS_UNLIKELY(extract_epu32<0>(x) >= extract_epu32<3>(y))) {
|
||||
return concat(a, Reg::permute<X0, X1, X2, X3>(b));
|
||||
}
|
||||
|
||||
// merge
|
||||
l = _mm_min_epu32(a, b); // ↓a3b3 ↓a2b2 ↓a1b1 ↓a0b0
|
||||
h = _mm_max_epu32(a, b); // ↑a3b3 ↑a2b2 ↑a1b1 ↑a0b0
|
||||
|
||||
a = _mm_unpacklo_epi32(l, h); // ↑a1b1 ↓a1b1 ↑a0b0 ↓a0b0
|
||||
b = _mm_unpackhi_epi32(l, h); // ↑a3b3 ↓a3b3 ↑a2b2 ↓a2b2
|
||||
l = _mm_min_epu32(a, b); // ↓(↑a1b1,↑a3b3) ↓a1b3 ↓(↑a0b0,↑a2b2) ↓a0b2
|
||||
h = _mm_max_epu32(a, b); // ↑a3b1 ↑(↓a1b1,↓a3b3) ↑a2b0 ↑(↓a0b0,↓a2b2)
|
||||
|
||||
a = _mm_unpacklo_epi32(l, h); // ↑a2b0 ↓(↑a0b0,↑a2b2) ↑(↓a0b0,↓a2b2) ↓a0b2
|
||||
b = _mm_unpackhi_epi32(l, h); // ↑a3b1 ↓(↑a1b1,↑a3b3) ↑(↓a1b1,↓a3b3) ↓a1b3
|
||||
l = _mm_min_epu32(a, b); // ↓(↑a2b0,↑a3b1) ↓(↑a0b0,↑a2b2,↑a1b1,↑a3b3) ↓(↑(↓a0b0,↓a2b2) ↑(↓a1b1,↓a3b3)) ↓a0b3
|
||||
h = _mm_max_epu32(a, b); // ↑a3b0 ↑(↓(↑a0b0,↑a2b2) ↓(↑a1b1,↑a3b3)) ↑(↓a0b0,↓a2b2,↓a1b1,↓a3b3) ↑(↓a0b2,↓a1b3)
|
||||
|
||||
return concat(_mm_unpacklo_epi32(l, h), _mm_unpackhi_epi32(l, h));
|
||||
}
|
||||
#endif // AVX2
|
||||
|
||||
template <>
|
||||
Vc_CONST AVX2::float_v sorted<CurrentImplementation::current()>(AVX2::float_v x_)
|
||||
{
|
||||
__m256 hgfedcba = x_.data();
|
||||
const __m128 hgfe = AVX::hi128(hgfedcba);
|
||||
const __m128 dcba = AVX::lo128(hgfedcba);
|
||||
__m128 l = _mm_min_ps(hgfe, dcba); // ↓hd ↓gc ↓fb ↓ea
|
||||
__m128 h = _mm_max_ps(hgfe, dcba); // ↑hd ↑gc ↑fb ↑ea
|
||||
|
||||
__m128 x = _mm_unpacklo_ps(l, h); // ↑fb ↓fb ↑ea ↓ea
|
||||
__m128 y = _mm_unpackhi_ps(l, h); // ↑hd ↓hd ↑gc ↓gc
|
||||
|
||||
l = _mm_min_ps(x, y); // ↓(↑fb,↑hd) ↓hfdb ↓(↑ea,↑gc) ↓geca
|
||||
h = _mm_max_ps(x, y); // ↑hfdb ↑(↓fb,↓hd) ↑geca ↑(↓ea,↓gc)
|
||||
|
||||
x = _mm_min_ps(l, Reg::permute<X2, X2, X0, X0>(h)); // 2(hfdb) 1(hfdb) 2(geca) 1(geca)
|
||||
y = _mm_max_ps(h, Reg::permute<X3, X3, X1, X1>(l)); // 4(hfdb) 3(hfdb) 4(geca) 3(geca)
|
||||
|
||||
__m128 a = _mm_castpd_ps(_mm_unpackhi_pd(_mm_castps_pd(x), _mm_castps_pd(y))); // a3 >= a2 >= a1 >= a0
|
||||
__m128 b = Reg::shuffle<Y0, Y1, X0, X1>(y, x); // b3 <= b2 <= b1 <= b0
|
||||
|
||||
// merge
|
||||
l = _mm_min_ps(a, b); // ↓a3b3 ↓a2b2 ↓a1b1 ↓a0b0
|
||||
h = _mm_max_ps(a, b); // ↑a3b3 ↑a2b2 ↑a1b1 ↑a0b0
|
||||
|
||||
a = _mm_unpacklo_ps(l, h); // ↑a1b1 ↓a1b1 ↑a0b0 ↓a0b0
|
||||
b = _mm_unpackhi_ps(l, h); // ↑a3b3 ↓a3b3 ↑a2b2 ↓a2b2
|
||||
l = _mm_min_ps(a, b); // ↓(↑a1b1,↑a3b3) ↓a1b3 ↓(↑a0b0,↑a2b2) ↓a0b2
|
||||
h = _mm_max_ps(a, b); // ↑a3b1 ↑(↓a1b1,↓a3b3) ↑a2b0 ↑(↓a0b0,↓a2b2)
|
||||
|
||||
a = _mm_unpacklo_ps(l, h); // ↑a2b0 ↓(↑a0b0,↑a2b2) ↑(↓a0b0,↓a2b2) ↓a0b2
|
||||
b = _mm_unpackhi_ps(l, h); // ↑a3b1 ↓(↑a1b1,↑a3b3) ↑(↓a1b1,↓a3b3) ↓a1b3
|
||||
l = _mm_min_ps(a, b); // ↓(↑a2b0,↑a3b1) ↓(↑a0b0,↑a2b2,↑a1b1,↑a3b3) ↓(↑(↓a0b0,↓a2b2),↑(↓a1b1,↓a3b3)) ↓a0b3
|
||||
h = _mm_max_ps(a, b); // ↑a3b0 ↑(↓(↑a0b0,↑a2b2),↓(↑a1b1,↑a3b3)) ↑(↓a0b0,↓a2b2,↓a1b1,↓a3b3) ↑(↓a0b2,↓a1b3)
|
||||
|
||||
return AVX::concat(_mm_unpacklo_ps(l, h), _mm_unpackhi_ps(l, h));
|
||||
}
|
||||
|
||||
#if 0
|
||||
template<> void SortHelper<double>::sort(__m256d &Vc_RESTRICT x, __m256d &Vc_RESTRICT y)
|
||||
{
|
||||
__m256d l = _mm256_min_pd(x, y); // ↓x3y3 ↓x2y2 ↓x1y1 ↓x0y0
|
||||
__m256d h = _mm256_max_pd(x, y); // ↑x3y3 ↑x2y2 ↑x1y1 ↑x0y0
|
||||
x = _mm256_unpacklo_pd(l, h); // ↑x2y2 ↓x2y2 ↑x0y0 ↓x0y0
|
||||
y = _mm256_unpackhi_pd(l, h); // ↑x3y3 ↓x3y3 ↑x1y1 ↓x1y1
|
||||
l = _mm256_min_pd(x, y); // ↓(↑x2y2,↑x3y3) ↓x3x2y3y2 ↓(↑x0y0,↑x1y1) ↓x1x0y1y0
|
||||
h = _mm256_max_pd(x, y); // ↑x3x2y3y2 ↑(↓x2y2,↓x3y3) ↑x1x0y1y0 ↑(↓x0y0,↓x1y1)
|
||||
x = _mm256_unpacklo_pd(l, h); // ↑(↓x2y2,↓x3y3) ↓x3x2y3y2 ↑(↓x0y0,↓x1y1) ↓x1x0y1y0
|
||||
y = _mm256_unpackhi_pd(h, l); // ↓(↑x2y2,↑x3y3) ↑x3x2y3y2 ↓(↑x0y0,↑x1y1) ↑x1x0y1y0
|
||||
l = _mm256_min_pd(x, y); // ↓(↑(↓x2y2,↓x3y3) ↓(↑x2y2,↑x3y3)) ↓x3x2y3y2 ↓(↑(↓x0y0,↓x1y1) ↓(↑x0y0,↑x1y1)) ↓x1x0y1y0
|
||||
h = _mm256_max_pd(x, y); // ↑(↑(↓x2y2,↓x3y3) ↓(↑x2y2,↑x3y3)) ↑x3x2y3y2 ↑(↑(↓x0y0,↓x1y1) ↓(↑x0y0,↑x1y1)) ↑x1x0y1y0
|
||||
__m256d a = Reg::permute<X2, X3, X1, X0>(Reg::permute128<X0, X1>(h, h)); // h0 h1 h3 h2
|
||||
__m256d b = Reg::permute<X2, X3, X1, X0>(l); // l2 l3 l1 l0
|
||||
|
||||
// a3 >= a2 >= b1 >= b0
|
||||
// b3 <= b2 <= a1 <= a0
|
||||
|
||||
// merge
|
||||
l = _mm256_min_pd(a, b); // ↓a3b3 ↓a2b2 ↓a1b1 ↓a0b0
|
||||
h = _mm256_min_pd(a, b); // ↑a3b3 ↑a2b2 ↑a1b1 ↑a0b0
|
||||
|
||||
x = _mm256_unpacklo_pd(l, h); // ↑a2b2 ↓a2b2 ↑a0b0 ↓a0b0
|
||||
y = _mm256_unpackhi_pd(l, h); // ↑a3b3 ↓a3b3 ↑a1b1 ↓a1b1
|
||||
l = _mm256_min_pd(x, y); // ↓(↑a2b2,↑a3b3) ↓a2b3 ↓(↑a0b0,↑a1b1) ↓a1b0
|
||||
h = _mm256_min_pd(x, y); // ↑a3b2 ↑(↓a2b2,↓a3b3) ↑a0b1 ↑(↓a0b0,↓a1b1)
|
||||
|
||||
x = Reg::permute128<Y0, X0>(l, h); // ↑a0b1 ↑(↓a0b0,↓a1b1) ↓(↑a0b0,↑a1b1) ↓a1b0
|
||||
y = Reg::permute128<Y1, X1>(l, h); // ↑a3b2 ↑(↓a2b2,↓a3b3) ↓(↑a2b2,↑a3b3) ↓a2b3
|
||||
l = _mm256_min_pd(x, y); // ↓(↑a0b1,↑a3b2) ↓(↑(↓a0b0,↓a1b1) ↑(↓a2b2,↓a3b3)) ↓(↑a0b0,↑a1b1,↑a2b2,↑a3b3) ↓b0b3
|
||||
h = _mm256_min_pd(x, y); // ↑a0a3 ↑(↓a0b0,↓a1b1,↓a2b2,↓a3b3) ↑(↓(↑a0b0,↑a1b1) ↓(↑a2b2,↑a3b3)) ↑(↓a1b0,↓a2b3)
|
||||
|
||||
x = _mm256_unpacklo_pd(l, h); // h2 l2 h0 l0
|
||||
y = _mm256_unpackhi_pd(l, h); // h3 l3 h1 l1
|
||||
}
|
||||
#endif
|
||||
template <>
|
||||
Vc_CONST AVX2::double_v sorted<CurrentImplementation::current()>(AVX2::double_v x_)
|
||||
{
|
||||
__m256d dcba = x_.data();
|
||||
/*
|
||||
* to find the second largest number find
|
||||
* max(min(max(ab),max(cd)), min(max(ad),max(bc)))
|
||||
* or
|
||||
* max(max(min(ab),min(cd)), min(max(ab),max(cd)))
|
||||
*
|
||||
const __m256d adcb = avx_cast<__m256d>(AVX::concat(_mm_alignr_epi8(avx_cast<__m128i>(dc), avx_cast<__m128i>(ba), 8), _mm_alignr_epi8(avx_cast<__m128i>(ba), avx_cast<__m128i>(dc), 8)));
|
||||
const __m256d l = _mm256_min_pd(dcba, adcb); // min(ad cd bc ab)
|
||||
const __m256d h = _mm256_max_pd(dcba, adcb); // max(ad cd bc ab)
|
||||
// max(h3, h1)
|
||||
// max(min(h0,h2), min(h3,h1))
|
||||
// min(max(l0,l2), max(l3,l1))
|
||||
// min(l3, l1)
|
||||
|
||||
const __m256d ll = _mm256_min_pd(h, Reg::permute128<X0, X1>(h, h)); // min(h3h1 h2h0 h1h3 h0h2)
|
||||
//const __m256d hh = _mm256_max_pd(h3 ll1_3 l1 l0, h1 ll0_2 l3 l2);
|
||||
const __m256d hh = _mm256_max_pd(
|
||||
Reg::permute128<X1, Y0>(_mm256_unpackhi_pd(ll, h), l),
|
||||
Reg::permute128<X0, Y1>(_mm256_blend_pd(h ll, 0x1), l));
|
||||
_mm256_min_pd(hh0, hh1
|
||||
*/
|
||||
|
||||
//////////////////////////////////////////////////////////////////////////////////
|
||||
// max(max(ac), max(bd))
|
||||
// max(max(min(ac),min(bd)), min(max(ac),max(bd)))
|
||||
// min(max(min(ac),min(bd)), min(max(ac),max(bd)))
|
||||
// min(min(ac), min(bd))
|
||||
__m128d l = _mm_min_pd(AVX::lo128(dcba), AVX::hi128(dcba)); // min(bd) min(ac)
|
||||
__m128d h = _mm_max_pd(AVX::lo128(dcba), AVX::hi128(dcba)); // max(bd) max(ac)
|
||||
__m128d h0_l0 = _mm_unpacklo_pd(l, h);
|
||||
__m128d h1_l1 = _mm_unpackhi_pd(l, h);
|
||||
l = _mm_min_pd(h0_l0, h1_l1);
|
||||
h = _mm_max_pd(h0_l0, h1_l1);
|
||||
return AVX::concat(
|
||||
_mm_min_pd(l, Reg::permute<X0, X0>(h)),
|
||||
_mm_max_pd(h, Reg::permute<X1, X1>(l))
|
||||
);
|
||||
// extract: 1 cycle
|
||||
// min/max: 4 cycles
|
||||
// unpacklo/hi: 2 cycles
|
||||
// min/max: 4 cycles
|
||||
// permute: 1 cycle
|
||||
// min/max: 4 cycles
|
||||
// insert: 1 cycle
|
||||
// ----------------------
|
||||
// total: 17 cycles
|
||||
|
||||
/*
|
||||
__m256d cdab = Reg::permute<X2, X3, X0, X1>(dcba);
|
||||
__m256d l = _mm256_min_pd(dcba, cdab);
|
||||
__m256d h = _mm256_max_pd(dcba, cdab);
|
||||
__m256d maxmin_ba = Reg::permute128<X0, Y0>(l, h);
|
||||
__m256d maxmin_dc = Reg::permute128<X1, Y1>(l, h);
|
||||
|
||||
l = _mm256_min_pd(maxmin_ba, maxmin_dc);
|
||||
h = _mm256_max_pd(maxmin_ba, maxmin_dc);
|
||||
|
||||
return _mm256_blend_pd(h, l, 0x55);
|
||||
*/
|
||||
|
||||
/*
|
||||
// a b c d
|
||||
// b a d c
|
||||
// sort pairs
|
||||
__m256d y, l, h;
|
||||
__m128d l2, h2;
|
||||
y = shuffle<X1, Y0, X3, Y2>(x, x);
|
||||
l = _mm256_min_pd(x, y); // min[ab ab cd cd]
|
||||
h = _mm256_max_pd(x, y); // max[ab ab cd cd]
|
||||
|
||||
// 1 of 2 is at [0]
|
||||
// 1 of 4 is at [1]
|
||||
// 1 of 4 is at [2]
|
||||
// 1 of 2 is at [3]
|
||||
|
||||
// don't be fooled by unpack here. It works differently for AVX pd than for SSE ps
|
||||
x = _mm256_unpacklo_pd(l, h); // l_ab h_ab l_cd h_cd
|
||||
l2 = _mm_min_pd(AVX::lo128(x), AVX::hi128(x)); // l_abcd l(h_ab hcd)
|
||||
h2 = _mm_max_pd(AVX::lo128(x), AVX::hi128(x)); // h(l_ab l_cd) h_abcd
|
||||
|
||||
// either it is:
|
||||
return AVX::concat(l2, h2);
|
||||
// or:
|
||||
// AVX::concat(_mm_unpacklo_pd(l2, h2), _mm_unpackhi_pd(l2, h2));
|
||||
|
||||
// I'd like to have four useful compares
|
||||
const __m128d dc = AVX::hi128(dcba);
|
||||
const __m128d ba = AVX::lo128(dcba);
|
||||
const __m256d adcb = avx_cast<__m256d>(AVX::concat(_mm_alignr_epi8(avx_cast<__m128i>(dc), avx_cast<__m128i>(ba), 8), _mm_alignr_epi8(avx_cast<__m128i>(ba), avx_cast<__m128i>(dc), 8)));
|
||||
|
||||
const int extraCmp = _mm_movemask_pd(_mm_cmpgt_pd(dc, ba));
|
||||
// 0x0: d <= b && c <= a
|
||||
// 0x1: d <= b && c > a
|
||||
// 0x2: d > b && c <= a
|
||||
// 0x3: d > b && c > a
|
||||
|
||||
switch (_mm256_movemask_pd(_mm256_cmpgt_pd(dcba, adcb))) {
|
||||
// impossible: 0x0, 0xf
|
||||
case 0x1: // a <= b && b <= c && c <= d && d > a
|
||||
// abcd
|
||||
return Reg::permute<X2, X3, X0, X1>(Reg::permute<X0, X1>(dcba, dcba));
|
||||
case 0x2: // a <= b && b <= c && c > d && d <= a
|
||||
// dabc
|
||||
return Reg::permute<X2, X3, X0, X1>(adcb);
|
||||
case 0x3: // a <= b && b <= c && c > d && d > a
|
||||
// a[bd]c
|
||||
if (extraCmp & 2) {
|
||||
// abdc
|
||||
return Reg::permute<X2, X3, X1, X0>(Reg::permute<X0, X1>(dcba, dcba));
|
||||
} else {
|
||||
// adbc
|
||||
return Reg::permute<X3, X2, X0, X1>(adcb);
|
||||
}
|
||||
case 0x4: // a <= b && b > c && c <= d && d <= a
|
||||
// cdab;
|
||||
return Reg::permute<X2, X3, X0, X1>(dcba);
|
||||
case 0x5: // a <= b && b > c && c <= d && d > a
|
||||
// [ac] < [bd]
|
||||
switch (extraCmp) {
|
||||
case 0x0: // d <= b && c <= a
|
||||
// cadb
|
||||
return shuffle<>(dcba, bcda);
|
||||
case 0x1: // d <= b && c > a
|
||||
case 0x2: // d > b && c <= a
|
||||
case 0x3: // d > b && c > a
|
||||
}
|
||||
case 0x6: // a <= b && b > c && c > d && d <= a
|
||||
// d[ac]b
|
||||
case 0x7: // a <= b && b > c && c > d && d > a
|
||||
// adcb;
|
||||
return permute<X1, X0, X3, X2>(permute128<X1, X0>(bcda, bcda));
|
||||
case 0x8: // a > b && b <= c && c <= d && d <= a
|
||||
return bcda;
|
||||
case 0x9: // a > b && b <= c && c <= d && d > a
|
||||
// b[ac]d;
|
||||
case 0xa: // a > b && b <= c && c > d && d <= a
|
||||
// [ac] > [bd]
|
||||
case 0xb: // a > b && b <= c && c > d && d > a
|
||||
// badc;
|
||||
return permute128<X1, X0>(dcba);
|
||||
case 0xc: // a > b && b > c && c <= d && d <= a
|
||||
// c[bd]a;
|
||||
case 0xd: // a > b && b > c && c <= d && d > a
|
||||
// cbad;
|
||||
return permute<X1, X0, X3, X2>(bcda);
|
||||
case 0xe: // a > b && b > c && c > d && d <= a
|
||||
return dcba;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
} // namespace Detail
|
||||
} // namespace Vc
|
||||
|
||||
// vim: foldmethod=marker
|
||||
202
obj-x86_64-linux-gnu/3rdparty/Vc/sse_sorthelper_AVX.cpp
vendored
Normal file
202
obj-x86_64-linux-gnu/3rdparty/Vc/sse_sorthelper_AVX.cpp
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2009-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
#include <Vc/sse/vector.h>
|
||||
#include <Vc/sse/macros.h>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
template <>
|
||||
Vc_CONST SSE::short_v sorted<CurrentImplementation::current()>(SSE::short_v x_)
|
||||
{
|
||||
__m128i lo, hi, y, x = x_.data();
|
||||
// sort pairs
|
||||
y = Mem::permute<X1, X0, X3, X2, X5, X4, X7, X6>(x);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, hi);
|
||||
|
||||
// merge left and right quads
|
||||
y = Mem::permute<X3, X2, X1, X0, X7, X6, X5, X4>(x);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
x = SSE::blend_epi16<0xcc>(lo, hi);
|
||||
y = _mm_srli_si128(x, 2);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, _mm_slli_si128(hi, 2));
|
||||
|
||||
// merge quads into octs
|
||||
y = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
y = _mm_shufflelo_epi16(y, _MM_SHUFFLE(0, 1, 2, 3));
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
|
||||
return _mm_unpacklo_epi16(lo, hi);
|
||||
}
|
||||
|
||||
template <>
|
||||
Vc_CONST SSE::ushort_v sorted<CurrentImplementation::current()>(SSE::ushort_v x_)
|
||||
{
|
||||
__m128i lo, hi, y, x = x_.data();
|
||||
// sort pairs
|
||||
y = Mem::permute<X1, X0, X3, X2, X5, X4, X7, X6>(x);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, hi);
|
||||
|
||||
// merge left and right quads
|
||||
y = Mem::permute<X3, X2, X1, X0, X7, X6, X5, X4>(x);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
x = SSE::blend_epi16<0xcc>(lo, hi);
|
||||
y = _mm_srli_si128(x, 2);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, _mm_slli_si128(hi, 2));
|
||||
|
||||
// merge quads into octs
|
||||
y = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
y = _mm_shufflelo_epi16(y, _MM_SHUFFLE(0, 1, 2, 3));
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
|
||||
return _mm_unpacklo_epi16(lo, hi);
|
||||
}
|
||||
|
||||
template <> Vc_CONST SSE::int_v sorted<CurrentImplementation::current()>(SSE::int_v x_)
|
||||
{
|
||||
__m128i x = x_.data();
|
||||
/*
|
||||
// in 16,67% of the cases the merge can be replaced by an append
|
||||
|
||||
// x = [a b c d]
|
||||
// y = [c d a b]
|
||||
__m128i y = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
__m128i l = SSE::min_epi32(x, y); // min[ac bd ac bd]
|
||||
__m128i h = SSE::max_epi32(x, y); // max[ac bd ac bd]
|
||||
if (IS_UNLIKELY(_mm_cvtsi128_si32(h) <= l[1])) { // l[0] < h[0] < l[1] < h[1]
|
||||
return _mm_unpacklo_epi32(l, h);
|
||||
}
|
||||
// h[0] > l[1]
|
||||
*/
|
||||
|
||||
// sort pairs
|
||||
__m128i y = _mm_shuffle_epi32(x, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128i l = SSE::min_epi32(x, y);
|
||||
__m128i h = SSE::max_epi32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi32(h, l);
|
||||
|
||||
// sort quads
|
||||
l = SSE::min_epi32(x, y);
|
||||
h = SSE::max_epi32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi64(x, x);
|
||||
|
||||
l = SSE::min_epi32(x, y);
|
||||
h = SSE::max_epi32(x, y);
|
||||
return _mm_unpacklo_epi32(l, h);
|
||||
}
|
||||
|
||||
template <> Vc_CONST SSE::uint_v sorted<CurrentImplementation::current()>(SSE::uint_v x_)
|
||||
{
|
||||
__m128i x = x_.data();
|
||||
__m128i y = _mm_shuffle_epi32(x, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128i l = SSE::min_epu32(x, y);
|
||||
__m128i h = SSE::max_epu32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi32(h, l);
|
||||
|
||||
// sort quads
|
||||
l = SSE::min_epu32(x, y);
|
||||
h = SSE::max_epu32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi64(x, x);
|
||||
|
||||
l = SSE::min_epu32(x, y);
|
||||
h = SSE::max_epu32(x, y);
|
||||
return _mm_unpacklo_epi32(l, h);
|
||||
}
|
||||
|
||||
template <>
|
||||
Vc_CONST SSE::float_v sorted<CurrentImplementation::current()>(SSE::float_v x_)
|
||||
{
|
||||
__m128 x = x_.data();
|
||||
__m128 y = _mm_shuffle_ps(x, x, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128 l = _mm_min_ps(x, y);
|
||||
__m128 h = _mm_max_ps(x, y);
|
||||
x = _mm_unpacklo_ps(l, h);
|
||||
y = _mm_unpackhi_ps(h, l);
|
||||
|
||||
l = _mm_min_ps(x, y);
|
||||
h = _mm_max_ps(x, y);
|
||||
x = _mm_unpacklo_ps(l, h);
|
||||
y = _mm_movehl_ps(x, x);
|
||||
|
||||
l = _mm_min_ps(x, y);
|
||||
h = _mm_max_ps(x, y);
|
||||
return _mm_unpacklo_ps(l, h);
|
||||
// X __m128 k = _mm_cmpgt_ps(x, y);
|
||||
// X k = _mm_shuffle_ps(k, k, _MM_SHUFFLE(2, 2, 0, 0));
|
||||
// X x = SSE::blendv_ps(x, y, k);
|
||||
// X y = _mm_shuffle_ps(x, x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
// X k = _mm_cmpgt_ps(x, y);
|
||||
// X k = _mm_shuffle_ps(k, k, _MM_SHUFFLE(1, 0, 1, 0));
|
||||
// X x = SSE::blendv_ps(x, y, k);
|
||||
// X y = _mm_shuffle_ps(x, x, _MM_SHUFFLE(3, 1, 2, 0));
|
||||
// X k = _mm_cmpgt_ps(x, y);
|
||||
// X k = _mm_shuffle_ps(k, k, _MM_SHUFFLE(0, 1, 1, 0));
|
||||
// X return SSE::blendv_ps(x, y, k);
|
||||
}
|
||||
|
||||
} // namespace Detail
|
||||
} // namespace Vc
|
||||
|
||||
// vim: foldmethod=marker
|
||||
202
obj-x86_64-linux-gnu/3rdparty/Vc/sse_sorthelper_AVX2+FMA+BMI2.cpp
vendored
Normal file
202
obj-x86_64-linux-gnu/3rdparty/Vc/sse_sorthelper_AVX2+FMA+BMI2.cpp
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2009-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
#include <Vc/sse/vector.h>
|
||||
#include <Vc/sse/macros.h>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
template <>
|
||||
Vc_CONST SSE::short_v sorted<CurrentImplementation::current()>(SSE::short_v x_)
|
||||
{
|
||||
__m128i lo, hi, y, x = x_.data();
|
||||
// sort pairs
|
||||
y = Mem::permute<X1, X0, X3, X2, X5, X4, X7, X6>(x);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, hi);
|
||||
|
||||
// merge left and right quads
|
||||
y = Mem::permute<X3, X2, X1, X0, X7, X6, X5, X4>(x);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
x = SSE::blend_epi16<0xcc>(lo, hi);
|
||||
y = _mm_srli_si128(x, 2);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, _mm_slli_si128(hi, 2));
|
||||
|
||||
// merge quads into octs
|
||||
y = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
y = _mm_shufflelo_epi16(y, _MM_SHUFFLE(0, 1, 2, 3));
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
|
||||
return _mm_unpacklo_epi16(lo, hi);
|
||||
}
|
||||
|
||||
template <>
|
||||
Vc_CONST SSE::ushort_v sorted<CurrentImplementation::current()>(SSE::ushort_v x_)
|
||||
{
|
||||
__m128i lo, hi, y, x = x_.data();
|
||||
// sort pairs
|
||||
y = Mem::permute<X1, X0, X3, X2, X5, X4, X7, X6>(x);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, hi);
|
||||
|
||||
// merge left and right quads
|
||||
y = Mem::permute<X3, X2, X1, X0, X7, X6, X5, X4>(x);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
x = SSE::blend_epi16<0xcc>(lo, hi);
|
||||
y = _mm_srli_si128(x, 2);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, _mm_slli_si128(hi, 2));
|
||||
|
||||
// merge quads into octs
|
||||
y = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
y = _mm_shufflelo_epi16(y, _MM_SHUFFLE(0, 1, 2, 3));
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
|
||||
return _mm_unpacklo_epi16(lo, hi);
|
||||
}
|
||||
|
||||
template <> Vc_CONST SSE::int_v sorted<CurrentImplementation::current()>(SSE::int_v x_)
|
||||
{
|
||||
__m128i x = x_.data();
|
||||
/*
|
||||
// in 16,67% of the cases the merge can be replaced by an append
|
||||
|
||||
// x = [a b c d]
|
||||
// y = [c d a b]
|
||||
__m128i y = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
__m128i l = SSE::min_epi32(x, y); // min[ac bd ac bd]
|
||||
__m128i h = SSE::max_epi32(x, y); // max[ac bd ac bd]
|
||||
if (IS_UNLIKELY(_mm_cvtsi128_si32(h) <= l[1])) { // l[0] < h[0] < l[1] < h[1]
|
||||
return _mm_unpacklo_epi32(l, h);
|
||||
}
|
||||
// h[0] > l[1]
|
||||
*/
|
||||
|
||||
// sort pairs
|
||||
__m128i y = _mm_shuffle_epi32(x, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128i l = SSE::min_epi32(x, y);
|
||||
__m128i h = SSE::max_epi32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi32(h, l);
|
||||
|
||||
// sort quads
|
||||
l = SSE::min_epi32(x, y);
|
||||
h = SSE::max_epi32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi64(x, x);
|
||||
|
||||
l = SSE::min_epi32(x, y);
|
||||
h = SSE::max_epi32(x, y);
|
||||
return _mm_unpacklo_epi32(l, h);
|
||||
}
|
||||
|
||||
template <> Vc_CONST SSE::uint_v sorted<CurrentImplementation::current()>(SSE::uint_v x_)
|
||||
{
|
||||
__m128i x = x_.data();
|
||||
__m128i y = _mm_shuffle_epi32(x, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128i l = SSE::min_epu32(x, y);
|
||||
__m128i h = SSE::max_epu32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi32(h, l);
|
||||
|
||||
// sort quads
|
||||
l = SSE::min_epu32(x, y);
|
||||
h = SSE::max_epu32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi64(x, x);
|
||||
|
||||
l = SSE::min_epu32(x, y);
|
||||
h = SSE::max_epu32(x, y);
|
||||
return _mm_unpacklo_epi32(l, h);
|
||||
}
|
||||
|
||||
template <>
|
||||
Vc_CONST SSE::float_v sorted<CurrentImplementation::current()>(SSE::float_v x_)
|
||||
{
|
||||
__m128 x = x_.data();
|
||||
__m128 y = _mm_shuffle_ps(x, x, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128 l = _mm_min_ps(x, y);
|
||||
__m128 h = _mm_max_ps(x, y);
|
||||
x = _mm_unpacklo_ps(l, h);
|
||||
y = _mm_unpackhi_ps(h, l);
|
||||
|
||||
l = _mm_min_ps(x, y);
|
||||
h = _mm_max_ps(x, y);
|
||||
x = _mm_unpacklo_ps(l, h);
|
||||
y = _mm_movehl_ps(x, x);
|
||||
|
||||
l = _mm_min_ps(x, y);
|
||||
h = _mm_max_ps(x, y);
|
||||
return _mm_unpacklo_ps(l, h);
|
||||
// X __m128 k = _mm_cmpgt_ps(x, y);
|
||||
// X k = _mm_shuffle_ps(k, k, _MM_SHUFFLE(2, 2, 0, 0));
|
||||
// X x = SSE::blendv_ps(x, y, k);
|
||||
// X y = _mm_shuffle_ps(x, x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
// X k = _mm_cmpgt_ps(x, y);
|
||||
// X k = _mm_shuffle_ps(k, k, _MM_SHUFFLE(1, 0, 1, 0));
|
||||
// X x = SSE::blendv_ps(x, y, k);
|
||||
// X y = _mm_shuffle_ps(x, x, _MM_SHUFFLE(3, 1, 2, 0));
|
||||
// X k = _mm_cmpgt_ps(x, y);
|
||||
// X k = _mm_shuffle_ps(k, k, _MM_SHUFFLE(0, 1, 1, 0));
|
||||
// X return SSE::blendv_ps(x, y, k);
|
||||
}
|
||||
|
||||
} // namespace Detail
|
||||
} // namespace Vc
|
||||
|
||||
// vim: foldmethod=marker
|
||||
202
obj-x86_64-linux-gnu/3rdparty/Vc/sse_sorthelper_SSE2.cpp
vendored
Normal file
202
obj-x86_64-linux-gnu/3rdparty/Vc/sse_sorthelper_SSE2.cpp
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2009-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
#include <Vc/sse/vector.h>
|
||||
#include <Vc/sse/macros.h>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
template <>
|
||||
Vc_CONST SSE::short_v sorted<CurrentImplementation::current()>(SSE::short_v x_)
|
||||
{
|
||||
__m128i lo, hi, y, x = x_.data();
|
||||
// sort pairs
|
||||
y = Mem::permute<X1, X0, X3, X2, X5, X4, X7, X6>(x);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, hi);
|
||||
|
||||
// merge left and right quads
|
||||
y = Mem::permute<X3, X2, X1, X0, X7, X6, X5, X4>(x);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
x = SSE::blend_epi16<0xcc>(lo, hi);
|
||||
y = _mm_srli_si128(x, 2);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, _mm_slli_si128(hi, 2));
|
||||
|
||||
// merge quads into octs
|
||||
y = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
y = _mm_shufflelo_epi16(y, _MM_SHUFFLE(0, 1, 2, 3));
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
|
||||
return _mm_unpacklo_epi16(lo, hi);
|
||||
}
|
||||
|
||||
template <>
|
||||
Vc_CONST SSE::ushort_v sorted<CurrentImplementation::current()>(SSE::ushort_v x_)
|
||||
{
|
||||
__m128i lo, hi, y, x = x_.data();
|
||||
// sort pairs
|
||||
y = Mem::permute<X1, X0, X3, X2, X5, X4, X7, X6>(x);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, hi);
|
||||
|
||||
// merge left and right quads
|
||||
y = Mem::permute<X3, X2, X1, X0, X7, X6, X5, X4>(x);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
x = SSE::blend_epi16<0xcc>(lo, hi);
|
||||
y = _mm_srli_si128(x, 2);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, _mm_slli_si128(hi, 2));
|
||||
|
||||
// merge quads into octs
|
||||
y = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
y = _mm_shufflelo_epi16(y, _MM_SHUFFLE(0, 1, 2, 3));
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
|
||||
return _mm_unpacklo_epi16(lo, hi);
|
||||
}
|
||||
|
||||
template <> Vc_CONST SSE::int_v sorted<CurrentImplementation::current()>(SSE::int_v x_)
|
||||
{
|
||||
__m128i x = x_.data();
|
||||
/*
|
||||
// in 16,67% of the cases the merge can be replaced by an append
|
||||
|
||||
// x = [a b c d]
|
||||
// y = [c d a b]
|
||||
__m128i y = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
__m128i l = SSE::min_epi32(x, y); // min[ac bd ac bd]
|
||||
__m128i h = SSE::max_epi32(x, y); // max[ac bd ac bd]
|
||||
if (IS_UNLIKELY(_mm_cvtsi128_si32(h) <= l[1])) { // l[0] < h[0] < l[1] < h[1]
|
||||
return _mm_unpacklo_epi32(l, h);
|
||||
}
|
||||
// h[0] > l[1]
|
||||
*/
|
||||
|
||||
// sort pairs
|
||||
__m128i y = _mm_shuffle_epi32(x, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128i l = SSE::min_epi32(x, y);
|
||||
__m128i h = SSE::max_epi32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi32(h, l);
|
||||
|
||||
// sort quads
|
||||
l = SSE::min_epi32(x, y);
|
||||
h = SSE::max_epi32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi64(x, x);
|
||||
|
||||
l = SSE::min_epi32(x, y);
|
||||
h = SSE::max_epi32(x, y);
|
||||
return _mm_unpacklo_epi32(l, h);
|
||||
}
|
||||
|
||||
template <> Vc_CONST SSE::uint_v sorted<CurrentImplementation::current()>(SSE::uint_v x_)
|
||||
{
|
||||
__m128i x = x_.data();
|
||||
__m128i y = _mm_shuffle_epi32(x, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128i l = SSE::min_epu32(x, y);
|
||||
__m128i h = SSE::max_epu32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi32(h, l);
|
||||
|
||||
// sort quads
|
||||
l = SSE::min_epu32(x, y);
|
||||
h = SSE::max_epu32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi64(x, x);
|
||||
|
||||
l = SSE::min_epu32(x, y);
|
||||
h = SSE::max_epu32(x, y);
|
||||
return _mm_unpacklo_epi32(l, h);
|
||||
}
|
||||
|
||||
template <>
|
||||
Vc_CONST SSE::float_v sorted<CurrentImplementation::current()>(SSE::float_v x_)
|
||||
{
|
||||
__m128 x = x_.data();
|
||||
__m128 y = _mm_shuffle_ps(x, x, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128 l = _mm_min_ps(x, y);
|
||||
__m128 h = _mm_max_ps(x, y);
|
||||
x = _mm_unpacklo_ps(l, h);
|
||||
y = _mm_unpackhi_ps(h, l);
|
||||
|
||||
l = _mm_min_ps(x, y);
|
||||
h = _mm_max_ps(x, y);
|
||||
x = _mm_unpacklo_ps(l, h);
|
||||
y = _mm_movehl_ps(x, x);
|
||||
|
||||
l = _mm_min_ps(x, y);
|
||||
h = _mm_max_ps(x, y);
|
||||
return _mm_unpacklo_ps(l, h);
|
||||
// X __m128 k = _mm_cmpgt_ps(x, y);
|
||||
// X k = _mm_shuffle_ps(k, k, _MM_SHUFFLE(2, 2, 0, 0));
|
||||
// X x = SSE::blendv_ps(x, y, k);
|
||||
// X y = _mm_shuffle_ps(x, x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
// X k = _mm_cmpgt_ps(x, y);
|
||||
// X k = _mm_shuffle_ps(k, k, _MM_SHUFFLE(1, 0, 1, 0));
|
||||
// X x = SSE::blendv_ps(x, y, k);
|
||||
// X y = _mm_shuffle_ps(x, x, _MM_SHUFFLE(3, 1, 2, 0));
|
||||
// X k = _mm_cmpgt_ps(x, y);
|
||||
// X k = _mm_shuffle_ps(k, k, _MM_SHUFFLE(0, 1, 1, 0));
|
||||
// X return SSE::blendv_ps(x, y, k);
|
||||
}
|
||||
|
||||
} // namespace Detail
|
||||
} // namespace Vc
|
||||
|
||||
// vim: foldmethod=marker
|
||||
202
obj-x86_64-linux-gnu/3rdparty/Vc/sse_sorthelper_SSE4_1.cpp
vendored
Normal file
202
obj-x86_64-linux-gnu/3rdparty/Vc/sse_sorthelper_SSE4_1.cpp
vendored
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2009-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
#include <Vc/sse/vector.h>
|
||||
#include <Vc/sse/macros.h>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Detail
|
||||
{
|
||||
template <>
|
||||
Vc_CONST SSE::short_v sorted<CurrentImplementation::current()>(SSE::short_v x_)
|
||||
{
|
||||
__m128i lo, hi, y, x = x_.data();
|
||||
// sort pairs
|
||||
y = Mem::permute<X1, X0, X3, X2, X5, X4, X7, X6>(x);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, hi);
|
||||
|
||||
// merge left and right quads
|
||||
y = Mem::permute<X3, X2, X1, X0, X7, X6, X5, X4>(x);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
x = SSE::blend_epi16<0xcc>(lo, hi);
|
||||
y = _mm_srli_si128(x, 2);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, _mm_slli_si128(hi, 2));
|
||||
|
||||
// merge quads into octs
|
||||
y = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
y = _mm_shufflelo_epi16(y, _MM_SHUFFLE(0, 1, 2, 3));
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = _mm_min_epi16(x, y);
|
||||
hi = _mm_max_epi16(x, y);
|
||||
|
||||
return _mm_unpacklo_epi16(lo, hi);
|
||||
}
|
||||
|
||||
template <>
|
||||
Vc_CONST SSE::ushort_v sorted<CurrentImplementation::current()>(SSE::ushort_v x_)
|
||||
{
|
||||
__m128i lo, hi, y, x = x_.data();
|
||||
// sort pairs
|
||||
y = Mem::permute<X1, X0, X3, X2, X5, X4, X7, X6>(x);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, hi);
|
||||
|
||||
// merge left and right quads
|
||||
y = Mem::permute<X3, X2, X1, X0, X7, X6, X5, X4>(x);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
x = SSE::blend_epi16<0xcc>(lo, hi);
|
||||
y = _mm_srli_si128(x, 2);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
x = SSE::blend_epi16<0xaa>(lo, _mm_slli_si128(hi, 2));
|
||||
|
||||
// merge quads into octs
|
||||
y = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
y = _mm_shufflelo_epi16(y, _MM_SHUFFLE(0, 1, 2, 3));
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
|
||||
x = _mm_unpacklo_epi16(lo, hi);
|
||||
y = _mm_srli_si128(x, 8);
|
||||
lo = SSE::min_epu16(x, y);
|
||||
hi = SSE::max_epu16(x, y);
|
||||
|
||||
return _mm_unpacklo_epi16(lo, hi);
|
||||
}
|
||||
|
||||
template <> Vc_CONST SSE::int_v sorted<CurrentImplementation::current()>(SSE::int_v x_)
|
||||
{
|
||||
__m128i x = x_.data();
|
||||
/*
|
||||
// in 16,67% of the cases the merge can be replaced by an append
|
||||
|
||||
// x = [a b c d]
|
||||
// y = [c d a b]
|
||||
__m128i y = _mm_shuffle_epi32(x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
__m128i l = SSE::min_epi32(x, y); // min[ac bd ac bd]
|
||||
__m128i h = SSE::max_epi32(x, y); // max[ac bd ac bd]
|
||||
if (IS_UNLIKELY(_mm_cvtsi128_si32(h) <= l[1])) { // l[0] < h[0] < l[1] < h[1]
|
||||
return _mm_unpacklo_epi32(l, h);
|
||||
}
|
||||
// h[0] > l[1]
|
||||
*/
|
||||
|
||||
// sort pairs
|
||||
__m128i y = _mm_shuffle_epi32(x, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128i l = SSE::min_epi32(x, y);
|
||||
__m128i h = SSE::max_epi32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi32(h, l);
|
||||
|
||||
// sort quads
|
||||
l = SSE::min_epi32(x, y);
|
||||
h = SSE::max_epi32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi64(x, x);
|
||||
|
||||
l = SSE::min_epi32(x, y);
|
||||
h = SSE::max_epi32(x, y);
|
||||
return _mm_unpacklo_epi32(l, h);
|
||||
}
|
||||
|
||||
template <> Vc_CONST SSE::uint_v sorted<CurrentImplementation::current()>(SSE::uint_v x_)
|
||||
{
|
||||
__m128i x = x_.data();
|
||||
__m128i y = _mm_shuffle_epi32(x, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128i l = SSE::min_epu32(x, y);
|
||||
__m128i h = SSE::max_epu32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi32(h, l);
|
||||
|
||||
// sort quads
|
||||
l = SSE::min_epu32(x, y);
|
||||
h = SSE::max_epu32(x, y);
|
||||
x = _mm_unpacklo_epi32(l, h);
|
||||
y = _mm_unpackhi_epi64(x, x);
|
||||
|
||||
l = SSE::min_epu32(x, y);
|
||||
h = SSE::max_epu32(x, y);
|
||||
return _mm_unpacklo_epi32(l, h);
|
||||
}
|
||||
|
||||
template <>
|
||||
Vc_CONST SSE::float_v sorted<CurrentImplementation::current()>(SSE::float_v x_)
|
||||
{
|
||||
__m128 x = x_.data();
|
||||
__m128 y = _mm_shuffle_ps(x, x, _MM_SHUFFLE(2, 3, 0, 1));
|
||||
__m128 l = _mm_min_ps(x, y);
|
||||
__m128 h = _mm_max_ps(x, y);
|
||||
x = _mm_unpacklo_ps(l, h);
|
||||
y = _mm_unpackhi_ps(h, l);
|
||||
|
||||
l = _mm_min_ps(x, y);
|
||||
h = _mm_max_ps(x, y);
|
||||
x = _mm_unpacklo_ps(l, h);
|
||||
y = _mm_movehl_ps(x, x);
|
||||
|
||||
l = _mm_min_ps(x, y);
|
||||
h = _mm_max_ps(x, y);
|
||||
return _mm_unpacklo_ps(l, h);
|
||||
// X __m128 k = _mm_cmpgt_ps(x, y);
|
||||
// X k = _mm_shuffle_ps(k, k, _MM_SHUFFLE(2, 2, 0, 0));
|
||||
// X x = SSE::blendv_ps(x, y, k);
|
||||
// X y = _mm_shuffle_ps(x, x, _MM_SHUFFLE(1, 0, 3, 2));
|
||||
// X k = _mm_cmpgt_ps(x, y);
|
||||
// X k = _mm_shuffle_ps(k, k, _MM_SHUFFLE(1, 0, 1, 0));
|
||||
// X x = SSE::blendv_ps(x, y, k);
|
||||
// X y = _mm_shuffle_ps(x, x, _MM_SHUFFLE(3, 1, 2, 0));
|
||||
// X k = _mm_cmpgt_ps(x, y);
|
||||
// X k = _mm_shuffle_ps(k, k, _MM_SHUFFLE(0, 1, 1, 0));
|
||||
// X return SSE::blendv_ps(x, y, k);
|
||||
}
|
||||
|
||||
} // namespace Detail
|
||||
} // namespace Vc
|
||||
|
||||
// vim: foldmethod=marker
|
||||
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_AVX+FMA.cpp
vendored
Normal file
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_AVX+FMA.cpp
vendored
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2012-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
// enable bit operators for easier portable bit manipulation on floats
|
||||
#define Vc_ENABLE_FLOAT_BIT_OPERATORS 1
|
||||
|
||||
#include <Vc/vector.h>
|
||||
#if defined(Vc_IMPL_SSE) || defined(Vc_IMPL_AVX)
|
||||
#include <Vc/common/macros.h>
|
||||
//#include <Vc/IO>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Vc::Vector;
|
||||
template <typename T, typename Abi>
|
||||
using Const = typename std::conditional<std::is_same<Abi, VectorAbi::Avx>::value,
|
||||
AVX::Const<T>, SSE::Const<T>>::type;
|
||||
|
||||
template <typename V>
|
||||
using best_int_v_for =
|
||||
typename std::conditional<(V::size() <= Vector<int, VectorAbi::Best<int>>::size()),
|
||||
Vector<int, VectorAbi::Best<int>>,
|
||||
SimdArray<int, V::size()>>::type;
|
||||
template <typename Abi> using float_int_v = best_int_v_for<Vector<float, Abi>>;
|
||||
template <typename Abi> using double_int_v = best_int_v_for<Vector<double, Abi>>;
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> cosSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant< 1, 0x500000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x360800, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x2AAAAB, -5>(); // 1/4!
|
||||
return y * (x2 * x2) - .5f * x2 + 1.f;
|
||||
// alternative (appears neither faster nor more precise):
|
||||
// return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> cosSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant< 1, 0xAC00000000000, -45>(); // 1/16!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x9394000000000, -37>(); // -1/14!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1EED8C0000000, -29>(); // 1/12!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x27E4FB7400000, -22>(); // -1/10!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0xA01A01A018000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x6C16C16C16C00, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x5555555555554, -5>(); // 1/4!
|
||||
return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> sinSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant<-1, 0x4E6000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x088880, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x2AAAAB, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> sinSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
// x = [0, 0.7854 = pi/4]
|
||||
// x² = [0, 0.6169 = pi²/8]
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant<-1, 0xACF0000000000, -41>(); // -1/15!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x6124400000000, -33>(); // 1/13!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xAE64567000000, -26>(); // -1/11!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x71DE3A5540000, -19>(); // 1/9!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xA01A01A01A000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1111111111110, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x5555555555555, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
/**\internal
|
||||
* Fold \p x into [-¼π, ¼π] and remember the quadrant it came from:
|
||||
* quadrant 0: [-¼π, ¼π]
|
||||
* quadrant 1: [ ¼π, ¾π]
|
||||
* quadrant 2: [ ¾π, 1¼π]
|
||||
* quadrant 3: [1¼π, 1¾π]
|
||||
*
|
||||
* The algorithm determines `y` as the multiple `x - y * ¼π = [-¼π, ¼π]`. Using a bitmask,
|
||||
* `y` is reduced to `quadrant`. `y` can be calculated as
|
||||
* ```
|
||||
* y = trunc(x / ¼π);
|
||||
* y += fmod(y, 2);
|
||||
* ```
|
||||
* This can be simplified by moving the (implicit) division by 2 into the truncation
|
||||
* expression. The `+= fmod` effect can the be achieved by using rounding instead of
|
||||
* truncation:
|
||||
* `y = round(x / ½π) * 2`.
|
||||
* If precision allows, `2/π * x` is better (faster).
|
||||
*/
|
||||
template <class T, class Abi> struct folded {
|
||||
Vector<T, Abi> x, quadrant;
|
||||
};
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<float, Abi> foldInput(const Vector<float, Abi> &x)
|
||||
{
|
||||
using V = Vector<float, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<float, Abi>;
|
||||
|
||||
folded<float, Abi> r;
|
||||
r.x = abs(x);
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < C::_pi_4()))) {
|
||||
r.quadrant = 0;
|
||||
} else if (Vc_IS_LIKELY(all_of(r.x < 33 * C::_pi_4()))) {
|
||||
const float _2_over_pi = Vc::Detail::floatConstant<1, 0x22F983, -1>(); // ½π
|
||||
V y = round(r.x * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // y mod 4
|
||||
r.x -= y * Vc::Detail::floatConstant<1, 0x490fe0, 0>(); // pi/2
|
||||
r.x -= y * Vc::Detail::floatConstant<-1, 0x2bbbd3, -21>(); // pi/2 remainder
|
||||
} else {
|
||||
typedef SimdArray<double, V::size()> VD;
|
||||
VD xd = simd_cast<VD>(abs(r.x));
|
||||
constexpr double _2_over_pi =
|
||||
Vc::Detail::doubleConstant<1, 0x45F306DC9C883, -1>();
|
||||
VD y = round(xd * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // = y mod 4
|
||||
constexpr double pi_over_2 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, 0>();
|
||||
r.x = simd_cast<V>(xd - y * pi_over_2);
|
||||
}
|
||||
//std::cout << std::hexfloat << r.x << ' ' << r.quadrant << std::defaultfloat << '\n';
|
||||
return r;
|
||||
|
||||
/*
|
||||
More alternatives to avoid double precision calculation follow. Their main problem
|
||||
is unfortunate rounding on intermediate values of x (after subtraction):
|
||||
|
||||
if (all_of(y <= 0x1000)) {
|
||||
// 0 <= x <= 0x1.921fb6p12
|
||||
// π/4 - 12 bits precision (12 zero bits):
|
||||
//const auto _pi_4_1 = Vc::Detail::floatConstant< 1, 0x491000, -1>();
|
||||
//const auto _pi_4_2 = Vc::Detail::floatConstant<-1, 0x157000, -19>();
|
||||
//const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x6F4B9F, -32>();
|
||||
x -= y * C::_pi_4_hi();
|
||||
x -= y * C::_pi_4_rem1();
|
||||
x -= y * C::_pi_4_rem2();
|
||||
} else { //if (all_of(y <= 0x10000)) {
|
||||
// consider y = 0x1.fffep+16 / x = 0x1.921e24p+16
|
||||
// critical input: 0x1.921fb6p+12 <= x <= 0x1.921fb6p+16
|
||||
// π/4 - 8 bits precision (16 zero bits):
|
||||
const auto _pi_4_1 = Vc::Detail::floatConstant<+1, 0x490000, -1>();
|
||||
const auto _pi_4_2 = Vc::Detail::floatConstant<+1, 0x7E0000, -13>();
|
||||
const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x2C0000, -22>();
|
||||
const auto _pi_4_4 = Vc::Detail::floatConstant<+1, 0x085A31, -31>();
|
||||
x -= y * _pi_4_1; // (x -= 0x1.91fe6ep+16) -> 0x1.fb6000p+4
|
||||
x -= y * _pi_4_2; // (x -= 0x1.fbfe04p+4) -> -0x1.3c0800p-6
|
||||
x -= y * _pi_4_3; // (x -= 0x1.57fea8p-5) -> 0x1.bf5a80p-9
|
||||
x -= y * _pi_4_4; // (x -= 0x1.10b352p-14) -> 0x1.b6e4e6p-9
|
||||
x.setQnan(y > 0x10000);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<double, Abi> foldInput(const Vector<double, Abi> &x)
|
||||
{
|
||||
using V = Vector<double, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<double, Abi> ;
|
||||
|
||||
folded<double, Abi> r;
|
||||
r.x = abs(x);
|
||||
constexpr double pi_over_4 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, -1>();
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < pi_over_4))) {
|
||||
r.quadrant = 0;
|
||||
return r;
|
||||
}
|
||||
const V y = round(r.x / (2 * pi_over_4));
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3);
|
||||
|
||||
if (Vc_IS_LIKELY(all_of(r.x < 1025 * pi_over_4))) {
|
||||
// x - y * pi/2, y uses no more than 11 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x921FB54443000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<-1, 0x73DCB3B39A000, -43>();
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x45C06E0E68948, -86>();
|
||||
} else if (Vc_IS_LIKELY(all_of(y <= Vc::Detail::doubleConstant<1, 0, 30>()))) {
|
||||
// x - y * pi/2, y uses no more than 29 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x921FB40000000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x4442D00000000, -24>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x8469898CC5170, -48>();
|
||||
} else {
|
||||
// x - y * pi/2, y may require all mantissa bits
|
||||
const V y_hi = y & C::highMask(26);
|
||||
const V y_lo = y - y_hi;
|
||||
const auto _pi_2_1 = Vc::Detail::doubleConstant<1, 0x921FB50000000, 0>();
|
||||
const auto _pi_2_2 = Vc::Detail::doubleConstant<1, 0x110B460000000, -26>();
|
||||
const auto _pi_2_3 = Vc::Detail::doubleConstant<1, 0x1A62630000000, -54>();
|
||||
const auto _pi_2_4 = Vc::Detail::doubleConstant<1, 0x8A2E03707344A, -81>();
|
||||
/*
|
||||
std::cout << std::hexfloat;
|
||||
std::cout
|
||||
<< y_hi << '\n'
|
||||
<< y_lo << '\n'
|
||||
<< _pi_2_1 << '\n'
|
||||
<< _pi_2_2 << '\n'
|
||||
<< _pi_2_3 << '\n'
|
||||
<< y_hi * _pi_2_1 << '\n'
|
||||
<< y_hi * _pi_2_2 << '\n'
|
||||
<< y_hi * _pi_2_3 << '\n'
|
||||
<< y * _pi_2_4 << '\n'
|
||||
<< y_lo * _pi_2_1 << '\n'
|
||||
<< y_lo * _pi_2_2 << '\n'
|
||||
<< y_lo * _pi_2_3 << '\n';
|
||||
std::cout << std::defaultfloat;
|
||||
*/
|
||||
r.x = r.x
|
||||
- y_hi * _pi_2_1
|
||||
- max(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- min(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- max(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- min(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- max(y * _pi_2_4, y_lo * _pi_2_3)
|
||||
- min(y * _pi_2_4, y_lo * _pi_2_3);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr double signmask = -0.;
|
||||
constexpr float signmaskf = -0.f;
|
||||
} // anonymous namespace
|
||||
|
||||
/*
|
||||
* algorithm for sine and cosine:
|
||||
*
|
||||
* The result can be calculated with sine or cosine depending on the π/4 section the input is
|
||||
* in.
|
||||
* sine ≈ x + x³
|
||||
* cosine ≈ 1 - x²
|
||||
*
|
||||
* sine:
|
||||
* Map -x to x and invert the output
|
||||
* Extend precision of x - n * π/4 by calculating
|
||||
* ((x - n * p1) - n * p2) - n * p3 (p1 + p2 + p3 = π/4)
|
||||
*
|
||||
* Calculate Taylor series with tuned coefficients.
|
||||
* Fix sign.
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | sinSeries
|
||||
// 1 | cosSeries
|
||||
// 2 | sinSeries, sign flip
|
||||
// 3 | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 527449))) {
|
||||
return simd_cast<V>(sin(simd_cast<Vc::double_v, 0>(x)),
|
||||
sin(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | cosSeries, +
|
||||
// 1 | sinSeries, -
|
||||
// 2 | cosSeries, -
|
||||
// 3 | sinSeries, +
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
return simd_cast<V>(cos(simd_cast<Vc::double_v, 0>(x)),
|
||||
cos(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::double_v &x, Vc::double_v *s,
|
||||
Vc::double_v *c)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | cosine | sine
|
||||
// 0 | cosSeries, + | sinSeries
|
||||
// 1 | sinSeries, - | cosSeries
|
||||
// 2 | cosSeries, - | sinSeries, sign flip
|
||||
// 3 | sinSeries, + | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::float_v &x, Vc::float_v *s,
|
||||
Vc::float_v *c)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
Vc::double_v s0, s1, c0, c1;
|
||||
sincos(simd_cast<Vc::double_v, 0>(x), &s0, &c0);
|
||||
sincos(simd_cast<Vc::double_v, 1>(x), &s1, &c1);
|
||||
*s = simd_cast<V>(s0, s1);
|
||||
*c = simd_cast<V>(c0, c1);
|
||||
return;
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::asin(const Vc::float_v &_x)
|
||||
{
|
||||
typedef Vc::float_v V;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M &negative = _x < V::Zero();
|
||||
|
||||
const V &a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M &small = a < C::smallAsinInput();
|
||||
const M >_0_5 = a > C::_1_2();
|
||||
V x = a;
|
||||
V z = a * a;
|
||||
z(gt_0_5) = (V::One() - a) * C::_1_2();
|
||||
x(gt_0_5) = sqrt(z);
|
||||
z = ((((C::asinCoeff0(0) * z
|
||||
+ C::asinCoeff0(1)) * z
|
||||
+ C::asinCoeff0(2)) * z
|
||||
+ C::asinCoeff0(3)) * z
|
||||
+ C::asinCoeff0(4)) * z * x
|
||||
+ x;
|
||||
z(gt_0_5) = C::_pi_2() - (z + z);
|
||||
z(small) = a;
|
||||
z(negative) = -z;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::asin (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M negative = _x < V::Zero();
|
||||
|
||||
const V a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M small = a < C::smallAsinInput();
|
||||
const M large = a > C::largeAsinInput();
|
||||
|
||||
V zz = V::One() - a;
|
||||
const V r = (((C::asinCoeff0(0) * zz + C::asinCoeff0(1)) * zz + C::asinCoeff0(2)) * zz +
|
||||
C::asinCoeff0(3)) * zz + C::asinCoeff0(4);
|
||||
const V s = (((zz + C::asinCoeff1(0)) * zz + C::asinCoeff1(1)) * zz +
|
||||
C::asinCoeff1(2)) * zz + C::asinCoeff1(3);
|
||||
V sqrtzz = sqrt(zz + zz);
|
||||
V z = C::_pi_4() - sqrtzz;
|
||||
z -= sqrtzz * (zz * r / s) - C::_pi_2_rem();
|
||||
z += C::_pi_4();
|
||||
|
||||
V a2 = a * a;
|
||||
const V p = ((((C::asinCoeff2(0) * a2 + C::asinCoeff2(1)) * a2 + C::asinCoeff2(2)) * a2 +
|
||||
C::asinCoeff2(3)) * a2 + C::asinCoeff2(4)) * a2 + C::asinCoeff2(5);
|
||||
const V q = ((((a2 + C::asinCoeff3(0)) * a2 + C::asinCoeff3(1)) * a2 +
|
||||
C::asinCoeff3(2)) * a2 + C::asinCoeff3(3)) * a2 + C::asinCoeff3(4);
|
||||
z(!large) = a * (a2 * p / q) + a;
|
||||
|
||||
z(negative) = -z;
|
||||
z(small) = _x;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan(const Vc::float_v &_x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
V x = abs(_x);
|
||||
const M >_tan_3pi_8 = x > C::atanThrsHi();
|
||||
const M >_tan_pi_8 = x > C::atanThrsLo() && !gt_tan_3pi_8;
|
||||
V y = V::Zero();
|
||||
y(gt_tan_3pi_8) = C::_pi_2();
|
||||
y(gt_tan_pi_8) = C::_pi_4();
|
||||
x(gt_tan_3pi_8) = -V::One() / x;
|
||||
x(gt_tan_pi_8) = (x - V::One()) / (x + V::One());
|
||||
const V &x2 = x * x;
|
||||
y += (((C::atanP(0) * x2
|
||||
- C::atanP(1)) * x2
|
||||
+ C::atanP(2)) * x2
|
||||
- C::atanP(3)) * x2 * x
|
||||
+ x;
|
||||
y(_x < V::Zero()) = -y;
|
||||
y.setQnan(isnan(_x));
|
||||
return y;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
M sign = _x < V::Zero();
|
||||
V x = abs(_x);
|
||||
M finite = isfinite(_x);
|
||||
V ret = C::_pi_2();
|
||||
V y = V::Zero();
|
||||
const M large = x > C::atanThrsHi();
|
||||
const M gt_06 = x > C::atanThrsLo();
|
||||
V tmp = (x - V::One()) / (x + V::One());
|
||||
tmp(large) = -V::One() / x;
|
||||
x(gt_06) = tmp;
|
||||
y(gt_06) = C::_pi_4();
|
||||
y(large) = C::_pi_2();
|
||||
V z = x * x;
|
||||
const V p = (((C::atanP(0) * z + C::atanP(1)) * z + C::atanP(2)) * z + C::atanP(3)) * z + C::atanP(4);
|
||||
const V q = ((((z + C::atanQ(0)) * z + C::atanQ(1)) * z + C::atanQ(2)) * z + C::atanQ(3)) * z + C::atanQ(4);
|
||||
z = z * p / q;
|
||||
z = x * z + x;
|
||||
V morebits = C::_pi_2_rem();
|
||||
morebits(!large) *= C::_1_2();
|
||||
z(gt_06) += morebits;
|
||||
ret(finite) = y + z;
|
||||
ret(sign) = -ret;
|
||||
ret.setQnan(isnan(_x));
|
||||
return ret;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan2(const Vc::float_v &y,
|
||||
const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(C::_pi(), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan2 (const Vc::double_v &y, const Vc::double_v &x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(V(C::_pi()), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_AVX+XOP+FMA.cpp
vendored
Normal file
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_AVX+XOP+FMA.cpp
vendored
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2012-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
// enable bit operators for easier portable bit manipulation on floats
|
||||
#define Vc_ENABLE_FLOAT_BIT_OPERATORS 1
|
||||
|
||||
#include <Vc/vector.h>
|
||||
#if defined(Vc_IMPL_SSE) || defined(Vc_IMPL_AVX)
|
||||
#include <Vc/common/macros.h>
|
||||
//#include <Vc/IO>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Vc::Vector;
|
||||
template <typename T, typename Abi>
|
||||
using Const = typename std::conditional<std::is_same<Abi, VectorAbi::Avx>::value,
|
||||
AVX::Const<T>, SSE::Const<T>>::type;
|
||||
|
||||
template <typename V>
|
||||
using best_int_v_for =
|
||||
typename std::conditional<(V::size() <= Vector<int, VectorAbi::Best<int>>::size()),
|
||||
Vector<int, VectorAbi::Best<int>>,
|
||||
SimdArray<int, V::size()>>::type;
|
||||
template <typename Abi> using float_int_v = best_int_v_for<Vector<float, Abi>>;
|
||||
template <typename Abi> using double_int_v = best_int_v_for<Vector<double, Abi>>;
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> cosSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant< 1, 0x500000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x360800, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x2AAAAB, -5>(); // 1/4!
|
||||
return y * (x2 * x2) - .5f * x2 + 1.f;
|
||||
// alternative (appears neither faster nor more precise):
|
||||
// return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> cosSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant< 1, 0xAC00000000000, -45>(); // 1/16!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x9394000000000, -37>(); // -1/14!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1EED8C0000000, -29>(); // 1/12!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x27E4FB7400000, -22>(); // -1/10!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0xA01A01A018000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x6C16C16C16C00, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x5555555555554, -5>(); // 1/4!
|
||||
return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> sinSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant<-1, 0x4E6000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x088880, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x2AAAAB, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> sinSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
// x = [0, 0.7854 = pi/4]
|
||||
// x² = [0, 0.6169 = pi²/8]
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant<-1, 0xACF0000000000, -41>(); // -1/15!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x6124400000000, -33>(); // 1/13!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xAE64567000000, -26>(); // -1/11!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x71DE3A5540000, -19>(); // 1/9!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xA01A01A01A000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1111111111110, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x5555555555555, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
/**\internal
|
||||
* Fold \p x into [-¼π, ¼π] and remember the quadrant it came from:
|
||||
* quadrant 0: [-¼π, ¼π]
|
||||
* quadrant 1: [ ¼π, ¾π]
|
||||
* quadrant 2: [ ¾π, 1¼π]
|
||||
* quadrant 3: [1¼π, 1¾π]
|
||||
*
|
||||
* The algorithm determines `y` as the multiple `x - y * ¼π = [-¼π, ¼π]`. Using a bitmask,
|
||||
* `y` is reduced to `quadrant`. `y` can be calculated as
|
||||
* ```
|
||||
* y = trunc(x / ¼π);
|
||||
* y += fmod(y, 2);
|
||||
* ```
|
||||
* This can be simplified by moving the (implicit) division by 2 into the truncation
|
||||
* expression. The `+= fmod` effect can the be achieved by using rounding instead of
|
||||
* truncation:
|
||||
* `y = round(x / ½π) * 2`.
|
||||
* If precision allows, `2/π * x` is better (faster).
|
||||
*/
|
||||
template <class T, class Abi> struct folded {
|
||||
Vector<T, Abi> x, quadrant;
|
||||
};
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<float, Abi> foldInput(const Vector<float, Abi> &x)
|
||||
{
|
||||
using V = Vector<float, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<float, Abi>;
|
||||
|
||||
folded<float, Abi> r;
|
||||
r.x = abs(x);
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < C::_pi_4()))) {
|
||||
r.quadrant = 0;
|
||||
} else if (Vc_IS_LIKELY(all_of(r.x < 33 * C::_pi_4()))) {
|
||||
const float _2_over_pi = Vc::Detail::floatConstant<1, 0x22F983, -1>(); // ½π
|
||||
V y = round(r.x * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // y mod 4
|
||||
r.x -= y * Vc::Detail::floatConstant<1, 0x490fe0, 0>(); // pi/2
|
||||
r.x -= y * Vc::Detail::floatConstant<-1, 0x2bbbd3, -21>(); // pi/2 remainder
|
||||
} else {
|
||||
typedef SimdArray<double, V::size()> VD;
|
||||
VD xd = simd_cast<VD>(abs(r.x));
|
||||
constexpr double _2_over_pi =
|
||||
Vc::Detail::doubleConstant<1, 0x45F306DC9C883, -1>();
|
||||
VD y = round(xd * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // = y mod 4
|
||||
constexpr double pi_over_2 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, 0>();
|
||||
r.x = simd_cast<V>(xd - y * pi_over_2);
|
||||
}
|
||||
//std::cout << std::hexfloat << r.x << ' ' << r.quadrant << std::defaultfloat << '\n';
|
||||
return r;
|
||||
|
||||
/*
|
||||
More alternatives to avoid double precision calculation follow. Their main problem
|
||||
is unfortunate rounding on intermediate values of x (after subtraction):
|
||||
|
||||
if (all_of(y <= 0x1000)) {
|
||||
// 0 <= x <= 0x1.921fb6p12
|
||||
// π/4 - 12 bits precision (12 zero bits):
|
||||
//const auto _pi_4_1 = Vc::Detail::floatConstant< 1, 0x491000, -1>();
|
||||
//const auto _pi_4_2 = Vc::Detail::floatConstant<-1, 0x157000, -19>();
|
||||
//const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x6F4B9F, -32>();
|
||||
x -= y * C::_pi_4_hi();
|
||||
x -= y * C::_pi_4_rem1();
|
||||
x -= y * C::_pi_4_rem2();
|
||||
} else { //if (all_of(y <= 0x10000)) {
|
||||
// consider y = 0x1.fffep+16 / x = 0x1.921e24p+16
|
||||
// critical input: 0x1.921fb6p+12 <= x <= 0x1.921fb6p+16
|
||||
// π/4 - 8 bits precision (16 zero bits):
|
||||
const auto _pi_4_1 = Vc::Detail::floatConstant<+1, 0x490000, -1>();
|
||||
const auto _pi_4_2 = Vc::Detail::floatConstant<+1, 0x7E0000, -13>();
|
||||
const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x2C0000, -22>();
|
||||
const auto _pi_4_4 = Vc::Detail::floatConstant<+1, 0x085A31, -31>();
|
||||
x -= y * _pi_4_1; // (x -= 0x1.91fe6ep+16) -> 0x1.fb6000p+4
|
||||
x -= y * _pi_4_2; // (x -= 0x1.fbfe04p+4) -> -0x1.3c0800p-6
|
||||
x -= y * _pi_4_3; // (x -= 0x1.57fea8p-5) -> 0x1.bf5a80p-9
|
||||
x -= y * _pi_4_4; // (x -= 0x1.10b352p-14) -> 0x1.b6e4e6p-9
|
||||
x.setQnan(y > 0x10000);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<double, Abi> foldInput(const Vector<double, Abi> &x)
|
||||
{
|
||||
using V = Vector<double, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<double, Abi> ;
|
||||
|
||||
folded<double, Abi> r;
|
||||
r.x = abs(x);
|
||||
constexpr double pi_over_4 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, -1>();
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < pi_over_4))) {
|
||||
r.quadrant = 0;
|
||||
return r;
|
||||
}
|
||||
const V y = round(r.x / (2 * pi_over_4));
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3);
|
||||
|
||||
if (Vc_IS_LIKELY(all_of(r.x < 1025 * pi_over_4))) {
|
||||
// x - y * pi/2, y uses no more than 11 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x921FB54443000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<-1, 0x73DCB3B39A000, -43>();
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x45C06E0E68948, -86>();
|
||||
} else if (Vc_IS_LIKELY(all_of(y <= Vc::Detail::doubleConstant<1, 0, 30>()))) {
|
||||
// x - y * pi/2, y uses no more than 29 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x921FB40000000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x4442D00000000, -24>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x8469898CC5170, -48>();
|
||||
} else {
|
||||
// x - y * pi/2, y may require all mantissa bits
|
||||
const V y_hi = y & C::highMask(26);
|
||||
const V y_lo = y - y_hi;
|
||||
const auto _pi_2_1 = Vc::Detail::doubleConstant<1, 0x921FB50000000, 0>();
|
||||
const auto _pi_2_2 = Vc::Detail::doubleConstant<1, 0x110B460000000, -26>();
|
||||
const auto _pi_2_3 = Vc::Detail::doubleConstant<1, 0x1A62630000000, -54>();
|
||||
const auto _pi_2_4 = Vc::Detail::doubleConstant<1, 0x8A2E03707344A, -81>();
|
||||
/*
|
||||
std::cout << std::hexfloat;
|
||||
std::cout
|
||||
<< y_hi << '\n'
|
||||
<< y_lo << '\n'
|
||||
<< _pi_2_1 << '\n'
|
||||
<< _pi_2_2 << '\n'
|
||||
<< _pi_2_3 << '\n'
|
||||
<< y_hi * _pi_2_1 << '\n'
|
||||
<< y_hi * _pi_2_2 << '\n'
|
||||
<< y_hi * _pi_2_3 << '\n'
|
||||
<< y * _pi_2_4 << '\n'
|
||||
<< y_lo * _pi_2_1 << '\n'
|
||||
<< y_lo * _pi_2_2 << '\n'
|
||||
<< y_lo * _pi_2_3 << '\n';
|
||||
std::cout << std::defaultfloat;
|
||||
*/
|
||||
r.x = r.x
|
||||
- y_hi * _pi_2_1
|
||||
- max(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- min(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- max(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- min(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- max(y * _pi_2_4, y_lo * _pi_2_3)
|
||||
- min(y * _pi_2_4, y_lo * _pi_2_3);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr double signmask = -0.;
|
||||
constexpr float signmaskf = -0.f;
|
||||
} // anonymous namespace
|
||||
|
||||
/*
|
||||
* algorithm for sine and cosine:
|
||||
*
|
||||
* The result can be calculated with sine or cosine depending on the π/4 section the input is
|
||||
* in.
|
||||
* sine ≈ x + x³
|
||||
* cosine ≈ 1 - x²
|
||||
*
|
||||
* sine:
|
||||
* Map -x to x and invert the output
|
||||
* Extend precision of x - n * π/4 by calculating
|
||||
* ((x - n * p1) - n * p2) - n * p3 (p1 + p2 + p3 = π/4)
|
||||
*
|
||||
* Calculate Taylor series with tuned coefficients.
|
||||
* Fix sign.
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | sinSeries
|
||||
// 1 | cosSeries
|
||||
// 2 | sinSeries, sign flip
|
||||
// 3 | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 527449))) {
|
||||
return simd_cast<V>(sin(simd_cast<Vc::double_v, 0>(x)),
|
||||
sin(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | cosSeries, +
|
||||
// 1 | sinSeries, -
|
||||
// 2 | cosSeries, -
|
||||
// 3 | sinSeries, +
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
return simd_cast<V>(cos(simd_cast<Vc::double_v, 0>(x)),
|
||||
cos(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::double_v &x, Vc::double_v *s,
|
||||
Vc::double_v *c)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | cosine | sine
|
||||
// 0 | cosSeries, + | sinSeries
|
||||
// 1 | sinSeries, - | cosSeries
|
||||
// 2 | cosSeries, - | sinSeries, sign flip
|
||||
// 3 | sinSeries, + | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::float_v &x, Vc::float_v *s,
|
||||
Vc::float_v *c)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
Vc::double_v s0, s1, c0, c1;
|
||||
sincos(simd_cast<Vc::double_v, 0>(x), &s0, &c0);
|
||||
sincos(simd_cast<Vc::double_v, 1>(x), &s1, &c1);
|
||||
*s = simd_cast<V>(s0, s1);
|
||||
*c = simd_cast<V>(c0, c1);
|
||||
return;
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::asin(const Vc::float_v &_x)
|
||||
{
|
||||
typedef Vc::float_v V;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M &negative = _x < V::Zero();
|
||||
|
||||
const V &a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M &small = a < C::smallAsinInput();
|
||||
const M >_0_5 = a > C::_1_2();
|
||||
V x = a;
|
||||
V z = a * a;
|
||||
z(gt_0_5) = (V::One() - a) * C::_1_2();
|
||||
x(gt_0_5) = sqrt(z);
|
||||
z = ((((C::asinCoeff0(0) * z
|
||||
+ C::asinCoeff0(1)) * z
|
||||
+ C::asinCoeff0(2)) * z
|
||||
+ C::asinCoeff0(3)) * z
|
||||
+ C::asinCoeff0(4)) * z * x
|
||||
+ x;
|
||||
z(gt_0_5) = C::_pi_2() - (z + z);
|
||||
z(small) = a;
|
||||
z(negative) = -z;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::asin (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M negative = _x < V::Zero();
|
||||
|
||||
const V a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M small = a < C::smallAsinInput();
|
||||
const M large = a > C::largeAsinInput();
|
||||
|
||||
V zz = V::One() - a;
|
||||
const V r = (((C::asinCoeff0(0) * zz + C::asinCoeff0(1)) * zz + C::asinCoeff0(2)) * zz +
|
||||
C::asinCoeff0(3)) * zz + C::asinCoeff0(4);
|
||||
const V s = (((zz + C::asinCoeff1(0)) * zz + C::asinCoeff1(1)) * zz +
|
||||
C::asinCoeff1(2)) * zz + C::asinCoeff1(3);
|
||||
V sqrtzz = sqrt(zz + zz);
|
||||
V z = C::_pi_4() - sqrtzz;
|
||||
z -= sqrtzz * (zz * r / s) - C::_pi_2_rem();
|
||||
z += C::_pi_4();
|
||||
|
||||
V a2 = a * a;
|
||||
const V p = ((((C::asinCoeff2(0) * a2 + C::asinCoeff2(1)) * a2 + C::asinCoeff2(2)) * a2 +
|
||||
C::asinCoeff2(3)) * a2 + C::asinCoeff2(4)) * a2 + C::asinCoeff2(5);
|
||||
const V q = ((((a2 + C::asinCoeff3(0)) * a2 + C::asinCoeff3(1)) * a2 +
|
||||
C::asinCoeff3(2)) * a2 + C::asinCoeff3(3)) * a2 + C::asinCoeff3(4);
|
||||
z(!large) = a * (a2 * p / q) + a;
|
||||
|
||||
z(negative) = -z;
|
||||
z(small) = _x;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan(const Vc::float_v &_x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
V x = abs(_x);
|
||||
const M >_tan_3pi_8 = x > C::atanThrsHi();
|
||||
const M >_tan_pi_8 = x > C::atanThrsLo() && !gt_tan_3pi_8;
|
||||
V y = V::Zero();
|
||||
y(gt_tan_3pi_8) = C::_pi_2();
|
||||
y(gt_tan_pi_8) = C::_pi_4();
|
||||
x(gt_tan_3pi_8) = -V::One() / x;
|
||||
x(gt_tan_pi_8) = (x - V::One()) / (x + V::One());
|
||||
const V &x2 = x * x;
|
||||
y += (((C::atanP(0) * x2
|
||||
- C::atanP(1)) * x2
|
||||
+ C::atanP(2)) * x2
|
||||
- C::atanP(3)) * x2 * x
|
||||
+ x;
|
||||
y(_x < V::Zero()) = -y;
|
||||
y.setQnan(isnan(_x));
|
||||
return y;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
M sign = _x < V::Zero();
|
||||
V x = abs(_x);
|
||||
M finite = isfinite(_x);
|
||||
V ret = C::_pi_2();
|
||||
V y = V::Zero();
|
||||
const M large = x > C::atanThrsHi();
|
||||
const M gt_06 = x > C::atanThrsLo();
|
||||
V tmp = (x - V::One()) / (x + V::One());
|
||||
tmp(large) = -V::One() / x;
|
||||
x(gt_06) = tmp;
|
||||
y(gt_06) = C::_pi_4();
|
||||
y(large) = C::_pi_2();
|
||||
V z = x * x;
|
||||
const V p = (((C::atanP(0) * z + C::atanP(1)) * z + C::atanP(2)) * z + C::atanP(3)) * z + C::atanP(4);
|
||||
const V q = ((((z + C::atanQ(0)) * z + C::atanQ(1)) * z + C::atanQ(2)) * z + C::atanQ(3)) * z + C::atanQ(4);
|
||||
z = z * p / q;
|
||||
z = x * z + x;
|
||||
V morebits = C::_pi_2_rem();
|
||||
morebits(!large) *= C::_1_2();
|
||||
z(gt_06) += morebits;
|
||||
ret(finite) = y + z;
|
||||
ret(sign) = -ret;
|
||||
ret.setQnan(isnan(_x));
|
||||
return ret;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan2(const Vc::float_v &y,
|
||||
const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(C::_pi(), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan2 (const Vc::double_v &y, const Vc::double_v &x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(V(C::_pi()), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_AVX+XOP+FMA4.cpp
vendored
Normal file
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_AVX+XOP+FMA4.cpp
vendored
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2012-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
// enable bit operators for easier portable bit manipulation on floats
|
||||
#define Vc_ENABLE_FLOAT_BIT_OPERATORS 1
|
||||
|
||||
#include <Vc/vector.h>
|
||||
#if defined(Vc_IMPL_SSE) || defined(Vc_IMPL_AVX)
|
||||
#include <Vc/common/macros.h>
|
||||
//#include <Vc/IO>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Vc::Vector;
|
||||
template <typename T, typename Abi>
|
||||
using Const = typename std::conditional<std::is_same<Abi, VectorAbi::Avx>::value,
|
||||
AVX::Const<T>, SSE::Const<T>>::type;
|
||||
|
||||
template <typename V>
|
||||
using best_int_v_for =
|
||||
typename std::conditional<(V::size() <= Vector<int, VectorAbi::Best<int>>::size()),
|
||||
Vector<int, VectorAbi::Best<int>>,
|
||||
SimdArray<int, V::size()>>::type;
|
||||
template <typename Abi> using float_int_v = best_int_v_for<Vector<float, Abi>>;
|
||||
template <typename Abi> using double_int_v = best_int_v_for<Vector<double, Abi>>;
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> cosSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant< 1, 0x500000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x360800, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x2AAAAB, -5>(); // 1/4!
|
||||
return y * (x2 * x2) - .5f * x2 + 1.f;
|
||||
// alternative (appears neither faster nor more precise):
|
||||
// return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> cosSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant< 1, 0xAC00000000000, -45>(); // 1/16!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x9394000000000, -37>(); // -1/14!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1EED8C0000000, -29>(); // 1/12!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x27E4FB7400000, -22>(); // -1/10!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0xA01A01A018000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x6C16C16C16C00, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x5555555555554, -5>(); // 1/4!
|
||||
return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> sinSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant<-1, 0x4E6000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x088880, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x2AAAAB, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> sinSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
// x = [0, 0.7854 = pi/4]
|
||||
// x² = [0, 0.6169 = pi²/8]
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant<-1, 0xACF0000000000, -41>(); // -1/15!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x6124400000000, -33>(); // 1/13!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xAE64567000000, -26>(); // -1/11!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x71DE3A5540000, -19>(); // 1/9!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xA01A01A01A000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1111111111110, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x5555555555555, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
/**\internal
|
||||
* Fold \p x into [-¼π, ¼π] and remember the quadrant it came from:
|
||||
* quadrant 0: [-¼π, ¼π]
|
||||
* quadrant 1: [ ¼π, ¾π]
|
||||
* quadrant 2: [ ¾π, 1¼π]
|
||||
* quadrant 3: [1¼π, 1¾π]
|
||||
*
|
||||
* The algorithm determines `y` as the multiple `x - y * ¼π = [-¼π, ¼π]`. Using a bitmask,
|
||||
* `y` is reduced to `quadrant`. `y` can be calculated as
|
||||
* ```
|
||||
* y = trunc(x / ¼π);
|
||||
* y += fmod(y, 2);
|
||||
* ```
|
||||
* This can be simplified by moving the (implicit) division by 2 into the truncation
|
||||
* expression. The `+= fmod` effect can the be achieved by using rounding instead of
|
||||
* truncation:
|
||||
* `y = round(x / ½π) * 2`.
|
||||
* If precision allows, `2/π * x` is better (faster).
|
||||
*/
|
||||
template <class T, class Abi> struct folded {
|
||||
Vector<T, Abi> x, quadrant;
|
||||
};
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<float, Abi> foldInput(const Vector<float, Abi> &x)
|
||||
{
|
||||
using V = Vector<float, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<float, Abi>;
|
||||
|
||||
folded<float, Abi> r;
|
||||
r.x = abs(x);
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < C::_pi_4()))) {
|
||||
r.quadrant = 0;
|
||||
} else if (Vc_IS_LIKELY(all_of(r.x < 33 * C::_pi_4()))) {
|
||||
const float _2_over_pi = Vc::Detail::floatConstant<1, 0x22F983, -1>(); // ½π
|
||||
V y = round(r.x * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // y mod 4
|
||||
r.x -= y * Vc::Detail::floatConstant<1, 0x490fe0, 0>(); // pi/2
|
||||
r.x -= y * Vc::Detail::floatConstant<-1, 0x2bbbd3, -21>(); // pi/2 remainder
|
||||
} else {
|
||||
typedef SimdArray<double, V::size()> VD;
|
||||
VD xd = simd_cast<VD>(abs(r.x));
|
||||
constexpr double _2_over_pi =
|
||||
Vc::Detail::doubleConstant<1, 0x45F306DC9C883, -1>();
|
||||
VD y = round(xd * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // = y mod 4
|
||||
constexpr double pi_over_2 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, 0>();
|
||||
r.x = simd_cast<V>(xd - y * pi_over_2);
|
||||
}
|
||||
//std::cout << std::hexfloat << r.x << ' ' << r.quadrant << std::defaultfloat << '\n';
|
||||
return r;
|
||||
|
||||
/*
|
||||
More alternatives to avoid double precision calculation follow. Their main problem
|
||||
is unfortunate rounding on intermediate values of x (after subtraction):
|
||||
|
||||
if (all_of(y <= 0x1000)) {
|
||||
// 0 <= x <= 0x1.921fb6p12
|
||||
// π/4 - 12 bits precision (12 zero bits):
|
||||
//const auto _pi_4_1 = Vc::Detail::floatConstant< 1, 0x491000, -1>();
|
||||
//const auto _pi_4_2 = Vc::Detail::floatConstant<-1, 0x157000, -19>();
|
||||
//const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x6F4B9F, -32>();
|
||||
x -= y * C::_pi_4_hi();
|
||||
x -= y * C::_pi_4_rem1();
|
||||
x -= y * C::_pi_4_rem2();
|
||||
} else { //if (all_of(y <= 0x10000)) {
|
||||
// consider y = 0x1.fffep+16 / x = 0x1.921e24p+16
|
||||
// critical input: 0x1.921fb6p+12 <= x <= 0x1.921fb6p+16
|
||||
// π/4 - 8 bits precision (16 zero bits):
|
||||
const auto _pi_4_1 = Vc::Detail::floatConstant<+1, 0x490000, -1>();
|
||||
const auto _pi_4_2 = Vc::Detail::floatConstant<+1, 0x7E0000, -13>();
|
||||
const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x2C0000, -22>();
|
||||
const auto _pi_4_4 = Vc::Detail::floatConstant<+1, 0x085A31, -31>();
|
||||
x -= y * _pi_4_1; // (x -= 0x1.91fe6ep+16) -> 0x1.fb6000p+4
|
||||
x -= y * _pi_4_2; // (x -= 0x1.fbfe04p+4) -> -0x1.3c0800p-6
|
||||
x -= y * _pi_4_3; // (x -= 0x1.57fea8p-5) -> 0x1.bf5a80p-9
|
||||
x -= y * _pi_4_4; // (x -= 0x1.10b352p-14) -> 0x1.b6e4e6p-9
|
||||
x.setQnan(y > 0x10000);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<double, Abi> foldInput(const Vector<double, Abi> &x)
|
||||
{
|
||||
using V = Vector<double, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<double, Abi> ;
|
||||
|
||||
folded<double, Abi> r;
|
||||
r.x = abs(x);
|
||||
constexpr double pi_over_4 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, -1>();
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < pi_over_4))) {
|
||||
r.quadrant = 0;
|
||||
return r;
|
||||
}
|
||||
const V y = round(r.x / (2 * pi_over_4));
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3);
|
||||
|
||||
if (Vc_IS_LIKELY(all_of(r.x < 1025 * pi_over_4))) {
|
||||
// x - y * pi/2, y uses no more than 11 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x921FB54443000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<-1, 0x73DCB3B39A000, -43>();
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x45C06E0E68948, -86>();
|
||||
} else if (Vc_IS_LIKELY(all_of(y <= Vc::Detail::doubleConstant<1, 0, 30>()))) {
|
||||
// x - y * pi/2, y uses no more than 29 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x921FB40000000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x4442D00000000, -24>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x8469898CC5170, -48>();
|
||||
} else {
|
||||
// x - y * pi/2, y may require all mantissa bits
|
||||
const V y_hi = y & C::highMask(26);
|
||||
const V y_lo = y - y_hi;
|
||||
const auto _pi_2_1 = Vc::Detail::doubleConstant<1, 0x921FB50000000, 0>();
|
||||
const auto _pi_2_2 = Vc::Detail::doubleConstant<1, 0x110B460000000, -26>();
|
||||
const auto _pi_2_3 = Vc::Detail::doubleConstant<1, 0x1A62630000000, -54>();
|
||||
const auto _pi_2_4 = Vc::Detail::doubleConstant<1, 0x8A2E03707344A, -81>();
|
||||
/*
|
||||
std::cout << std::hexfloat;
|
||||
std::cout
|
||||
<< y_hi << '\n'
|
||||
<< y_lo << '\n'
|
||||
<< _pi_2_1 << '\n'
|
||||
<< _pi_2_2 << '\n'
|
||||
<< _pi_2_3 << '\n'
|
||||
<< y_hi * _pi_2_1 << '\n'
|
||||
<< y_hi * _pi_2_2 << '\n'
|
||||
<< y_hi * _pi_2_3 << '\n'
|
||||
<< y * _pi_2_4 << '\n'
|
||||
<< y_lo * _pi_2_1 << '\n'
|
||||
<< y_lo * _pi_2_2 << '\n'
|
||||
<< y_lo * _pi_2_3 << '\n';
|
||||
std::cout << std::defaultfloat;
|
||||
*/
|
||||
r.x = r.x
|
||||
- y_hi * _pi_2_1
|
||||
- max(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- min(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- max(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- min(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- max(y * _pi_2_4, y_lo * _pi_2_3)
|
||||
- min(y * _pi_2_4, y_lo * _pi_2_3);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr double signmask = -0.;
|
||||
constexpr float signmaskf = -0.f;
|
||||
} // anonymous namespace
|
||||
|
||||
/*
|
||||
* algorithm for sine and cosine:
|
||||
*
|
||||
* The result can be calculated with sine or cosine depending on the π/4 section the input is
|
||||
* in.
|
||||
* sine ≈ x + x³
|
||||
* cosine ≈ 1 - x²
|
||||
*
|
||||
* sine:
|
||||
* Map -x to x and invert the output
|
||||
* Extend precision of x - n * π/4 by calculating
|
||||
* ((x - n * p1) - n * p2) - n * p3 (p1 + p2 + p3 = π/4)
|
||||
*
|
||||
* Calculate Taylor series with tuned coefficients.
|
||||
* Fix sign.
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | sinSeries
|
||||
// 1 | cosSeries
|
||||
// 2 | sinSeries, sign flip
|
||||
// 3 | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 527449))) {
|
||||
return simd_cast<V>(sin(simd_cast<Vc::double_v, 0>(x)),
|
||||
sin(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | cosSeries, +
|
||||
// 1 | sinSeries, -
|
||||
// 2 | cosSeries, -
|
||||
// 3 | sinSeries, +
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
return simd_cast<V>(cos(simd_cast<Vc::double_v, 0>(x)),
|
||||
cos(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::double_v &x, Vc::double_v *s,
|
||||
Vc::double_v *c)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | cosine | sine
|
||||
// 0 | cosSeries, + | sinSeries
|
||||
// 1 | sinSeries, - | cosSeries
|
||||
// 2 | cosSeries, - | sinSeries, sign flip
|
||||
// 3 | sinSeries, + | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::float_v &x, Vc::float_v *s,
|
||||
Vc::float_v *c)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
Vc::double_v s0, s1, c0, c1;
|
||||
sincos(simd_cast<Vc::double_v, 0>(x), &s0, &c0);
|
||||
sincos(simd_cast<Vc::double_v, 1>(x), &s1, &c1);
|
||||
*s = simd_cast<V>(s0, s1);
|
||||
*c = simd_cast<V>(c0, c1);
|
||||
return;
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::asin(const Vc::float_v &_x)
|
||||
{
|
||||
typedef Vc::float_v V;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M &negative = _x < V::Zero();
|
||||
|
||||
const V &a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M &small = a < C::smallAsinInput();
|
||||
const M >_0_5 = a > C::_1_2();
|
||||
V x = a;
|
||||
V z = a * a;
|
||||
z(gt_0_5) = (V::One() - a) * C::_1_2();
|
||||
x(gt_0_5) = sqrt(z);
|
||||
z = ((((C::asinCoeff0(0) * z
|
||||
+ C::asinCoeff0(1)) * z
|
||||
+ C::asinCoeff0(2)) * z
|
||||
+ C::asinCoeff0(3)) * z
|
||||
+ C::asinCoeff0(4)) * z * x
|
||||
+ x;
|
||||
z(gt_0_5) = C::_pi_2() - (z + z);
|
||||
z(small) = a;
|
||||
z(negative) = -z;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::asin (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M negative = _x < V::Zero();
|
||||
|
||||
const V a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M small = a < C::smallAsinInput();
|
||||
const M large = a > C::largeAsinInput();
|
||||
|
||||
V zz = V::One() - a;
|
||||
const V r = (((C::asinCoeff0(0) * zz + C::asinCoeff0(1)) * zz + C::asinCoeff0(2)) * zz +
|
||||
C::asinCoeff0(3)) * zz + C::asinCoeff0(4);
|
||||
const V s = (((zz + C::asinCoeff1(0)) * zz + C::asinCoeff1(1)) * zz +
|
||||
C::asinCoeff1(2)) * zz + C::asinCoeff1(3);
|
||||
V sqrtzz = sqrt(zz + zz);
|
||||
V z = C::_pi_4() - sqrtzz;
|
||||
z -= sqrtzz * (zz * r / s) - C::_pi_2_rem();
|
||||
z += C::_pi_4();
|
||||
|
||||
V a2 = a * a;
|
||||
const V p = ((((C::asinCoeff2(0) * a2 + C::asinCoeff2(1)) * a2 + C::asinCoeff2(2)) * a2 +
|
||||
C::asinCoeff2(3)) * a2 + C::asinCoeff2(4)) * a2 + C::asinCoeff2(5);
|
||||
const V q = ((((a2 + C::asinCoeff3(0)) * a2 + C::asinCoeff3(1)) * a2 +
|
||||
C::asinCoeff3(2)) * a2 + C::asinCoeff3(3)) * a2 + C::asinCoeff3(4);
|
||||
z(!large) = a * (a2 * p / q) + a;
|
||||
|
||||
z(negative) = -z;
|
||||
z(small) = _x;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan(const Vc::float_v &_x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
V x = abs(_x);
|
||||
const M >_tan_3pi_8 = x > C::atanThrsHi();
|
||||
const M >_tan_pi_8 = x > C::atanThrsLo() && !gt_tan_3pi_8;
|
||||
V y = V::Zero();
|
||||
y(gt_tan_3pi_8) = C::_pi_2();
|
||||
y(gt_tan_pi_8) = C::_pi_4();
|
||||
x(gt_tan_3pi_8) = -V::One() / x;
|
||||
x(gt_tan_pi_8) = (x - V::One()) / (x + V::One());
|
||||
const V &x2 = x * x;
|
||||
y += (((C::atanP(0) * x2
|
||||
- C::atanP(1)) * x2
|
||||
+ C::atanP(2)) * x2
|
||||
- C::atanP(3)) * x2 * x
|
||||
+ x;
|
||||
y(_x < V::Zero()) = -y;
|
||||
y.setQnan(isnan(_x));
|
||||
return y;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
M sign = _x < V::Zero();
|
||||
V x = abs(_x);
|
||||
M finite = isfinite(_x);
|
||||
V ret = C::_pi_2();
|
||||
V y = V::Zero();
|
||||
const M large = x > C::atanThrsHi();
|
||||
const M gt_06 = x > C::atanThrsLo();
|
||||
V tmp = (x - V::One()) / (x + V::One());
|
||||
tmp(large) = -V::One() / x;
|
||||
x(gt_06) = tmp;
|
||||
y(gt_06) = C::_pi_4();
|
||||
y(large) = C::_pi_2();
|
||||
V z = x * x;
|
||||
const V p = (((C::atanP(0) * z + C::atanP(1)) * z + C::atanP(2)) * z + C::atanP(3)) * z + C::atanP(4);
|
||||
const V q = ((((z + C::atanQ(0)) * z + C::atanQ(1)) * z + C::atanQ(2)) * z + C::atanQ(3)) * z + C::atanQ(4);
|
||||
z = z * p / q;
|
||||
z = x * z + x;
|
||||
V morebits = C::_pi_2_rem();
|
||||
morebits(!large) *= C::_1_2();
|
||||
z(gt_06) += morebits;
|
||||
ret(finite) = y + z;
|
||||
ret(sign) = -ret;
|
||||
ret.setQnan(isnan(_x));
|
||||
return ret;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan2(const Vc::float_v &y,
|
||||
const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(C::_pi(), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan2 (const Vc::double_v &y, const Vc::double_v &x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(V(C::_pi()), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_AVX.cpp
vendored
Normal file
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_AVX.cpp
vendored
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2012-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
// enable bit operators for easier portable bit manipulation on floats
|
||||
#define Vc_ENABLE_FLOAT_BIT_OPERATORS 1
|
||||
|
||||
#include <Vc/vector.h>
|
||||
#if defined(Vc_IMPL_SSE) || defined(Vc_IMPL_AVX)
|
||||
#include <Vc/common/macros.h>
|
||||
//#include <Vc/IO>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Vc::Vector;
|
||||
template <typename T, typename Abi>
|
||||
using Const = typename std::conditional<std::is_same<Abi, VectorAbi::Avx>::value,
|
||||
AVX::Const<T>, SSE::Const<T>>::type;
|
||||
|
||||
template <typename V>
|
||||
using best_int_v_for =
|
||||
typename std::conditional<(V::size() <= Vector<int, VectorAbi::Best<int>>::size()),
|
||||
Vector<int, VectorAbi::Best<int>>,
|
||||
SimdArray<int, V::size()>>::type;
|
||||
template <typename Abi> using float_int_v = best_int_v_for<Vector<float, Abi>>;
|
||||
template <typename Abi> using double_int_v = best_int_v_for<Vector<double, Abi>>;
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> cosSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant< 1, 0x500000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x360800, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x2AAAAB, -5>(); // 1/4!
|
||||
return y * (x2 * x2) - .5f * x2 + 1.f;
|
||||
// alternative (appears neither faster nor more precise):
|
||||
// return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> cosSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant< 1, 0xAC00000000000, -45>(); // 1/16!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x9394000000000, -37>(); // -1/14!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1EED8C0000000, -29>(); // 1/12!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x27E4FB7400000, -22>(); // -1/10!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0xA01A01A018000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x6C16C16C16C00, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x5555555555554, -5>(); // 1/4!
|
||||
return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> sinSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant<-1, 0x4E6000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x088880, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x2AAAAB, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> sinSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
// x = [0, 0.7854 = pi/4]
|
||||
// x² = [0, 0.6169 = pi²/8]
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant<-1, 0xACF0000000000, -41>(); // -1/15!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x6124400000000, -33>(); // 1/13!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xAE64567000000, -26>(); // -1/11!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x71DE3A5540000, -19>(); // 1/9!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xA01A01A01A000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1111111111110, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x5555555555555, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
/**\internal
|
||||
* Fold \p x into [-¼π, ¼π] and remember the quadrant it came from:
|
||||
* quadrant 0: [-¼π, ¼π]
|
||||
* quadrant 1: [ ¼π, ¾π]
|
||||
* quadrant 2: [ ¾π, 1¼π]
|
||||
* quadrant 3: [1¼π, 1¾π]
|
||||
*
|
||||
* The algorithm determines `y` as the multiple `x - y * ¼π = [-¼π, ¼π]`. Using a bitmask,
|
||||
* `y` is reduced to `quadrant`. `y` can be calculated as
|
||||
* ```
|
||||
* y = trunc(x / ¼π);
|
||||
* y += fmod(y, 2);
|
||||
* ```
|
||||
* This can be simplified by moving the (implicit) division by 2 into the truncation
|
||||
* expression. The `+= fmod` effect can the be achieved by using rounding instead of
|
||||
* truncation:
|
||||
* `y = round(x / ½π) * 2`.
|
||||
* If precision allows, `2/π * x` is better (faster).
|
||||
*/
|
||||
template <class T, class Abi> struct folded {
|
||||
Vector<T, Abi> x, quadrant;
|
||||
};
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<float, Abi> foldInput(const Vector<float, Abi> &x)
|
||||
{
|
||||
using V = Vector<float, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<float, Abi>;
|
||||
|
||||
folded<float, Abi> r;
|
||||
r.x = abs(x);
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < C::_pi_4()))) {
|
||||
r.quadrant = 0;
|
||||
} else if (Vc_IS_LIKELY(all_of(r.x < 33 * C::_pi_4()))) {
|
||||
const float _2_over_pi = Vc::Detail::floatConstant<1, 0x22F983, -1>(); // ½π
|
||||
V y = round(r.x * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // y mod 4
|
||||
r.x -= y * Vc::Detail::floatConstant<1, 0x490fe0, 0>(); // pi/2
|
||||
r.x -= y * Vc::Detail::floatConstant<-1, 0x2bbbd3, -21>(); // pi/2 remainder
|
||||
} else {
|
||||
typedef SimdArray<double, V::size()> VD;
|
||||
VD xd = simd_cast<VD>(abs(r.x));
|
||||
constexpr double _2_over_pi =
|
||||
Vc::Detail::doubleConstant<1, 0x45F306DC9C883, -1>();
|
||||
VD y = round(xd * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // = y mod 4
|
||||
constexpr double pi_over_2 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, 0>();
|
||||
r.x = simd_cast<V>(xd - y * pi_over_2);
|
||||
}
|
||||
//std::cout << std::hexfloat << r.x << ' ' << r.quadrant << std::defaultfloat << '\n';
|
||||
return r;
|
||||
|
||||
/*
|
||||
More alternatives to avoid double precision calculation follow. Their main problem
|
||||
is unfortunate rounding on intermediate values of x (after subtraction):
|
||||
|
||||
if (all_of(y <= 0x1000)) {
|
||||
// 0 <= x <= 0x1.921fb6p12
|
||||
// π/4 - 12 bits precision (12 zero bits):
|
||||
//const auto _pi_4_1 = Vc::Detail::floatConstant< 1, 0x491000, -1>();
|
||||
//const auto _pi_4_2 = Vc::Detail::floatConstant<-1, 0x157000, -19>();
|
||||
//const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x6F4B9F, -32>();
|
||||
x -= y * C::_pi_4_hi();
|
||||
x -= y * C::_pi_4_rem1();
|
||||
x -= y * C::_pi_4_rem2();
|
||||
} else { //if (all_of(y <= 0x10000)) {
|
||||
// consider y = 0x1.fffep+16 / x = 0x1.921e24p+16
|
||||
// critical input: 0x1.921fb6p+12 <= x <= 0x1.921fb6p+16
|
||||
// π/4 - 8 bits precision (16 zero bits):
|
||||
const auto _pi_4_1 = Vc::Detail::floatConstant<+1, 0x490000, -1>();
|
||||
const auto _pi_4_2 = Vc::Detail::floatConstant<+1, 0x7E0000, -13>();
|
||||
const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x2C0000, -22>();
|
||||
const auto _pi_4_4 = Vc::Detail::floatConstant<+1, 0x085A31, -31>();
|
||||
x -= y * _pi_4_1; // (x -= 0x1.91fe6ep+16) -> 0x1.fb6000p+4
|
||||
x -= y * _pi_4_2; // (x -= 0x1.fbfe04p+4) -> -0x1.3c0800p-6
|
||||
x -= y * _pi_4_3; // (x -= 0x1.57fea8p-5) -> 0x1.bf5a80p-9
|
||||
x -= y * _pi_4_4; // (x -= 0x1.10b352p-14) -> 0x1.b6e4e6p-9
|
||||
x.setQnan(y > 0x10000);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<double, Abi> foldInput(const Vector<double, Abi> &x)
|
||||
{
|
||||
using V = Vector<double, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<double, Abi> ;
|
||||
|
||||
folded<double, Abi> r;
|
||||
r.x = abs(x);
|
||||
constexpr double pi_over_4 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, -1>();
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < pi_over_4))) {
|
||||
r.quadrant = 0;
|
||||
return r;
|
||||
}
|
||||
const V y = round(r.x / (2 * pi_over_4));
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3);
|
||||
|
||||
if (Vc_IS_LIKELY(all_of(r.x < 1025 * pi_over_4))) {
|
||||
// x - y * pi/2, y uses no more than 11 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x921FB54443000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<-1, 0x73DCB3B39A000, -43>();
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x45C06E0E68948, -86>();
|
||||
} else if (Vc_IS_LIKELY(all_of(y <= Vc::Detail::doubleConstant<1, 0, 30>()))) {
|
||||
// x - y * pi/2, y uses no more than 29 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x921FB40000000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x4442D00000000, -24>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x8469898CC5170, -48>();
|
||||
} else {
|
||||
// x - y * pi/2, y may require all mantissa bits
|
||||
const V y_hi = y & C::highMask(26);
|
||||
const V y_lo = y - y_hi;
|
||||
const auto _pi_2_1 = Vc::Detail::doubleConstant<1, 0x921FB50000000, 0>();
|
||||
const auto _pi_2_2 = Vc::Detail::doubleConstant<1, 0x110B460000000, -26>();
|
||||
const auto _pi_2_3 = Vc::Detail::doubleConstant<1, 0x1A62630000000, -54>();
|
||||
const auto _pi_2_4 = Vc::Detail::doubleConstant<1, 0x8A2E03707344A, -81>();
|
||||
/*
|
||||
std::cout << std::hexfloat;
|
||||
std::cout
|
||||
<< y_hi << '\n'
|
||||
<< y_lo << '\n'
|
||||
<< _pi_2_1 << '\n'
|
||||
<< _pi_2_2 << '\n'
|
||||
<< _pi_2_3 << '\n'
|
||||
<< y_hi * _pi_2_1 << '\n'
|
||||
<< y_hi * _pi_2_2 << '\n'
|
||||
<< y_hi * _pi_2_3 << '\n'
|
||||
<< y * _pi_2_4 << '\n'
|
||||
<< y_lo * _pi_2_1 << '\n'
|
||||
<< y_lo * _pi_2_2 << '\n'
|
||||
<< y_lo * _pi_2_3 << '\n';
|
||||
std::cout << std::defaultfloat;
|
||||
*/
|
||||
r.x = r.x
|
||||
- y_hi * _pi_2_1
|
||||
- max(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- min(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- max(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- min(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- max(y * _pi_2_4, y_lo * _pi_2_3)
|
||||
- min(y * _pi_2_4, y_lo * _pi_2_3);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr double signmask = -0.;
|
||||
constexpr float signmaskf = -0.f;
|
||||
} // anonymous namespace
|
||||
|
||||
/*
|
||||
* algorithm for sine and cosine:
|
||||
*
|
||||
* The result can be calculated with sine or cosine depending on the π/4 section the input is
|
||||
* in.
|
||||
* sine ≈ x + x³
|
||||
* cosine ≈ 1 - x²
|
||||
*
|
||||
* sine:
|
||||
* Map -x to x and invert the output
|
||||
* Extend precision of x - n * π/4 by calculating
|
||||
* ((x - n * p1) - n * p2) - n * p3 (p1 + p2 + p3 = π/4)
|
||||
*
|
||||
* Calculate Taylor series with tuned coefficients.
|
||||
* Fix sign.
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | sinSeries
|
||||
// 1 | cosSeries
|
||||
// 2 | sinSeries, sign flip
|
||||
// 3 | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 527449))) {
|
||||
return simd_cast<V>(sin(simd_cast<Vc::double_v, 0>(x)),
|
||||
sin(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | cosSeries, +
|
||||
// 1 | sinSeries, -
|
||||
// 2 | cosSeries, -
|
||||
// 3 | sinSeries, +
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
return simd_cast<V>(cos(simd_cast<Vc::double_v, 0>(x)),
|
||||
cos(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::double_v &x, Vc::double_v *s,
|
||||
Vc::double_v *c)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | cosine | sine
|
||||
// 0 | cosSeries, + | sinSeries
|
||||
// 1 | sinSeries, - | cosSeries
|
||||
// 2 | cosSeries, - | sinSeries, sign flip
|
||||
// 3 | sinSeries, + | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::float_v &x, Vc::float_v *s,
|
||||
Vc::float_v *c)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
Vc::double_v s0, s1, c0, c1;
|
||||
sincos(simd_cast<Vc::double_v, 0>(x), &s0, &c0);
|
||||
sincos(simd_cast<Vc::double_v, 1>(x), &s1, &c1);
|
||||
*s = simd_cast<V>(s0, s1);
|
||||
*c = simd_cast<V>(c0, c1);
|
||||
return;
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::asin(const Vc::float_v &_x)
|
||||
{
|
||||
typedef Vc::float_v V;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M &negative = _x < V::Zero();
|
||||
|
||||
const V &a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M &small = a < C::smallAsinInput();
|
||||
const M >_0_5 = a > C::_1_2();
|
||||
V x = a;
|
||||
V z = a * a;
|
||||
z(gt_0_5) = (V::One() - a) * C::_1_2();
|
||||
x(gt_0_5) = sqrt(z);
|
||||
z = ((((C::asinCoeff0(0) * z
|
||||
+ C::asinCoeff0(1)) * z
|
||||
+ C::asinCoeff0(2)) * z
|
||||
+ C::asinCoeff0(3)) * z
|
||||
+ C::asinCoeff0(4)) * z * x
|
||||
+ x;
|
||||
z(gt_0_5) = C::_pi_2() - (z + z);
|
||||
z(small) = a;
|
||||
z(negative) = -z;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::asin (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M negative = _x < V::Zero();
|
||||
|
||||
const V a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M small = a < C::smallAsinInput();
|
||||
const M large = a > C::largeAsinInput();
|
||||
|
||||
V zz = V::One() - a;
|
||||
const V r = (((C::asinCoeff0(0) * zz + C::asinCoeff0(1)) * zz + C::asinCoeff0(2)) * zz +
|
||||
C::asinCoeff0(3)) * zz + C::asinCoeff0(4);
|
||||
const V s = (((zz + C::asinCoeff1(0)) * zz + C::asinCoeff1(1)) * zz +
|
||||
C::asinCoeff1(2)) * zz + C::asinCoeff1(3);
|
||||
V sqrtzz = sqrt(zz + zz);
|
||||
V z = C::_pi_4() - sqrtzz;
|
||||
z -= sqrtzz * (zz * r / s) - C::_pi_2_rem();
|
||||
z += C::_pi_4();
|
||||
|
||||
V a2 = a * a;
|
||||
const V p = ((((C::asinCoeff2(0) * a2 + C::asinCoeff2(1)) * a2 + C::asinCoeff2(2)) * a2 +
|
||||
C::asinCoeff2(3)) * a2 + C::asinCoeff2(4)) * a2 + C::asinCoeff2(5);
|
||||
const V q = ((((a2 + C::asinCoeff3(0)) * a2 + C::asinCoeff3(1)) * a2 +
|
||||
C::asinCoeff3(2)) * a2 + C::asinCoeff3(3)) * a2 + C::asinCoeff3(4);
|
||||
z(!large) = a * (a2 * p / q) + a;
|
||||
|
||||
z(negative) = -z;
|
||||
z(small) = _x;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan(const Vc::float_v &_x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
V x = abs(_x);
|
||||
const M >_tan_3pi_8 = x > C::atanThrsHi();
|
||||
const M >_tan_pi_8 = x > C::atanThrsLo() && !gt_tan_3pi_8;
|
||||
V y = V::Zero();
|
||||
y(gt_tan_3pi_8) = C::_pi_2();
|
||||
y(gt_tan_pi_8) = C::_pi_4();
|
||||
x(gt_tan_3pi_8) = -V::One() / x;
|
||||
x(gt_tan_pi_8) = (x - V::One()) / (x + V::One());
|
||||
const V &x2 = x * x;
|
||||
y += (((C::atanP(0) * x2
|
||||
- C::atanP(1)) * x2
|
||||
+ C::atanP(2)) * x2
|
||||
- C::atanP(3)) * x2 * x
|
||||
+ x;
|
||||
y(_x < V::Zero()) = -y;
|
||||
y.setQnan(isnan(_x));
|
||||
return y;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
M sign = _x < V::Zero();
|
||||
V x = abs(_x);
|
||||
M finite = isfinite(_x);
|
||||
V ret = C::_pi_2();
|
||||
V y = V::Zero();
|
||||
const M large = x > C::atanThrsHi();
|
||||
const M gt_06 = x > C::atanThrsLo();
|
||||
V tmp = (x - V::One()) / (x + V::One());
|
||||
tmp(large) = -V::One() / x;
|
||||
x(gt_06) = tmp;
|
||||
y(gt_06) = C::_pi_4();
|
||||
y(large) = C::_pi_2();
|
||||
V z = x * x;
|
||||
const V p = (((C::atanP(0) * z + C::atanP(1)) * z + C::atanP(2)) * z + C::atanP(3)) * z + C::atanP(4);
|
||||
const V q = ((((z + C::atanQ(0)) * z + C::atanQ(1)) * z + C::atanQ(2)) * z + C::atanQ(3)) * z + C::atanQ(4);
|
||||
z = z * p / q;
|
||||
z = x * z + x;
|
||||
V morebits = C::_pi_2_rem();
|
||||
morebits(!large) *= C::_1_2();
|
||||
z(gt_06) += morebits;
|
||||
ret(finite) = y + z;
|
||||
ret(sign) = -ret;
|
||||
ret.setQnan(isnan(_x));
|
||||
return ret;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan2(const Vc::float_v &y,
|
||||
const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(C::_pi(), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan2 (const Vc::double_v &y, const Vc::double_v &x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(V(C::_pi()), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_AVX2+FMA+BMI2.cpp
vendored
Normal file
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_AVX2+FMA+BMI2.cpp
vendored
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2012-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
// enable bit operators for easier portable bit manipulation on floats
|
||||
#define Vc_ENABLE_FLOAT_BIT_OPERATORS 1
|
||||
|
||||
#include <Vc/vector.h>
|
||||
#if defined(Vc_IMPL_SSE) || defined(Vc_IMPL_AVX)
|
||||
#include <Vc/common/macros.h>
|
||||
//#include <Vc/IO>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Vc::Vector;
|
||||
template <typename T, typename Abi>
|
||||
using Const = typename std::conditional<std::is_same<Abi, VectorAbi::Avx>::value,
|
||||
AVX::Const<T>, SSE::Const<T>>::type;
|
||||
|
||||
template <typename V>
|
||||
using best_int_v_for =
|
||||
typename std::conditional<(V::size() <= Vector<int, VectorAbi::Best<int>>::size()),
|
||||
Vector<int, VectorAbi::Best<int>>,
|
||||
SimdArray<int, V::size()>>::type;
|
||||
template <typename Abi> using float_int_v = best_int_v_for<Vector<float, Abi>>;
|
||||
template <typename Abi> using double_int_v = best_int_v_for<Vector<double, Abi>>;
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> cosSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant< 1, 0x500000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x360800, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x2AAAAB, -5>(); // 1/4!
|
||||
return y * (x2 * x2) - .5f * x2 + 1.f;
|
||||
// alternative (appears neither faster nor more precise):
|
||||
// return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> cosSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant< 1, 0xAC00000000000, -45>(); // 1/16!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x9394000000000, -37>(); // -1/14!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1EED8C0000000, -29>(); // 1/12!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x27E4FB7400000, -22>(); // -1/10!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0xA01A01A018000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x6C16C16C16C00, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x5555555555554, -5>(); // 1/4!
|
||||
return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> sinSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant<-1, 0x4E6000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x088880, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x2AAAAB, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> sinSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
// x = [0, 0.7854 = pi/4]
|
||||
// x² = [0, 0.6169 = pi²/8]
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant<-1, 0xACF0000000000, -41>(); // -1/15!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x6124400000000, -33>(); // 1/13!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xAE64567000000, -26>(); // -1/11!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x71DE3A5540000, -19>(); // 1/9!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xA01A01A01A000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1111111111110, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x5555555555555, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
/**\internal
|
||||
* Fold \p x into [-¼π, ¼π] and remember the quadrant it came from:
|
||||
* quadrant 0: [-¼π, ¼π]
|
||||
* quadrant 1: [ ¼π, ¾π]
|
||||
* quadrant 2: [ ¾π, 1¼π]
|
||||
* quadrant 3: [1¼π, 1¾π]
|
||||
*
|
||||
* The algorithm determines `y` as the multiple `x - y * ¼π = [-¼π, ¼π]`. Using a bitmask,
|
||||
* `y` is reduced to `quadrant`. `y` can be calculated as
|
||||
* ```
|
||||
* y = trunc(x / ¼π);
|
||||
* y += fmod(y, 2);
|
||||
* ```
|
||||
* This can be simplified by moving the (implicit) division by 2 into the truncation
|
||||
* expression. The `+= fmod` effect can the be achieved by using rounding instead of
|
||||
* truncation:
|
||||
* `y = round(x / ½π) * 2`.
|
||||
* If precision allows, `2/π * x` is better (faster).
|
||||
*/
|
||||
template <class T, class Abi> struct folded {
|
||||
Vector<T, Abi> x, quadrant;
|
||||
};
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<float, Abi> foldInput(const Vector<float, Abi> &x)
|
||||
{
|
||||
using V = Vector<float, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<float, Abi>;
|
||||
|
||||
folded<float, Abi> r;
|
||||
r.x = abs(x);
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < C::_pi_4()))) {
|
||||
r.quadrant = 0;
|
||||
} else if (Vc_IS_LIKELY(all_of(r.x < 33 * C::_pi_4()))) {
|
||||
const float _2_over_pi = Vc::Detail::floatConstant<1, 0x22F983, -1>(); // ½π
|
||||
V y = round(r.x * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // y mod 4
|
||||
r.x -= y * Vc::Detail::floatConstant<1, 0x490fe0, 0>(); // pi/2
|
||||
r.x -= y * Vc::Detail::floatConstant<-1, 0x2bbbd3, -21>(); // pi/2 remainder
|
||||
} else {
|
||||
typedef SimdArray<double, V::size()> VD;
|
||||
VD xd = simd_cast<VD>(abs(r.x));
|
||||
constexpr double _2_over_pi =
|
||||
Vc::Detail::doubleConstant<1, 0x45F306DC9C883, -1>();
|
||||
VD y = round(xd * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // = y mod 4
|
||||
constexpr double pi_over_2 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, 0>();
|
||||
r.x = simd_cast<V>(xd - y * pi_over_2);
|
||||
}
|
||||
//std::cout << std::hexfloat << r.x << ' ' << r.quadrant << std::defaultfloat << '\n';
|
||||
return r;
|
||||
|
||||
/*
|
||||
More alternatives to avoid double precision calculation follow. Their main problem
|
||||
is unfortunate rounding on intermediate values of x (after subtraction):
|
||||
|
||||
if (all_of(y <= 0x1000)) {
|
||||
// 0 <= x <= 0x1.921fb6p12
|
||||
// π/4 - 12 bits precision (12 zero bits):
|
||||
//const auto _pi_4_1 = Vc::Detail::floatConstant< 1, 0x491000, -1>();
|
||||
//const auto _pi_4_2 = Vc::Detail::floatConstant<-1, 0x157000, -19>();
|
||||
//const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x6F4B9F, -32>();
|
||||
x -= y * C::_pi_4_hi();
|
||||
x -= y * C::_pi_4_rem1();
|
||||
x -= y * C::_pi_4_rem2();
|
||||
} else { //if (all_of(y <= 0x10000)) {
|
||||
// consider y = 0x1.fffep+16 / x = 0x1.921e24p+16
|
||||
// critical input: 0x1.921fb6p+12 <= x <= 0x1.921fb6p+16
|
||||
// π/4 - 8 bits precision (16 zero bits):
|
||||
const auto _pi_4_1 = Vc::Detail::floatConstant<+1, 0x490000, -1>();
|
||||
const auto _pi_4_2 = Vc::Detail::floatConstant<+1, 0x7E0000, -13>();
|
||||
const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x2C0000, -22>();
|
||||
const auto _pi_4_4 = Vc::Detail::floatConstant<+1, 0x085A31, -31>();
|
||||
x -= y * _pi_4_1; // (x -= 0x1.91fe6ep+16) -> 0x1.fb6000p+4
|
||||
x -= y * _pi_4_2; // (x -= 0x1.fbfe04p+4) -> -0x1.3c0800p-6
|
||||
x -= y * _pi_4_3; // (x -= 0x1.57fea8p-5) -> 0x1.bf5a80p-9
|
||||
x -= y * _pi_4_4; // (x -= 0x1.10b352p-14) -> 0x1.b6e4e6p-9
|
||||
x.setQnan(y > 0x10000);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<double, Abi> foldInput(const Vector<double, Abi> &x)
|
||||
{
|
||||
using V = Vector<double, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<double, Abi> ;
|
||||
|
||||
folded<double, Abi> r;
|
||||
r.x = abs(x);
|
||||
constexpr double pi_over_4 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, -1>();
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < pi_over_4))) {
|
||||
r.quadrant = 0;
|
||||
return r;
|
||||
}
|
||||
const V y = round(r.x / (2 * pi_over_4));
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3);
|
||||
|
||||
if (Vc_IS_LIKELY(all_of(r.x < 1025 * pi_over_4))) {
|
||||
// x - y * pi/2, y uses no more than 11 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x921FB54443000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<-1, 0x73DCB3B39A000, -43>();
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x45C06E0E68948, -86>();
|
||||
} else if (Vc_IS_LIKELY(all_of(y <= Vc::Detail::doubleConstant<1, 0, 30>()))) {
|
||||
// x - y * pi/2, y uses no more than 29 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x921FB40000000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x4442D00000000, -24>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x8469898CC5170, -48>();
|
||||
} else {
|
||||
// x - y * pi/2, y may require all mantissa bits
|
||||
const V y_hi = y & C::highMask(26);
|
||||
const V y_lo = y - y_hi;
|
||||
const auto _pi_2_1 = Vc::Detail::doubleConstant<1, 0x921FB50000000, 0>();
|
||||
const auto _pi_2_2 = Vc::Detail::doubleConstant<1, 0x110B460000000, -26>();
|
||||
const auto _pi_2_3 = Vc::Detail::doubleConstant<1, 0x1A62630000000, -54>();
|
||||
const auto _pi_2_4 = Vc::Detail::doubleConstant<1, 0x8A2E03707344A, -81>();
|
||||
/*
|
||||
std::cout << std::hexfloat;
|
||||
std::cout
|
||||
<< y_hi << '\n'
|
||||
<< y_lo << '\n'
|
||||
<< _pi_2_1 << '\n'
|
||||
<< _pi_2_2 << '\n'
|
||||
<< _pi_2_3 << '\n'
|
||||
<< y_hi * _pi_2_1 << '\n'
|
||||
<< y_hi * _pi_2_2 << '\n'
|
||||
<< y_hi * _pi_2_3 << '\n'
|
||||
<< y * _pi_2_4 << '\n'
|
||||
<< y_lo * _pi_2_1 << '\n'
|
||||
<< y_lo * _pi_2_2 << '\n'
|
||||
<< y_lo * _pi_2_3 << '\n';
|
||||
std::cout << std::defaultfloat;
|
||||
*/
|
||||
r.x = r.x
|
||||
- y_hi * _pi_2_1
|
||||
- max(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- min(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- max(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- min(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- max(y * _pi_2_4, y_lo * _pi_2_3)
|
||||
- min(y * _pi_2_4, y_lo * _pi_2_3);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr double signmask = -0.;
|
||||
constexpr float signmaskf = -0.f;
|
||||
} // anonymous namespace
|
||||
|
||||
/*
|
||||
* algorithm for sine and cosine:
|
||||
*
|
||||
* The result can be calculated with sine or cosine depending on the π/4 section the input is
|
||||
* in.
|
||||
* sine ≈ x + x³
|
||||
* cosine ≈ 1 - x²
|
||||
*
|
||||
* sine:
|
||||
* Map -x to x and invert the output
|
||||
* Extend precision of x - n * π/4 by calculating
|
||||
* ((x - n * p1) - n * p2) - n * p3 (p1 + p2 + p3 = π/4)
|
||||
*
|
||||
* Calculate Taylor series with tuned coefficients.
|
||||
* Fix sign.
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | sinSeries
|
||||
// 1 | cosSeries
|
||||
// 2 | sinSeries, sign flip
|
||||
// 3 | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 527449))) {
|
||||
return simd_cast<V>(sin(simd_cast<Vc::double_v, 0>(x)),
|
||||
sin(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | cosSeries, +
|
||||
// 1 | sinSeries, -
|
||||
// 2 | cosSeries, -
|
||||
// 3 | sinSeries, +
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
return simd_cast<V>(cos(simd_cast<Vc::double_v, 0>(x)),
|
||||
cos(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::double_v &x, Vc::double_v *s,
|
||||
Vc::double_v *c)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | cosine | sine
|
||||
// 0 | cosSeries, + | sinSeries
|
||||
// 1 | sinSeries, - | cosSeries
|
||||
// 2 | cosSeries, - | sinSeries, sign flip
|
||||
// 3 | sinSeries, + | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::float_v &x, Vc::float_v *s,
|
||||
Vc::float_v *c)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
Vc::double_v s0, s1, c0, c1;
|
||||
sincos(simd_cast<Vc::double_v, 0>(x), &s0, &c0);
|
||||
sincos(simd_cast<Vc::double_v, 1>(x), &s1, &c1);
|
||||
*s = simd_cast<V>(s0, s1);
|
||||
*c = simd_cast<V>(c0, c1);
|
||||
return;
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::asin(const Vc::float_v &_x)
|
||||
{
|
||||
typedef Vc::float_v V;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M &negative = _x < V::Zero();
|
||||
|
||||
const V &a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M &small = a < C::smallAsinInput();
|
||||
const M >_0_5 = a > C::_1_2();
|
||||
V x = a;
|
||||
V z = a * a;
|
||||
z(gt_0_5) = (V::One() - a) * C::_1_2();
|
||||
x(gt_0_5) = sqrt(z);
|
||||
z = ((((C::asinCoeff0(0) * z
|
||||
+ C::asinCoeff0(1)) * z
|
||||
+ C::asinCoeff0(2)) * z
|
||||
+ C::asinCoeff0(3)) * z
|
||||
+ C::asinCoeff0(4)) * z * x
|
||||
+ x;
|
||||
z(gt_0_5) = C::_pi_2() - (z + z);
|
||||
z(small) = a;
|
||||
z(negative) = -z;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::asin (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M negative = _x < V::Zero();
|
||||
|
||||
const V a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M small = a < C::smallAsinInput();
|
||||
const M large = a > C::largeAsinInput();
|
||||
|
||||
V zz = V::One() - a;
|
||||
const V r = (((C::asinCoeff0(0) * zz + C::asinCoeff0(1)) * zz + C::asinCoeff0(2)) * zz +
|
||||
C::asinCoeff0(3)) * zz + C::asinCoeff0(4);
|
||||
const V s = (((zz + C::asinCoeff1(0)) * zz + C::asinCoeff1(1)) * zz +
|
||||
C::asinCoeff1(2)) * zz + C::asinCoeff1(3);
|
||||
V sqrtzz = sqrt(zz + zz);
|
||||
V z = C::_pi_4() - sqrtzz;
|
||||
z -= sqrtzz * (zz * r / s) - C::_pi_2_rem();
|
||||
z += C::_pi_4();
|
||||
|
||||
V a2 = a * a;
|
||||
const V p = ((((C::asinCoeff2(0) * a2 + C::asinCoeff2(1)) * a2 + C::asinCoeff2(2)) * a2 +
|
||||
C::asinCoeff2(3)) * a2 + C::asinCoeff2(4)) * a2 + C::asinCoeff2(5);
|
||||
const V q = ((((a2 + C::asinCoeff3(0)) * a2 + C::asinCoeff3(1)) * a2 +
|
||||
C::asinCoeff3(2)) * a2 + C::asinCoeff3(3)) * a2 + C::asinCoeff3(4);
|
||||
z(!large) = a * (a2 * p / q) + a;
|
||||
|
||||
z(negative) = -z;
|
||||
z(small) = _x;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan(const Vc::float_v &_x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
V x = abs(_x);
|
||||
const M >_tan_3pi_8 = x > C::atanThrsHi();
|
||||
const M >_tan_pi_8 = x > C::atanThrsLo() && !gt_tan_3pi_8;
|
||||
V y = V::Zero();
|
||||
y(gt_tan_3pi_8) = C::_pi_2();
|
||||
y(gt_tan_pi_8) = C::_pi_4();
|
||||
x(gt_tan_3pi_8) = -V::One() / x;
|
||||
x(gt_tan_pi_8) = (x - V::One()) / (x + V::One());
|
||||
const V &x2 = x * x;
|
||||
y += (((C::atanP(0) * x2
|
||||
- C::atanP(1)) * x2
|
||||
+ C::atanP(2)) * x2
|
||||
- C::atanP(3)) * x2 * x
|
||||
+ x;
|
||||
y(_x < V::Zero()) = -y;
|
||||
y.setQnan(isnan(_x));
|
||||
return y;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
M sign = _x < V::Zero();
|
||||
V x = abs(_x);
|
||||
M finite = isfinite(_x);
|
||||
V ret = C::_pi_2();
|
||||
V y = V::Zero();
|
||||
const M large = x > C::atanThrsHi();
|
||||
const M gt_06 = x > C::atanThrsLo();
|
||||
V tmp = (x - V::One()) / (x + V::One());
|
||||
tmp(large) = -V::One() / x;
|
||||
x(gt_06) = tmp;
|
||||
y(gt_06) = C::_pi_4();
|
||||
y(large) = C::_pi_2();
|
||||
V z = x * x;
|
||||
const V p = (((C::atanP(0) * z + C::atanP(1)) * z + C::atanP(2)) * z + C::atanP(3)) * z + C::atanP(4);
|
||||
const V q = ((((z + C::atanQ(0)) * z + C::atanQ(1)) * z + C::atanQ(2)) * z + C::atanQ(3)) * z + C::atanQ(4);
|
||||
z = z * p / q;
|
||||
z = x * z + x;
|
||||
V morebits = C::_pi_2_rem();
|
||||
morebits(!large) *= C::_1_2();
|
||||
z(gt_06) += morebits;
|
||||
ret(finite) = y + z;
|
||||
ret(sign) = -ret;
|
||||
ret.setQnan(isnan(_x));
|
||||
return ret;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan2(const Vc::float_v &y,
|
||||
const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(C::_pi(), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan2 (const Vc::double_v &y, const Vc::double_v &x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(V(C::_pi()), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_SSE+XOP+FMA.cpp
vendored
Normal file
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_SSE+XOP+FMA.cpp
vendored
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2012-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
// enable bit operators for easier portable bit manipulation on floats
|
||||
#define Vc_ENABLE_FLOAT_BIT_OPERATORS 1
|
||||
|
||||
#include <Vc/vector.h>
|
||||
#if defined(Vc_IMPL_SSE) || defined(Vc_IMPL_AVX)
|
||||
#include <Vc/common/macros.h>
|
||||
//#include <Vc/IO>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Vc::Vector;
|
||||
template <typename T, typename Abi>
|
||||
using Const = typename std::conditional<std::is_same<Abi, VectorAbi::Avx>::value,
|
||||
AVX::Const<T>, SSE::Const<T>>::type;
|
||||
|
||||
template <typename V>
|
||||
using best_int_v_for =
|
||||
typename std::conditional<(V::size() <= Vector<int, VectorAbi::Best<int>>::size()),
|
||||
Vector<int, VectorAbi::Best<int>>,
|
||||
SimdArray<int, V::size()>>::type;
|
||||
template <typename Abi> using float_int_v = best_int_v_for<Vector<float, Abi>>;
|
||||
template <typename Abi> using double_int_v = best_int_v_for<Vector<double, Abi>>;
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> cosSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant< 1, 0x500000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x360800, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x2AAAAB, -5>(); // 1/4!
|
||||
return y * (x2 * x2) - .5f * x2 + 1.f;
|
||||
// alternative (appears neither faster nor more precise):
|
||||
// return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> cosSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant< 1, 0xAC00000000000, -45>(); // 1/16!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x9394000000000, -37>(); // -1/14!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1EED8C0000000, -29>(); // 1/12!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x27E4FB7400000, -22>(); // -1/10!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0xA01A01A018000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x6C16C16C16C00, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x5555555555554, -5>(); // 1/4!
|
||||
return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> sinSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant<-1, 0x4E6000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x088880, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x2AAAAB, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> sinSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
// x = [0, 0.7854 = pi/4]
|
||||
// x² = [0, 0.6169 = pi²/8]
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant<-1, 0xACF0000000000, -41>(); // -1/15!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x6124400000000, -33>(); // 1/13!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xAE64567000000, -26>(); // -1/11!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x71DE3A5540000, -19>(); // 1/9!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xA01A01A01A000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1111111111110, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x5555555555555, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
/**\internal
|
||||
* Fold \p x into [-¼π, ¼π] and remember the quadrant it came from:
|
||||
* quadrant 0: [-¼π, ¼π]
|
||||
* quadrant 1: [ ¼π, ¾π]
|
||||
* quadrant 2: [ ¾π, 1¼π]
|
||||
* quadrant 3: [1¼π, 1¾π]
|
||||
*
|
||||
* The algorithm determines `y` as the multiple `x - y * ¼π = [-¼π, ¼π]`. Using a bitmask,
|
||||
* `y` is reduced to `quadrant`. `y` can be calculated as
|
||||
* ```
|
||||
* y = trunc(x / ¼π);
|
||||
* y += fmod(y, 2);
|
||||
* ```
|
||||
* This can be simplified by moving the (implicit) division by 2 into the truncation
|
||||
* expression. The `+= fmod` effect can the be achieved by using rounding instead of
|
||||
* truncation:
|
||||
* `y = round(x / ½π) * 2`.
|
||||
* If precision allows, `2/π * x` is better (faster).
|
||||
*/
|
||||
template <class T, class Abi> struct folded {
|
||||
Vector<T, Abi> x, quadrant;
|
||||
};
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<float, Abi> foldInput(const Vector<float, Abi> &x)
|
||||
{
|
||||
using V = Vector<float, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<float, Abi>;
|
||||
|
||||
folded<float, Abi> r;
|
||||
r.x = abs(x);
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < C::_pi_4()))) {
|
||||
r.quadrant = 0;
|
||||
} else if (Vc_IS_LIKELY(all_of(r.x < 33 * C::_pi_4()))) {
|
||||
const float _2_over_pi = Vc::Detail::floatConstant<1, 0x22F983, -1>(); // ½π
|
||||
V y = round(r.x * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // y mod 4
|
||||
r.x -= y * Vc::Detail::floatConstant<1, 0x490fe0, 0>(); // pi/2
|
||||
r.x -= y * Vc::Detail::floatConstant<-1, 0x2bbbd3, -21>(); // pi/2 remainder
|
||||
} else {
|
||||
typedef SimdArray<double, V::size()> VD;
|
||||
VD xd = simd_cast<VD>(abs(r.x));
|
||||
constexpr double _2_over_pi =
|
||||
Vc::Detail::doubleConstant<1, 0x45F306DC9C883, -1>();
|
||||
VD y = round(xd * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // = y mod 4
|
||||
constexpr double pi_over_2 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, 0>();
|
||||
r.x = simd_cast<V>(xd - y * pi_over_2);
|
||||
}
|
||||
//std::cout << std::hexfloat << r.x << ' ' << r.quadrant << std::defaultfloat << '\n';
|
||||
return r;
|
||||
|
||||
/*
|
||||
More alternatives to avoid double precision calculation follow. Their main problem
|
||||
is unfortunate rounding on intermediate values of x (after subtraction):
|
||||
|
||||
if (all_of(y <= 0x1000)) {
|
||||
// 0 <= x <= 0x1.921fb6p12
|
||||
// π/4 - 12 bits precision (12 zero bits):
|
||||
//const auto _pi_4_1 = Vc::Detail::floatConstant< 1, 0x491000, -1>();
|
||||
//const auto _pi_4_2 = Vc::Detail::floatConstant<-1, 0x157000, -19>();
|
||||
//const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x6F4B9F, -32>();
|
||||
x -= y * C::_pi_4_hi();
|
||||
x -= y * C::_pi_4_rem1();
|
||||
x -= y * C::_pi_4_rem2();
|
||||
} else { //if (all_of(y <= 0x10000)) {
|
||||
// consider y = 0x1.fffep+16 / x = 0x1.921e24p+16
|
||||
// critical input: 0x1.921fb6p+12 <= x <= 0x1.921fb6p+16
|
||||
// π/4 - 8 bits precision (16 zero bits):
|
||||
const auto _pi_4_1 = Vc::Detail::floatConstant<+1, 0x490000, -1>();
|
||||
const auto _pi_4_2 = Vc::Detail::floatConstant<+1, 0x7E0000, -13>();
|
||||
const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x2C0000, -22>();
|
||||
const auto _pi_4_4 = Vc::Detail::floatConstant<+1, 0x085A31, -31>();
|
||||
x -= y * _pi_4_1; // (x -= 0x1.91fe6ep+16) -> 0x1.fb6000p+4
|
||||
x -= y * _pi_4_2; // (x -= 0x1.fbfe04p+4) -> -0x1.3c0800p-6
|
||||
x -= y * _pi_4_3; // (x -= 0x1.57fea8p-5) -> 0x1.bf5a80p-9
|
||||
x -= y * _pi_4_4; // (x -= 0x1.10b352p-14) -> 0x1.b6e4e6p-9
|
||||
x.setQnan(y > 0x10000);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<double, Abi> foldInput(const Vector<double, Abi> &x)
|
||||
{
|
||||
using V = Vector<double, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<double, Abi> ;
|
||||
|
||||
folded<double, Abi> r;
|
||||
r.x = abs(x);
|
||||
constexpr double pi_over_4 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, -1>();
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < pi_over_4))) {
|
||||
r.quadrant = 0;
|
||||
return r;
|
||||
}
|
||||
const V y = round(r.x / (2 * pi_over_4));
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3);
|
||||
|
||||
if (Vc_IS_LIKELY(all_of(r.x < 1025 * pi_over_4))) {
|
||||
// x - y * pi/2, y uses no more than 11 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x921FB54443000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<-1, 0x73DCB3B39A000, -43>();
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x45C06E0E68948, -86>();
|
||||
} else if (Vc_IS_LIKELY(all_of(y <= Vc::Detail::doubleConstant<1, 0, 30>()))) {
|
||||
// x - y * pi/2, y uses no more than 29 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x921FB40000000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x4442D00000000, -24>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x8469898CC5170, -48>();
|
||||
} else {
|
||||
// x - y * pi/2, y may require all mantissa bits
|
||||
const V y_hi = y & C::highMask(26);
|
||||
const V y_lo = y - y_hi;
|
||||
const auto _pi_2_1 = Vc::Detail::doubleConstant<1, 0x921FB50000000, 0>();
|
||||
const auto _pi_2_2 = Vc::Detail::doubleConstant<1, 0x110B460000000, -26>();
|
||||
const auto _pi_2_3 = Vc::Detail::doubleConstant<1, 0x1A62630000000, -54>();
|
||||
const auto _pi_2_4 = Vc::Detail::doubleConstant<1, 0x8A2E03707344A, -81>();
|
||||
/*
|
||||
std::cout << std::hexfloat;
|
||||
std::cout
|
||||
<< y_hi << '\n'
|
||||
<< y_lo << '\n'
|
||||
<< _pi_2_1 << '\n'
|
||||
<< _pi_2_2 << '\n'
|
||||
<< _pi_2_3 << '\n'
|
||||
<< y_hi * _pi_2_1 << '\n'
|
||||
<< y_hi * _pi_2_2 << '\n'
|
||||
<< y_hi * _pi_2_3 << '\n'
|
||||
<< y * _pi_2_4 << '\n'
|
||||
<< y_lo * _pi_2_1 << '\n'
|
||||
<< y_lo * _pi_2_2 << '\n'
|
||||
<< y_lo * _pi_2_3 << '\n';
|
||||
std::cout << std::defaultfloat;
|
||||
*/
|
||||
r.x = r.x
|
||||
- y_hi * _pi_2_1
|
||||
- max(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- min(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- max(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- min(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- max(y * _pi_2_4, y_lo * _pi_2_3)
|
||||
- min(y * _pi_2_4, y_lo * _pi_2_3);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr double signmask = -0.;
|
||||
constexpr float signmaskf = -0.f;
|
||||
} // anonymous namespace
|
||||
|
||||
/*
|
||||
* algorithm for sine and cosine:
|
||||
*
|
||||
* The result can be calculated with sine or cosine depending on the π/4 section the input is
|
||||
* in.
|
||||
* sine ≈ x + x³
|
||||
* cosine ≈ 1 - x²
|
||||
*
|
||||
* sine:
|
||||
* Map -x to x and invert the output
|
||||
* Extend precision of x - n * π/4 by calculating
|
||||
* ((x - n * p1) - n * p2) - n * p3 (p1 + p2 + p3 = π/4)
|
||||
*
|
||||
* Calculate Taylor series with tuned coefficients.
|
||||
* Fix sign.
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | sinSeries
|
||||
// 1 | cosSeries
|
||||
// 2 | sinSeries, sign flip
|
||||
// 3 | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 527449))) {
|
||||
return simd_cast<V>(sin(simd_cast<Vc::double_v, 0>(x)),
|
||||
sin(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | cosSeries, +
|
||||
// 1 | sinSeries, -
|
||||
// 2 | cosSeries, -
|
||||
// 3 | sinSeries, +
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
return simd_cast<V>(cos(simd_cast<Vc::double_v, 0>(x)),
|
||||
cos(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::double_v &x, Vc::double_v *s,
|
||||
Vc::double_v *c)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | cosine | sine
|
||||
// 0 | cosSeries, + | sinSeries
|
||||
// 1 | sinSeries, - | cosSeries
|
||||
// 2 | cosSeries, - | sinSeries, sign flip
|
||||
// 3 | sinSeries, + | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::float_v &x, Vc::float_v *s,
|
||||
Vc::float_v *c)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
Vc::double_v s0, s1, c0, c1;
|
||||
sincos(simd_cast<Vc::double_v, 0>(x), &s0, &c0);
|
||||
sincos(simd_cast<Vc::double_v, 1>(x), &s1, &c1);
|
||||
*s = simd_cast<V>(s0, s1);
|
||||
*c = simd_cast<V>(c0, c1);
|
||||
return;
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::asin(const Vc::float_v &_x)
|
||||
{
|
||||
typedef Vc::float_v V;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M &negative = _x < V::Zero();
|
||||
|
||||
const V &a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M &small = a < C::smallAsinInput();
|
||||
const M >_0_5 = a > C::_1_2();
|
||||
V x = a;
|
||||
V z = a * a;
|
||||
z(gt_0_5) = (V::One() - a) * C::_1_2();
|
||||
x(gt_0_5) = sqrt(z);
|
||||
z = ((((C::asinCoeff0(0) * z
|
||||
+ C::asinCoeff0(1)) * z
|
||||
+ C::asinCoeff0(2)) * z
|
||||
+ C::asinCoeff0(3)) * z
|
||||
+ C::asinCoeff0(4)) * z * x
|
||||
+ x;
|
||||
z(gt_0_5) = C::_pi_2() - (z + z);
|
||||
z(small) = a;
|
||||
z(negative) = -z;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::asin (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M negative = _x < V::Zero();
|
||||
|
||||
const V a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M small = a < C::smallAsinInput();
|
||||
const M large = a > C::largeAsinInput();
|
||||
|
||||
V zz = V::One() - a;
|
||||
const V r = (((C::asinCoeff0(0) * zz + C::asinCoeff0(1)) * zz + C::asinCoeff0(2)) * zz +
|
||||
C::asinCoeff0(3)) * zz + C::asinCoeff0(4);
|
||||
const V s = (((zz + C::asinCoeff1(0)) * zz + C::asinCoeff1(1)) * zz +
|
||||
C::asinCoeff1(2)) * zz + C::asinCoeff1(3);
|
||||
V sqrtzz = sqrt(zz + zz);
|
||||
V z = C::_pi_4() - sqrtzz;
|
||||
z -= sqrtzz * (zz * r / s) - C::_pi_2_rem();
|
||||
z += C::_pi_4();
|
||||
|
||||
V a2 = a * a;
|
||||
const V p = ((((C::asinCoeff2(0) * a2 + C::asinCoeff2(1)) * a2 + C::asinCoeff2(2)) * a2 +
|
||||
C::asinCoeff2(3)) * a2 + C::asinCoeff2(4)) * a2 + C::asinCoeff2(5);
|
||||
const V q = ((((a2 + C::asinCoeff3(0)) * a2 + C::asinCoeff3(1)) * a2 +
|
||||
C::asinCoeff3(2)) * a2 + C::asinCoeff3(3)) * a2 + C::asinCoeff3(4);
|
||||
z(!large) = a * (a2 * p / q) + a;
|
||||
|
||||
z(negative) = -z;
|
||||
z(small) = _x;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan(const Vc::float_v &_x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
V x = abs(_x);
|
||||
const M >_tan_3pi_8 = x > C::atanThrsHi();
|
||||
const M >_tan_pi_8 = x > C::atanThrsLo() && !gt_tan_3pi_8;
|
||||
V y = V::Zero();
|
||||
y(gt_tan_3pi_8) = C::_pi_2();
|
||||
y(gt_tan_pi_8) = C::_pi_4();
|
||||
x(gt_tan_3pi_8) = -V::One() / x;
|
||||
x(gt_tan_pi_8) = (x - V::One()) / (x + V::One());
|
||||
const V &x2 = x * x;
|
||||
y += (((C::atanP(0) * x2
|
||||
- C::atanP(1)) * x2
|
||||
+ C::atanP(2)) * x2
|
||||
- C::atanP(3)) * x2 * x
|
||||
+ x;
|
||||
y(_x < V::Zero()) = -y;
|
||||
y.setQnan(isnan(_x));
|
||||
return y;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
M sign = _x < V::Zero();
|
||||
V x = abs(_x);
|
||||
M finite = isfinite(_x);
|
||||
V ret = C::_pi_2();
|
||||
V y = V::Zero();
|
||||
const M large = x > C::atanThrsHi();
|
||||
const M gt_06 = x > C::atanThrsLo();
|
||||
V tmp = (x - V::One()) / (x + V::One());
|
||||
tmp(large) = -V::One() / x;
|
||||
x(gt_06) = tmp;
|
||||
y(gt_06) = C::_pi_4();
|
||||
y(large) = C::_pi_2();
|
||||
V z = x * x;
|
||||
const V p = (((C::atanP(0) * z + C::atanP(1)) * z + C::atanP(2)) * z + C::atanP(3)) * z + C::atanP(4);
|
||||
const V q = ((((z + C::atanQ(0)) * z + C::atanQ(1)) * z + C::atanQ(2)) * z + C::atanQ(3)) * z + C::atanQ(4);
|
||||
z = z * p / q;
|
||||
z = x * z + x;
|
||||
V morebits = C::_pi_2_rem();
|
||||
morebits(!large) *= C::_1_2();
|
||||
z(gt_06) += morebits;
|
||||
ret(finite) = y + z;
|
||||
ret(sign) = -ret;
|
||||
ret.setQnan(isnan(_x));
|
||||
return ret;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan2(const Vc::float_v &y,
|
||||
const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(C::_pi(), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan2 (const Vc::double_v &y, const Vc::double_v &x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(V(C::_pi()), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_SSE+XOP+FMA4.cpp
vendored
Normal file
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_SSE+XOP+FMA4.cpp
vendored
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2012-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
// enable bit operators for easier portable bit manipulation on floats
|
||||
#define Vc_ENABLE_FLOAT_BIT_OPERATORS 1
|
||||
|
||||
#include <Vc/vector.h>
|
||||
#if defined(Vc_IMPL_SSE) || defined(Vc_IMPL_AVX)
|
||||
#include <Vc/common/macros.h>
|
||||
//#include <Vc/IO>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Vc::Vector;
|
||||
template <typename T, typename Abi>
|
||||
using Const = typename std::conditional<std::is_same<Abi, VectorAbi::Avx>::value,
|
||||
AVX::Const<T>, SSE::Const<T>>::type;
|
||||
|
||||
template <typename V>
|
||||
using best_int_v_for =
|
||||
typename std::conditional<(V::size() <= Vector<int, VectorAbi::Best<int>>::size()),
|
||||
Vector<int, VectorAbi::Best<int>>,
|
||||
SimdArray<int, V::size()>>::type;
|
||||
template <typename Abi> using float_int_v = best_int_v_for<Vector<float, Abi>>;
|
||||
template <typename Abi> using double_int_v = best_int_v_for<Vector<double, Abi>>;
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> cosSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant< 1, 0x500000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x360800, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x2AAAAB, -5>(); // 1/4!
|
||||
return y * (x2 * x2) - .5f * x2 + 1.f;
|
||||
// alternative (appears neither faster nor more precise):
|
||||
// return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> cosSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant< 1, 0xAC00000000000, -45>(); // 1/16!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x9394000000000, -37>(); // -1/14!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1EED8C0000000, -29>(); // 1/12!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x27E4FB7400000, -22>(); // -1/10!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0xA01A01A018000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x6C16C16C16C00, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x5555555555554, -5>(); // 1/4!
|
||||
return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> sinSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant<-1, 0x4E6000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x088880, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x2AAAAB, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> sinSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
// x = [0, 0.7854 = pi/4]
|
||||
// x² = [0, 0.6169 = pi²/8]
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant<-1, 0xACF0000000000, -41>(); // -1/15!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x6124400000000, -33>(); // 1/13!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xAE64567000000, -26>(); // -1/11!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x71DE3A5540000, -19>(); // 1/9!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xA01A01A01A000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1111111111110, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x5555555555555, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
/**\internal
|
||||
* Fold \p x into [-¼π, ¼π] and remember the quadrant it came from:
|
||||
* quadrant 0: [-¼π, ¼π]
|
||||
* quadrant 1: [ ¼π, ¾π]
|
||||
* quadrant 2: [ ¾π, 1¼π]
|
||||
* quadrant 3: [1¼π, 1¾π]
|
||||
*
|
||||
* The algorithm determines `y` as the multiple `x - y * ¼π = [-¼π, ¼π]`. Using a bitmask,
|
||||
* `y` is reduced to `quadrant`. `y` can be calculated as
|
||||
* ```
|
||||
* y = trunc(x / ¼π);
|
||||
* y += fmod(y, 2);
|
||||
* ```
|
||||
* This can be simplified by moving the (implicit) division by 2 into the truncation
|
||||
* expression. The `+= fmod` effect can the be achieved by using rounding instead of
|
||||
* truncation:
|
||||
* `y = round(x / ½π) * 2`.
|
||||
* If precision allows, `2/π * x` is better (faster).
|
||||
*/
|
||||
template <class T, class Abi> struct folded {
|
||||
Vector<T, Abi> x, quadrant;
|
||||
};
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<float, Abi> foldInput(const Vector<float, Abi> &x)
|
||||
{
|
||||
using V = Vector<float, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<float, Abi>;
|
||||
|
||||
folded<float, Abi> r;
|
||||
r.x = abs(x);
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < C::_pi_4()))) {
|
||||
r.quadrant = 0;
|
||||
} else if (Vc_IS_LIKELY(all_of(r.x < 33 * C::_pi_4()))) {
|
||||
const float _2_over_pi = Vc::Detail::floatConstant<1, 0x22F983, -1>(); // ½π
|
||||
V y = round(r.x * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // y mod 4
|
||||
r.x -= y * Vc::Detail::floatConstant<1, 0x490fe0, 0>(); // pi/2
|
||||
r.x -= y * Vc::Detail::floatConstant<-1, 0x2bbbd3, -21>(); // pi/2 remainder
|
||||
} else {
|
||||
typedef SimdArray<double, V::size()> VD;
|
||||
VD xd = simd_cast<VD>(abs(r.x));
|
||||
constexpr double _2_over_pi =
|
||||
Vc::Detail::doubleConstant<1, 0x45F306DC9C883, -1>();
|
||||
VD y = round(xd * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // = y mod 4
|
||||
constexpr double pi_over_2 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, 0>();
|
||||
r.x = simd_cast<V>(xd - y * pi_over_2);
|
||||
}
|
||||
//std::cout << std::hexfloat << r.x << ' ' << r.quadrant << std::defaultfloat << '\n';
|
||||
return r;
|
||||
|
||||
/*
|
||||
More alternatives to avoid double precision calculation follow. Their main problem
|
||||
is unfortunate rounding on intermediate values of x (after subtraction):
|
||||
|
||||
if (all_of(y <= 0x1000)) {
|
||||
// 0 <= x <= 0x1.921fb6p12
|
||||
// π/4 - 12 bits precision (12 zero bits):
|
||||
//const auto _pi_4_1 = Vc::Detail::floatConstant< 1, 0x491000, -1>();
|
||||
//const auto _pi_4_2 = Vc::Detail::floatConstant<-1, 0x157000, -19>();
|
||||
//const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x6F4B9F, -32>();
|
||||
x -= y * C::_pi_4_hi();
|
||||
x -= y * C::_pi_4_rem1();
|
||||
x -= y * C::_pi_4_rem2();
|
||||
} else { //if (all_of(y <= 0x10000)) {
|
||||
// consider y = 0x1.fffep+16 / x = 0x1.921e24p+16
|
||||
// critical input: 0x1.921fb6p+12 <= x <= 0x1.921fb6p+16
|
||||
// π/4 - 8 bits precision (16 zero bits):
|
||||
const auto _pi_4_1 = Vc::Detail::floatConstant<+1, 0x490000, -1>();
|
||||
const auto _pi_4_2 = Vc::Detail::floatConstant<+1, 0x7E0000, -13>();
|
||||
const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x2C0000, -22>();
|
||||
const auto _pi_4_4 = Vc::Detail::floatConstant<+1, 0x085A31, -31>();
|
||||
x -= y * _pi_4_1; // (x -= 0x1.91fe6ep+16) -> 0x1.fb6000p+4
|
||||
x -= y * _pi_4_2; // (x -= 0x1.fbfe04p+4) -> -0x1.3c0800p-6
|
||||
x -= y * _pi_4_3; // (x -= 0x1.57fea8p-5) -> 0x1.bf5a80p-9
|
||||
x -= y * _pi_4_4; // (x -= 0x1.10b352p-14) -> 0x1.b6e4e6p-9
|
||||
x.setQnan(y > 0x10000);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<double, Abi> foldInput(const Vector<double, Abi> &x)
|
||||
{
|
||||
using V = Vector<double, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<double, Abi> ;
|
||||
|
||||
folded<double, Abi> r;
|
||||
r.x = abs(x);
|
||||
constexpr double pi_over_4 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, -1>();
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < pi_over_4))) {
|
||||
r.quadrant = 0;
|
||||
return r;
|
||||
}
|
||||
const V y = round(r.x / (2 * pi_over_4));
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3);
|
||||
|
||||
if (Vc_IS_LIKELY(all_of(r.x < 1025 * pi_over_4))) {
|
||||
// x - y * pi/2, y uses no more than 11 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x921FB54443000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<-1, 0x73DCB3B39A000, -43>();
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x45C06E0E68948, -86>();
|
||||
} else if (Vc_IS_LIKELY(all_of(y <= Vc::Detail::doubleConstant<1, 0, 30>()))) {
|
||||
// x - y * pi/2, y uses no more than 29 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x921FB40000000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x4442D00000000, -24>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x8469898CC5170, -48>();
|
||||
} else {
|
||||
// x - y * pi/2, y may require all mantissa bits
|
||||
const V y_hi = y & C::highMask(26);
|
||||
const V y_lo = y - y_hi;
|
||||
const auto _pi_2_1 = Vc::Detail::doubleConstant<1, 0x921FB50000000, 0>();
|
||||
const auto _pi_2_2 = Vc::Detail::doubleConstant<1, 0x110B460000000, -26>();
|
||||
const auto _pi_2_3 = Vc::Detail::doubleConstant<1, 0x1A62630000000, -54>();
|
||||
const auto _pi_2_4 = Vc::Detail::doubleConstant<1, 0x8A2E03707344A, -81>();
|
||||
/*
|
||||
std::cout << std::hexfloat;
|
||||
std::cout
|
||||
<< y_hi << '\n'
|
||||
<< y_lo << '\n'
|
||||
<< _pi_2_1 << '\n'
|
||||
<< _pi_2_2 << '\n'
|
||||
<< _pi_2_3 << '\n'
|
||||
<< y_hi * _pi_2_1 << '\n'
|
||||
<< y_hi * _pi_2_2 << '\n'
|
||||
<< y_hi * _pi_2_3 << '\n'
|
||||
<< y * _pi_2_4 << '\n'
|
||||
<< y_lo * _pi_2_1 << '\n'
|
||||
<< y_lo * _pi_2_2 << '\n'
|
||||
<< y_lo * _pi_2_3 << '\n';
|
||||
std::cout << std::defaultfloat;
|
||||
*/
|
||||
r.x = r.x
|
||||
- y_hi * _pi_2_1
|
||||
- max(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- min(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- max(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- min(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- max(y * _pi_2_4, y_lo * _pi_2_3)
|
||||
- min(y * _pi_2_4, y_lo * _pi_2_3);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr double signmask = -0.;
|
||||
constexpr float signmaskf = -0.f;
|
||||
} // anonymous namespace
|
||||
|
||||
/*
|
||||
* algorithm for sine and cosine:
|
||||
*
|
||||
* The result can be calculated with sine or cosine depending on the π/4 section the input is
|
||||
* in.
|
||||
* sine ≈ x + x³
|
||||
* cosine ≈ 1 - x²
|
||||
*
|
||||
* sine:
|
||||
* Map -x to x and invert the output
|
||||
* Extend precision of x - n * π/4 by calculating
|
||||
* ((x - n * p1) - n * p2) - n * p3 (p1 + p2 + p3 = π/4)
|
||||
*
|
||||
* Calculate Taylor series with tuned coefficients.
|
||||
* Fix sign.
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | sinSeries
|
||||
// 1 | cosSeries
|
||||
// 2 | sinSeries, sign flip
|
||||
// 3 | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 527449))) {
|
||||
return simd_cast<V>(sin(simd_cast<Vc::double_v, 0>(x)),
|
||||
sin(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | cosSeries, +
|
||||
// 1 | sinSeries, -
|
||||
// 2 | cosSeries, -
|
||||
// 3 | sinSeries, +
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
return simd_cast<V>(cos(simd_cast<Vc::double_v, 0>(x)),
|
||||
cos(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::double_v &x, Vc::double_v *s,
|
||||
Vc::double_v *c)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | cosine | sine
|
||||
// 0 | cosSeries, + | sinSeries
|
||||
// 1 | sinSeries, - | cosSeries
|
||||
// 2 | cosSeries, - | sinSeries, sign flip
|
||||
// 3 | sinSeries, + | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::float_v &x, Vc::float_v *s,
|
||||
Vc::float_v *c)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
Vc::double_v s0, s1, c0, c1;
|
||||
sincos(simd_cast<Vc::double_v, 0>(x), &s0, &c0);
|
||||
sincos(simd_cast<Vc::double_v, 1>(x), &s1, &c1);
|
||||
*s = simd_cast<V>(s0, s1);
|
||||
*c = simd_cast<V>(c0, c1);
|
||||
return;
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::asin(const Vc::float_v &_x)
|
||||
{
|
||||
typedef Vc::float_v V;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M &negative = _x < V::Zero();
|
||||
|
||||
const V &a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M &small = a < C::smallAsinInput();
|
||||
const M >_0_5 = a > C::_1_2();
|
||||
V x = a;
|
||||
V z = a * a;
|
||||
z(gt_0_5) = (V::One() - a) * C::_1_2();
|
||||
x(gt_0_5) = sqrt(z);
|
||||
z = ((((C::asinCoeff0(0) * z
|
||||
+ C::asinCoeff0(1)) * z
|
||||
+ C::asinCoeff0(2)) * z
|
||||
+ C::asinCoeff0(3)) * z
|
||||
+ C::asinCoeff0(4)) * z * x
|
||||
+ x;
|
||||
z(gt_0_5) = C::_pi_2() - (z + z);
|
||||
z(small) = a;
|
||||
z(negative) = -z;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::asin (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M negative = _x < V::Zero();
|
||||
|
||||
const V a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M small = a < C::smallAsinInput();
|
||||
const M large = a > C::largeAsinInput();
|
||||
|
||||
V zz = V::One() - a;
|
||||
const V r = (((C::asinCoeff0(0) * zz + C::asinCoeff0(1)) * zz + C::asinCoeff0(2)) * zz +
|
||||
C::asinCoeff0(3)) * zz + C::asinCoeff0(4);
|
||||
const V s = (((zz + C::asinCoeff1(0)) * zz + C::asinCoeff1(1)) * zz +
|
||||
C::asinCoeff1(2)) * zz + C::asinCoeff1(3);
|
||||
V sqrtzz = sqrt(zz + zz);
|
||||
V z = C::_pi_4() - sqrtzz;
|
||||
z -= sqrtzz * (zz * r / s) - C::_pi_2_rem();
|
||||
z += C::_pi_4();
|
||||
|
||||
V a2 = a * a;
|
||||
const V p = ((((C::asinCoeff2(0) * a2 + C::asinCoeff2(1)) * a2 + C::asinCoeff2(2)) * a2 +
|
||||
C::asinCoeff2(3)) * a2 + C::asinCoeff2(4)) * a2 + C::asinCoeff2(5);
|
||||
const V q = ((((a2 + C::asinCoeff3(0)) * a2 + C::asinCoeff3(1)) * a2 +
|
||||
C::asinCoeff3(2)) * a2 + C::asinCoeff3(3)) * a2 + C::asinCoeff3(4);
|
||||
z(!large) = a * (a2 * p / q) + a;
|
||||
|
||||
z(negative) = -z;
|
||||
z(small) = _x;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan(const Vc::float_v &_x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
V x = abs(_x);
|
||||
const M >_tan_3pi_8 = x > C::atanThrsHi();
|
||||
const M >_tan_pi_8 = x > C::atanThrsLo() && !gt_tan_3pi_8;
|
||||
V y = V::Zero();
|
||||
y(gt_tan_3pi_8) = C::_pi_2();
|
||||
y(gt_tan_pi_8) = C::_pi_4();
|
||||
x(gt_tan_3pi_8) = -V::One() / x;
|
||||
x(gt_tan_pi_8) = (x - V::One()) / (x + V::One());
|
||||
const V &x2 = x * x;
|
||||
y += (((C::atanP(0) * x2
|
||||
- C::atanP(1)) * x2
|
||||
+ C::atanP(2)) * x2
|
||||
- C::atanP(3)) * x2 * x
|
||||
+ x;
|
||||
y(_x < V::Zero()) = -y;
|
||||
y.setQnan(isnan(_x));
|
||||
return y;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
M sign = _x < V::Zero();
|
||||
V x = abs(_x);
|
||||
M finite = isfinite(_x);
|
||||
V ret = C::_pi_2();
|
||||
V y = V::Zero();
|
||||
const M large = x > C::atanThrsHi();
|
||||
const M gt_06 = x > C::atanThrsLo();
|
||||
V tmp = (x - V::One()) / (x + V::One());
|
||||
tmp(large) = -V::One() / x;
|
||||
x(gt_06) = tmp;
|
||||
y(gt_06) = C::_pi_4();
|
||||
y(large) = C::_pi_2();
|
||||
V z = x * x;
|
||||
const V p = (((C::atanP(0) * z + C::atanP(1)) * z + C::atanP(2)) * z + C::atanP(3)) * z + C::atanP(4);
|
||||
const V q = ((((z + C::atanQ(0)) * z + C::atanQ(1)) * z + C::atanQ(2)) * z + C::atanQ(3)) * z + C::atanQ(4);
|
||||
z = z * p / q;
|
||||
z = x * z + x;
|
||||
V morebits = C::_pi_2_rem();
|
||||
morebits(!large) *= C::_1_2();
|
||||
z(gt_06) += morebits;
|
||||
ret(finite) = y + z;
|
||||
ret(sign) = -ret;
|
||||
ret.setQnan(isnan(_x));
|
||||
return ret;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan2(const Vc::float_v &y,
|
||||
const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(C::_pi(), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan2 (const Vc::double_v &y, const Vc::double_v &x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(V(C::_pi()), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_SSE2.cpp
vendored
Normal file
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_SSE2.cpp
vendored
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2012-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
// enable bit operators for easier portable bit manipulation on floats
|
||||
#define Vc_ENABLE_FLOAT_BIT_OPERATORS 1
|
||||
|
||||
#include <Vc/vector.h>
|
||||
#if defined(Vc_IMPL_SSE) || defined(Vc_IMPL_AVX)
|
||||
#include <Vc/common/macros.h>
|
||||
//#include <Vc/IO>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Vc::Vector;
|
||||
template <typename T, typename Abi>
|
||||
using Const = typename std::conditional<std::is_same<Abi, VectorAbi::Avx>::value,
|
||||
AVX::Const<T>, SSE::Const<T>>::type;
|
||||
|
||||
template <typename V>
|
||||
using best_int_v_for =
|
||||
typename std::conditional<(V::size() <= Vector<int, VectorAbi::Best<int>>::size()),
|
||||
Vector<int, VectorAbi::Best<int>>,
|
||||
SimdArray<int, V::size()>>::type;
|
||||
template <typename Abi> using float_int_v = best_int_v_for<Vector<float, Abi>>;
|
||||
template <typename Abi> using double_int_v = best_int_v_for<Vector<double, Abi>>;
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> cosSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant< 1, 0x500000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x360800, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x2AAAAB, -5>(); // 1/4!
|
||||
return y * (x2 * x2) - .5f * x2 + 1.f;
|
||||
// alternative (appears neither faster nor more precise):
|
||||
// return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> cosSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant< 1, 0xAC00000000000, -45>(); // 1/16!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x9394000000000, -37>(); // -1/14!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1EED8C0000000, -29>(); // 1/12!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x27E4FB7400000, -22>(); // -1/10!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0xA01A01A018000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x6C16C16C16C00, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x5555555555554, -5>(); // 1/4!
|
||||
return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> sinSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant<-1, 0x4E6000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x088880, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x2AAAAB, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> sinSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
// x = [0, 0.7854 = pi/4]
|
||||
// x² = [0, 0.6169 = pi²/8]
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant<-1, 0xACF0000000000, -41>(); // -1/15!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x6124400000000, -33>(); // 1/13!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xAE64567000000, -26>(); // -1/11!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x71DE3A5540000, -19>(); // 1/9!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xA01A01A01A000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1111111111110, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x5555555555555, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
/**\internal
|
||||
* Fold \p x into [-¼π, ¼π] and remember the quadrant it came from:
|
||||
* quadrant 0: [-¼π, ¼π]
|
||||
* quadrant 1: [ ¼π, ¾π]
|
||||
* quadrant 2: [ ¾π, 1¼π]
|
||||
* quadrant 3: [1¼π, 1¾π]
|
||||
*
|
||||
* The algorithm determines `y` as the multiple `x - y * ¼π = [-¼π, ¼π]`. Using a bitmask,
|
||||
* `y` is reduced to `quadrant`. `y` can be calculated as
|
||||
* ```
|
||||
* y = trunc(x / ¼π);
|
||||
* y += fmod(y, 2);
|
||||
* ```
|
||||
* This can be simplified by moving the (implicit) division by 2 into the truncation
|
||||
* expression. The `+= fmod` effect can the be achieved by using rounding instead of
|
||||
* truncation:
|
||||
* `y = round(x / ½π) * 2`.
|
||||
* If precision allows, `2/π * x` is better (faster).
|
||||
*/
|
||||
template <class T, class Abi> struct folded {
|
||||
Vector<T, Abi> x, quadrant;
|
||||
};
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<float, Abi> foldInput(const Vector<float, Abi> &x)
|
||||
{
|
||||
using V = Vector<float, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<float, Abi>;
|
||||
|
||||
folded<float, Abi> r;
|
||||
r.x = abs(x);
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < C::_pi_4()))) {
|
||||
r.quadrant = 0;
|
||||
} else if (Vc_IS_LIKELY(all_of(r.x < 33 * C::_pi_4()))) {
|
||||
const float _2_over_pi = Vc::Detail::floatConstant<1, 0x22F983, -1>(); // ½π
|
||||
V y = round(r.x * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // y mod 4
|
||||
r.x -= y * Vc::Detail::floatConstant<1, 0x490fe0, 0>(); // pi/2
|
||||
r.x -= y * Vc::Detail::floatConstant<-1, 0x2bbbd3, -21>(); // pi/2 remainder
|
||||
} else {
|
||||
typedef SimdArray<double, V::size()> VD;
|
||||
VD xd = simd_cast<VD>(abs(r.x));
|
||||
constexpr double _2_over_pi =
|
||||
Vc::Detail::doubleConstant<1, 0x45F306DC9C883, -1>();
|
||||
VD y = round(xd * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // = y mod 4
|
||||
constexpr double pi_over_2 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, 0>();
|
||||
r.x = simd_cast<V>(xd - y * pi_over_2);
|
||||
}
|
||||
//std::cout << std::hexfloat << r.x << ' ' << r.quadrant << std::defaultfloat << '\n';
|
||||
return r;
|
||||
|
||||
/*
|
||||
More alternatives to avoid double precision calculation follow. Their main problem
|
||||
is unfortunate rounding on intermediate values of x (after subtraction):
|
||||
|
||||
if (all_of(y <= 0x1000)) {
|
||||
// 0 <= x <= 0x1.921fb6p12
|
||||
// π/4 - 12 bits precision (12 zero bits):
|
||||
//const auto _pi_4_1 = Vc::Detail::floatConstant< 1, 0x491000, -1>();
|
||||
//const auto _pi_4_2 = Vc::Detail::floatConstant<-1, 0x157000, -19>();
|
||||
//const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x6F4B9F, -32>();
|
||||
x -= y * C::_pi_4_hi();
|
||||
x -= y * C::_pi_4_rem1();
|
||||
x -= y * C::_pi_4_rem2();
|
||||
} else { //if (all_of(y <= 0x10000)) {
|
||||
// consider y = 0x1.fffep+16 / x = 0x1.921e24p+16
|
||||
// critical input: 0x1.921fb6p+12 <= x <= 0x1.921fb6p+16
|
||||
// π/4 - 8 bits precision (16 zero bits):
|
||||
const auto _pi_4_1 = Vc::Detail::floatConstant<+1, 0x490000, -1>();
|
||||
const auto _pi_4_2 = Vc::Detail::floatConstant<+1, 0x7E0000, -13>();
|
||||
const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x2C0000, -22>();
|
||||
const auto _pi_4_4 = Vc::Detail::floatConstant<+1, 0x085A31, -31>();
|
||||
x -= y * _pi_4_1; // (x -= 0x1.91fe6ep+16) -> 0x1.fb6000p+4
|
||||
x -= y * _pi_4_2; // (x -= 0x1.fbfe04p+4) -> -0x1.3c0800p-6
|
||||
x -= y * _pi_4_3; // (x -= 0x1.57fea8p-5) -> 0x1.bf5a80p-9
|
||||
x -= y * _pi_4_4; // (x -= 0x1.10b352p-14) -> 0x1.b6e4e6p-9
|
||||
x.setQnan(y > 0x10000);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<double, Abi> foldInput(const Vector<double, Abi> &x)
|
||||
{
|
||||
using V = Vector<double, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<double, Abi> ;
|
||||
|
||||
folded<double, Abi> r;
|
||||
r.x = abs(x);
|
||||
constexpr double pi_over_4 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, -1>();
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < pi_over_4))) {
|
||||
r.quadrant = 0;
|
||||
return r;
|
||||
}
|
||||
const V y = round(r.x / (2 * pi_over_4));
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3);
|
||||
|
||||
if (Vc_IS_LIKELY(all_of(r.x < 1025 * pi_over_4))) {
|
||||
// x - y * pi/2, y uses no more than 11 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x921FB54443000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<-1, 0x73DCB3B39A000, -43>();
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x45C06E0E68948, -86>();
|
||||
} else if (Vc_IS_LIKELY(all_of(y <= Vc::Detail::doubleConstant<1, 0, 30>()))) {
|
||||
// x - y * pi/2, y uses no more than 29 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x921FB40000000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x4442D00000000, -24>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x8469898CC5170, -48>();
|
||||
} else {
|
||||
// x - y * pi/2, y may require all mantissa bits
|
||||
const V y_hi = y & C::highMask(26);
|
||||
const V y_lo = y - y_hi;
|
||||
const auto _pi_2_1 = Vc::Detail::doubleConstant<1, 0x921FB50000000, 0>();
|
||||
const auto _pi_2_2 = Vc::Detail::doubleConstant<1, 0x110B460000000, -26>();
|
||||
const auto _pi_2_3 = Vc::Detail::doubleConstant<1, 0x1A62630000000, -54>();
|
||||
const auto _pi_2_4 = Vc::Detail::doubleConstant<1, 0x8A2E03707344A, -81>();
|
||||
/*
|
||||
std::cout << std::hexfloat;
|
||||
std::cout
|
||||
<< y_hi << '\n'
|
||||
<< y_lo << '\n'
|
||||
<< _pi_2_1 << '\n'
|
||||
<< _pi_2_2 << '\n'
|
||||
<< _pi_2_3 << '\n'
|
||||
<< y_hi * _pi_2_1 << '\n'
|
||||
<< y_hi * _pi_2_2 << '\n'
|
||||
<< y_hi * _pi_2_3 << '\n'
|
||||
<< y * _pi_2_4 << '\n'
|
||||
<< y_lo * _pi_2_1 << '\n'
|
||||
<< y_lo * _pi_2_2 << '\n'
|
||||
<< y_lo * _pi_2_3 << '\n';
|
||||
std::cout << std::defaultfloat;
|
||||
*/
|
||||
r.x = r.x
|
||||
- y_hi * _pi_2_1
|
||||
- max(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- min(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- max(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- min(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- max(y * _pi_2_4, y_lo * _pi_2_3)
|
||||
- min(y * _pi_2_4, y_lo * _pi_2_3);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr double signmask = -0.;
|
||||
constexpr float signmaskf = -0.f;
|
||||
} // anonymous namespace
|
||||
|
||||
/*
|
||||
* algorithm for sine and cosine:
|
||||
*
|
||||
* The result can be calculated with sine or cosine depending on the π/4 section the input is
|
||||
* in.
|
||||
* sine ≈ x + x³
|
||||
* cosine ≈ 1 - x²
|
||||
*
|
||||
* sine:
|
||||
* Map -x to x and invert the output
|
||||
* Extend precision of x - n * π/4 by calculating
|
||||
* ((x - n * p1) - n * p2) - n * p3 (p1 + p2 + p3 = π/4)
|
||||
*
|
||||
* Calculate Taylor series with tuned coefficients.
|
||||
* Fix sign.
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | sinSeries
|
||||
// 1 | cosSeries
|
||||
// 2 | sinSeries, sign flip
|
||||
// 3 | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 527449))) {
|
||||
return simd_cast<V>(sin(simd_cast<Vc::double_v, 0>(x)),
|
||||
sin(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | cosSeries, +
|
||||
// 1 | sinSeries, -
|
||||
// 2 | cosSeries, -
|
||||
// 3 | sinSeries, +
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
return simd_cast<V>(cos(simd_cast<Vc::double_v, 0>(x)),
|
||||
cos(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::double_v &x, Vc::double_v *s,
|
||||
Vc::double_v *c)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | cosine | sine
|
||||
// 0 | cosSeries, + | sinSeries
|
||||
// 1 | sinSeries, - | cosSeries
|
||||
// 2 | cosSeries, - | sinSeries, sign flip
|
||||
// 3 | sinSeries, + | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::float_v &x, Vc::float_v *s,
|
||||
Vc::float_v *c)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
Vc::double_v s0, s1, c0, c1;
|
||||
sincos(simd_cast<Vc::double_v, 0>(x), &s0, &c0);
|
||||
sincos(simd_cast<Vc::double_v, 1>(x), &s1, &c1);
|
||||
*s = simd_cast<V>(s0, s1);
|
||||
*c = simd_cast<V>(c0, c1);
|
||||
return;
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::asin(const Vc::float_v &_x)
|
||||
{
|
||||
typedef Vc::float_v V;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M &negative = _x < V::Zero();
|
||||
|
||||
const V &a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M &small = a < C::smallAsinInput();
|
||||
const M >_0_5 = a > C::_1_2();
|
||||
V x = a;
|
||||
V z = a * a;
|
||||
z(gt_0_5) = (V::One() - a) * C::_1_2();
|
||||
x(gt_0_5) = sqrt(z);
|
||||
z = ((((C::asinCoeff0(0) * z
|
||||
+ C::asinCoeff0(1)) * z
|
||||
+ C::asinCoeff0(2)) * z
|
||||
+ C::asinCoeff0(3)) * z
|
||||
+ C::asinCoeff0(4)) * z * x
|
||||
+ x;
|
||||
z(gt_0_5) = C::_pi_2() - (z + z);
|
||||
z(small) = a;
|
||||
z(negative) = -z;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::asin (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M negative = _x < V::Zero();
|
||||
|
||||
const V a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M small = a < C::smallAsinInput();
|
||||
const M large = a > C::largeAsinInput();
|
||||
|
||||
V zz = V::One() - a;
|
||||
const V r = (((C::asinCoeff0(0) * zz + C::asinCoeff0(1)) * zz + C::asinCoeff0(2)) * zz +
|
||||
C::asinCoeff0(3)) * zz + C::asinCoeff0(4);
|
||||
const V s = (((zz + C::asinCoeff1(0)) * zz + C::asinCoeff1(1)) * zz +
|
||||
C::asinCoeff1(2)) * zz + C::asinCoeff1(3);
|
||||
V sqrtzz = sqrt(zz + zz);
|
||||
V z = C::_pi_4() - sqrtzz;
|
||||
z -= sqrtzz * (zz * r / s) - C::_pi_2_rem();
|
||||
z += C::_pi_4();
|
||||
|
||||
V a2 = a * a;
|
||||
const V p = ((((C::asinCoeff2(0) * a2 + C::asinCoeff2(1)) * a2 + C::asinCoeff2(2)) * a2 +
|
||||
C::asinCoeff2(3)) * a2 + C::asinCoeff2(4)) * a2 + C::asinCoeff2(5);
|
||||
const V q = ((((a2 + C::asinCoeff3(0)) * a2 + C::asinCoeff3(1)) * a2 +
|
||||
C::asinCoeff3(2)) * a2 + C::asinCoeff3(3)) * a2 + C::asinCoeff3(4);
|
||||
z(!large) = a * (a2 * p / q) + a;
|
||||
|
||||
z(negative) = -z;
|
||||
z(small) = _x;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan(const Vc::float_v &_x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
V x = abs(_x);
|
||||
const M >_tan_3pi_8 = x > C::atanThrsHi();
|
||||
const M >_tan_pi_8 = x > C::atanThrsLo() && !gt_tan_3pi_8;
|
||||
V y = V::Zero();
|
||||
y(gt_tan_3pi_8) = C::_pi_2();
|
||||
y(gt_tan_pi_8) = C::_pi_4();
|
||||
x(gt_tan_3pi_8) = -V::One() / x;
|
||||
x(gt_tan_pi_8) = (x - V::One()) / (x + V::One());
|
||||
const V &x2 = x * x;
|
||||
y += (((C::atanP(0) * x2
|
||||
- C::atanP(1)) * x2
|
||||
+ C::atanP(2)) * x2
|
||||
- C::atanP(3)) * x2 * x
|
||||
+ x;
|
||||
y(_x < V::Zero()) = -y;
|
||||
y.setQnan(isnan(_x));
|
||||
return y;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
M sign = _x < V::Zero();
|
||||
V x = abs(_x);
|
||||
M finite = isfinite(_x);
|
||||
V ret = C::_pi_2();
|
||||
V y = V::Zero();
|
||||
const M large = x > C::atanThrsHi();
|
||||
const M gt_06 = x > C::atanThrsLo();
|
||||
V tmp = (x - V::One()) / (x + V::One());
|
||||
tmp(large) = -V::One() / x;
|
||||
x(gt_06) = tmp;
|
||||
y(gt_06) = C::_pi_4();
|
||||
y(large) = C::_pi_2();
|
||||
V z = x * x;
|
||||
const V p = (((C::atanP(0) * z + C::atanP(1)) * z + C::atanP(2)) * z + C::atanP(3)) * z + C::atanP(4);
|
||||
const V q = ((((z + C::atanQ(0)) * z + C::atanQ(1)) * z + C::atanQ(2)) * z + C::atanQ(3)) * z + C::atanQ(4);
|
||||
z = z * p / q;
|
||||
z = x * z + x;
|
||||
V morebits = C::_pi_2_rem();
|
||||
morebits(!large) *= C::_1_2();
|
||||
z(gt_06) += morebits;
|
||||
ret(finite) = y + z;
|
||||
ret(sign) = -ret;
|
||||
ret.setQnan(isnan(_x));
|
||||
return ret;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan2(const Vc::float_v &y,
|
||||
const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(C::_pi(), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan2 (const Vc::double_v &y, const Vc::double_v &x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(V(C::_pi()), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_SSE3.cpp
vendored
Normal file
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_SSE3.cpp
vendored
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2012-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
// enable bit operators for easier portable bit manipulation on floats
|
||||
#define Vc_ENABLE_FLOAT_BIT_OPERATORS 1
|
||||
|
||||
#include <Vc/vector.h>
|
||||
#if defined(Vc_IMPL_SSE) || defined(Vc_IMPL_AVX)
|
||||
#include <Vc/common/macros.h>
|
||||
//#include <Vc/IO>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Vc::Vector;
|
||||
template <typename T, typename Abi>
|
||||
using Const = typename std::conditional<std::is_same<Abi, VectorAbi::Avx>::value,
|
||||
AVX::Const<T>, SSE::Const<T>>::type;
|
||||
|
||||
template <typename V>
|
||||
using best_int_v_for =
|
||||
typename std::conditional<(V::size() <= Vector<int, VectorAbi::Best<int>>::size()),
|
||||
Vector<int, VectorAbi::Best<int>>,
|
||||
SimdArray<int, V::size()>>::type;
|
||||
template <typename Abi> using float_int_v = best_int_v_for<Vector<float, Abi>>;
|
||||
template <typename Abi> using double_int_v = best_int_v_for<Vector<double, Abi>>;
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> cosSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant< 1, 0x500000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x360800, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x2AAAAB, -5>(); // 1/4!
|
||||
return y * (x2 * x2) - .5f * x2 + 1.f;
|
||||
// alternative (appears neither faster nor more precise):
|
||||
// return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> cosSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant< 1, 0xAC00000000000, -45>(); // 1/16!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x9394000000000, -37>(); // -1/14!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1EED8C0000000, -29>(); // 1/12!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x27E4FB7400000, -22>(); // -1/10!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0xA01A01A018000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x6C16C16C16C00, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x5555555555554, -5>(); // 1/4!
|
||||
return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> sinSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant<-1, 0x4E6000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x088880, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x2AAAAB, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> sinSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
// x = [0, 0.7854 = pi/4]
|
||||
// x² = [0, 0.6169 = pi²/8]
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant<-1, 0xACF0000000000, -41>(); // -1/15!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x6124400000000, -33>(); // 1/13!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xAE64567000000, -26>(); // -1/11!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x71DE3A5540000, -19>(); // 1/9!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xA01A01A01A000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1111111111110, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x5555555555555, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
/**\internal
|
||||
* Fold \p x into [-¼π, ¼π] and remember the quadrant it came from:
|
||||
* quadrant 0: [-¼π, ¼π]
|
||||
* quadrant 1: [ ¼π, ¾π]
|
||||
* quadrant 2: [ ¾π, 1¼π]
|
||||
* quadrant 3: [1¼π, 1¾π]
|
||||
*
|
||||
* The algorithm determines `y` as the multiple `x - y * ¼π = [-¼π, ¼π]`. Using a bitmask,
|
||||
* `y` is reduced to `quadrant`. `y` can be calculated as
|
||||
* ```
|
||||
* y = trunc(x / ¼π);
|
||||
* y += fmod(y, 2);
|
||||
* ```
|
||||
* This can be simplified by moving the (implicit) division by 2 into the truncation
|
||||
* expression. The `+= fmod` effect can the be achieved by using rounding instead of
|
||||
* truncation:
|
||||
* `y = round(x / ½π) * 2`.
|
||||
* If precision allows, `2/π * x` is better (faster).
|
||||
*/
|
||||
template <class T, class Abi> struct folded {
|
||||
Vector<T, Abi> x, quadrant;
|
||||
};
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<float, Abi> foldInput(const Vector<float, Abi> &x)
|
||||
{
|
||||
using V = Vector<float, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<float, Abi>;
|
||||
|
||||
folded<float, Abi> r;
|
||||
r.x = abs(x);
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < C::_pi_4()))) {
|
||||
r.quadrant = 0;
|
||||
} else if (Vc_IS_LIKELY(all_of(r.x < 33 * C::_pi_4()))) {
|
||||
const float _2_over_pi = Vc::Detail::floatConstant<1, 0x22F983, -1>(); // ½π
|
||||
V y = round(r.x * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // y mod 4
|
||||
r.x -= y * Vc::Detail::floatConstant<1, 0x490fe0, 0>(); // pi/2
|
||||
r.x -= y * Vc::Detail::floatConstant<-1, 0x2bbbd3, -21>(); // pi/2 remainder
|
||||
} else {
|
||||
typedef SimdArray<double, V::size()> VD;
|
||||
VD xd = simd_cast<VD>(abs(r.x));
|
||||
constexpr double _2_over_pi =
|
||||
Vc::Detail::doubleConstant<1, 0x45F306DC9C883, -1>();
|
||||
VD y = round(xd * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // = y mod 4
|
||||
constexpr double pi_over_2 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, 0>();
|
||||
r.x = simd_cast<V>(xd - y * pi_over_2);
|
||||
}
|
||||
//std::cout << std::hexfloat << r.x << ' ' << r.quadrant << std::defaultfloat << '\n';
|
||||
return r;
|
||||
|
||||
/*
|
||||
More alternatives to avoid double precision calculation follow. Their main problem
|
||||
is unfortunate rounding on intermediate values of x (after subtraction):
|
||||
|
||||
if (all_of(y <= 0x1000)) {
|
||||
// 0 <= x <= 0x1.921fb6p12
|
||||
// π/4 - 12 bits precision (12 zero bits):
|
||||
//const auto _pi_4_1 = Vc::Detail::floatConstant< 1, 0x491000, -1>();
|
||||
//const auto _pi_4_2 = Vc::Detail::floatConstant<-1, 0x157000, -19>();
|
||||
//const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x6F4B9F, -32>();
|
||||
x -= y * C::_pi_4_hi();
|
||||
x -= y * C::_pi_4_rem1();
|
||||
x -= y * C::_pi_4_rem2();
|
||||
} else { //if (all_of(y <= 0x10000)) {
|
||||
// consider y = 0x1.fffep+16 / x = 0x1.921e24p+16
|
||||
// critical input: 0x1.921fb6p+12 <= x <= 0x1.921fb6p+16
|
||||
// π/4 - 8 bits precision (16 zero bits):
|
||||
const auto _pi_4_1 = Vc::Detail::floatConstant<+1, 0x490000, -1>();
|
||||
const auto _pi_4_2 = Vc::Detail::floatConstant<+1, 0x7E0000, -13>();
|
||||
const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x2C0000, -22>();
|
||||
const auto _pi_4_4 = Vc::Detail::floatConstant<+1, 0x085A31, -31>();
|
||||
x -= y * _pi_4_1; // (x -= 0x1.91fe6ep+16) -> 0x1.fb6000p+4
|
||||
x -= y * _pi_4_2; // (x -= 0x1.fbfe04p+4) -> -0x1.3c0800p-6
|
||||
x -= y * _pi_4_3; // (x -= 0x1.57fea8p-5) -> 0x1.bf5a80p-9
|
||||
x -= y * _pi_4_4; // (x -= 0x1.10b352p-14) -> 0x1.b6e4e6p-9
|
||||
x.setQnan(y > 0x10000);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<double, Abi> foldInput(const Vector<double, Abi> &x)
|
||||
{
|
||||
using V = Vector<double, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<double, Abi> ;
|
||||
|
||||
folded<double, Abi> r;
|
||||
r.x = abs(x);
|
||||
constexpr double pi_over_4 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, -1>();
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < pi_over_4))) {
|
||||
r.quadrant = 0;
|
||||
return r;
|
||||
}
|
||||
const V y = round(r.x / (2 * pi_over_4));
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3);
|
||||
|
||||
if (Vc_IS_LIKELY(all_of(r.x < 1025 * pi_over_4))) {
|
||||
// x - y * pi/2, y uses no more than 11 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x921FB54443000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<-1, 0x73DCB3B39A000, -43>();
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x45C06E0E68948, -86>();
|
||||
} else if (Vc_IS_LIKELY(all_of(y <= Vc::Detail::doubleConstant<1, 0, 30>()))) {
|
||||
// x - y * pi/2, y uses no more than 29 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x921FB40000000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x4442D00000000, -24>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x8469898CC5170, -48>();
|
||||
} else {
|
||||
// x - y * pi/2, y may require all mantissa bits
|
||||
const V y_hi = y & C::highMask(26);
|
||||
const V y_lo = y - y_hi;
|
||||
const auto _pi_2_1 = Vc::Detail::doubleConstant<1, 0x921FB50000000, 0>();
|
||||
const auto _pi_2_2 = Vc::Detail::doubleConstant<1, 0x110B460000000, -26>();
|
||||
const auto _pi_2_3 = Vc::Detail::doubleConstant<1, 0x1A62630000000, -54>();
|
||||
const auto _pi_2_4 = Vc::Detail::doubleConstant<1, 0x8A2E03707344A, -81>();
|
||||
/*
|
||||
std::cout << std::hexfloat;
|
||||
std::cout
|
||||
<< y_hi << '\n'
|
||||
<< y_lo << '\n'
|
||||
<< _pi_2_1 << '\n'
|
||||
<< _pi_2_2 << '\n'
|
||||
<< _pi_2_3 << '\n'
|
||||
<< y_hi * _pi_2_1 << '\n'
|
||||
<< y_hi * _pi_2_2 << '\n'
|
||||
<< y_hi * _pi_2_3 << '\n'
|
||||
<< y * _pi_2_4 << '\n'
|
||||
<< y_lo * _pi_2_1 << '\n'
|
||||
<< y_lo * _pi_2_2 << '\n'
|
||||
<< y_lo * _pi_2_3 << '\n';
|
||||
std::cout << std::defaultfloat;
|
||||
*/
|
||||
r.x = r.x
|
||||
- y_hi * _pi_2_1
|
||||
- max(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- min(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- max(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- min(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- max(y * _pi_2_4, y_lo * _pi_2_3)
|
||||
- min(y * _pi_2_4, y_lo * _pi_2_3);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr double signmask = -0.;
|
||||
constexpr float signmaskf = -0.f;
|
||||
} // anonymous namespace
|
||||
|
||||
/*
|
||||
* algorithm for sine and cosine:
|
||||
*
|
||||
* The result can be calculated with sine or cosine depending on the π/4 section the input is
|
||||
* in.
|
||||
* sine ≈ x + x³
|
||||
* cosine ≈ 1 - x²
|
||||
*
|
||||
* sine:
|
||||
* Map -x to x and invert the output
|
||||
* Extend precision of x - n * π/4 by calculating
|
||||
* ((x - n * p1) - n * p2) - n * p3 (p1 + p2 + p3 = π/4)
|
||||
*
|
||||
* Calculate Taylor series with tuned coefficients.
|
||||
* Fix sign.
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | sinSeries
|
||||
// 1 | cosSeries
|
||||
// 2 | sinSeries, sign flip
|
||||
// 3 | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 527449))) {
|
||||
return simd_cast<V>(sin(simd_cast<Vc::double_v, 0>(x)),
|
||||
sin(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | cosSeries, +
|
||||
// 1 | sinSeries, -
|
||||
// 2 | cosSeries, -
|
||||
// 3 | sinSeries, +
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
return simd_cast<V>(cos(simd_cast<Vc::double_v, 0>(x)),
|
||||
cos(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::double_v &x, Vc::double_v *s,
|
||||
Vc::double_v *c)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | cosine | sine
|
||||
// 0 | cosSeries, + | sinSeries
|
||||
// 1 | sinSeries, - | cosSeries
|
||||
// 2 | cosSeries, - | sinSeries, sign flip
|
||||
// 3 | sinSeries, + | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::float_v &x, Vc::float_v *s,
|
||||
Vc::float_v *c)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
Vc::double_v s0, s1, c0, c1;
|
||||
sincos(simd_cast<Vc::double_v, 0>(x), &s0, &c0);
|
||||
sincos(simd_cast<Vc::double_v, 1>(x), &s1, &c1);
|
||||
*s = simd_cast<V>(s0, s1);
|
||||
*c = simd_cast<V>(c0, c1);
|
||||
return;
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::asin(const Vc::float_v &_x)
|
||||
{
|
||||
typedef Vc::float_v V;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M &negative = _x < V::Zero();
|
||||
|
||||
const V &a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M &small = a < C::smallAsinInput();
|
||||
const M >_0_5 = a > C::_1_2();
|
||||
V x = a;
|
||||
V z = a * a;
|
||||
z(gt_0_5) = (V::One() - a) * C::_1_2();
|
||||
x(gt_0_5) = sqrt(z);
|
||||
z = ((((C::asinCoeff0(0) * z
|
||||
+ C::asinCoeff0(1)) * z
|
||||
+ C::asinCoeff0(2)) * z
|
||||
+ C::asinCoeff0(3)) * z
|
||||
+ C::asinCoeff0(4)) * z * x
|
||||
+ x;
|
||||
z(gt_0_5) = C::_pi_2() - (z + z);
|
||||
z(small) = a;
|
||||
z(negative) = -z;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::asin (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M negative = _x < V::Zero();
|
||||
|
||||
const V a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M small = a < C::smallAsinInput();
|
||||
const M large = a > C::largeAsinInput();
|
||||
|
||||
V zz = V::One() - a;
|
||||
const V r = (((C::asinCoeff0(0) * zz + C::asinCoeff0(1)) * zz + C::asinCoeff0(2)) * zz +
|
||||
C::asinCoeff0(3)) * zz + C::asinCoeff0(4);
|
||||
const V s = (((zz + C::asinCoeff1(0)) * zz + C::asinCoeff1(1)) * zz +
|
||||
C::asinCoeff1(2)) * zz + C::asinCoeff1(3);
|
||||
V sqrtzz = sqrt(zz + zz);
|
||||
V z = C::_pi_4() - sqrtzz;
|
||||
z -= sqrtzz * (zz * r / s) - C::_pi_2_rem();
|
||||
z += C::_pi_4();
|
||||
|
||||
V a2 = a * a;
|
||||
const V p = ((((C::asinCoeff2(0) * a2 + C::asinCoeff2(1)) * a2 + C::asinCoeff2(2)) * a2 +
|
||||
C::asinCoeff2(3)) * a2 + C::asinCoeff2(4)) * a2 + C::asinCoeff2(5);
|
||||
const V q = ((((a2 + C::asinCoeff3(0)) * a2 + C::asinCoeff3(1)) * a2 +
|
||||
C::asinCoeff3(2)) * a2 + C::asinCoeff3(3)) * a2 + C::asinCoeff3(4);
|
||||
z(!large) = a * (a2 * p / q) + a;
|
||||
|
||||
z(negative) = -z;
|
||||
z(small) = _x;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan(const Vc::float_v &_x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
V x = abs(_x);
|
||||
const M >_tan_3pi_8 = x > C::atanThrsHi();
|
||||
const M >_tan_pi_8 = x > C::atanThrsLo() && !gt_tan_3pi_8;
|
||||
V y = V::Zero();
|
||||
y(gt_tan_3pi_8) = C::_pi_2();
|
||||
y(gt_tan_pi_8) = C::_pi_4();
|
||||
x(gt_tan_3pi_8) = -V::One() / x;
|
||||
x(gt_tan_pi_8) = (x - V::One()) / (x + V::One());
|
||||
const V &x2 = x * x;
|
||||
y += (((C::atanP(0) * x2
|
||||
- C::atanP(1)) * x2
|
||||
+ C::atanP(2)) * x2
|
||||
- C::atanP(3)) * x2 * x
|
||||
+ x;
|
||||
y(_x < V::Zero()) = -y;
|
||||
y.setQnan(isnan(_x));
|
||||
return y;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
M sign = _x < V::Zero();
|
||||
V x = abs(_x);
|
||||
M finite = isfinite(_x);
|
||||
V ret = C::_pi_2();
|
||||
V y = V::Zero();
|
||||
const M large = x > C::atanThrsHi();
|
||||
const M gt_06 = x > C::atanThrsLo();
|
||||
V tmp = (x - V::One()) / (x + V::One());
|
||||
tmp(large) = -V::One() / x;
|
||||
x(gt_06) = tmp;
|
||||
y(gt_06) = C::_pi_4();
|
||||
y(large) = C::_pi_2();
|
||||
V z = x * x;
|
||||
const V p = (((C::atanP(0) * z + C::atanP(1)) * z + C::atanP(2)) * z + C::atanP(3)) * z + C::atanP(4);
|
||||
const V q = ((((z + C::atanQ(0)) * z + C::atanQ(1)) * z + C::atanQ(2)) * z + C::atanQ(3)) * z + C::atanQ(4);
|
||||
z = z * p / q;
|
||||
z = x * z + x;
|
||||
V morebits = C::_pi_2_rem();
|
||||
morebits(!large) *= C::_1_2();
|
||||
z(gt_06) += morebits;
|
||||
ret(finite) = y + z;
|
||||
ret(sign) = -ret;
|
||||
ret.setQnan(isnan(_x));
|
||||
return ret;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan2(const Vc::float_v &y,
|
||||
const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(C::_pi(), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan2 (const Vc::double_v &y, const Vc::double_v &x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(V(C::_pi()), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_SSE4_1.cpp
vendored
Normal file
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_SSE4_1.cpp
vendored
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2012-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
// enable bit operators for easier portable bit manipulation on floats
|
||||
#define Vc_ENABLE_FLOAT_BIT_OPERATORS 1
|
||||
|
||||
#include <Vc/vector.h>
|
||||
#if defined(Vc_IMPL_SSE) || defined(Vc_IMPL_AVX)
|
||||
#include <Vc/common/macros.h>
|
||||
//#include <Vc/IO>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Vc::Vector;
|
||||
template <typename T, typename Abi>
|
||||
using Const = typename std::conditional<std::is_same<Abi, VectorAbi::Avx>::value,
|
||||
AVX::Const<T>, SSE::Const<T>>::type;
|
||||
|
||||
template <typename V>
|
||||
using best_int_v_for =
|
||||
typename std::conditional<(V::size() <= Vector<int, VectorAbi::Best<int>>::size()),
|
||||
Vector<int, VectorAbi::Best<int>>,
|
||||
SimdArray<int, V::size()>>::type;
|
||||
template <typename Abi> using float_int_v = best_int_v_for<Vector<float, Abi>>;
|
||||
template <typename Abi> using double_int_v = best_int_v_for<Vector<double, Abi>>;
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> cosSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant< 1, 0x500000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x360800, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x2AAAAB, -5>(); // 1/4!
|
||||
return y * (x2 * x2) - .5f * x2 + 1.f;
|
||||
// alternative (appears neither faster nor more precise):
|
||||
// return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> cosSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant< 1, 0xAC00000000000, -45>(); // 1/16!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x9394000000000, -37>(); // -1/14!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1EED8C0000000, -29>(); // 1/12!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x27E4FB7400000, -22>(); // -1/10!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0xA01A01A018000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x6C16C16C16C00, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x5555555555554, -5>(); // 1/4!
|
||||
return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> sinSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant<-1, 0x4E6000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x088880, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x2AAAAB, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> sinSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
// x = [0, 0.7854 = pi/4]
|
||||
// x² = [0, 0.6169 = pi²/8]
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant<-1, 0xACF0000000000, -41>(); // -1/15!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x6124400000000, -33>(); // 1/13!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xAE64567000000, -26>(); // -1/11!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x71DE3A5540000, -19>(); // 1/9!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xA01A01A01A000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1111111111110, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x5555555555555, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
/**\internal
|
||||
* Fold \p x into [-¼π, ¼π] and remember the quadrant it came from:
|
||||
* quadrant 0: [-¼π, ¼π]
|
||||
* quadrant 1: [ ¼π, ¾π]
|
||||
* quadrant 2: [ ¾π, 1¼π]
|
||||
* quadrant 3: [1¼π, 1¾π]
|
||||
*
|
||||
* The algorithm determines `y` as the multiple `x - y * ¼π = [-¼π, ¼π]`. Using a bitmask,
|
||||
* `y` is reduced to `quadrant`. `y` can be calculated as
|
||||
* ```
|
||||
* y = trunc(x / ¼π);
|
||||
* y += fmod(y, 2);
|
||||
* ```
|
||||
* This can be simplified by moving the (implicit) division by 2 into the truncation
|
||||
* expression. The `+= fmod` effect can the be achieved by using rounding instead of
|
||||
* truncation:
|
||||
* `y = round(x / ½π) * 2`.
|
||||
* If precision allows, `2/π * x` is better (faster).
|
||||
*/
|
||||
template <class T, class Abi> struct folded {
|
||||
Vector<T, Abi> x, quadrant;
|
||||
};
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<float, Abi> foldInput(const Vector<float, Abi> &x)
|
||||
{
|
||||
using V = Vector<float, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<float, Abi>;
|
||||
|
||||
folded<float, Abi> r;
|
||||
r.x = abs(x);
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < C::_pi_4()))) {
|
||||
r.quadrant = 0;
|
||||
} else if (Vc_IS_LIKELY(all_of(r.x < 33 * C::_pi_4()))) {
|
||||
const float _2_over_pi = Vc::Detail::floatConstant<1, 0x22F983, -1>(); // ½π
|
||||
V y = round(r.x * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // y mod 4
|
||||
r.x -= y * Vc::Detail::floatConstant<1, 0x490fe0, 0>(); // pi/2
|
||||
r.x -= y * Vc::Detail::floatConstant<-1, 0x2bbbd3, -21>(); // pi/2 remainder
|
||||
} else {
|
||||
typedef SimdArray<double, V::size()> VD;
|
||||
VD xd = simd_cast<VD>(abs(r.x));
|
||||
constexpr double _2_over_pi =
|
||||
Vc::Detail::doubleConstant<1, 0x45F306DC9C883, -1>();
|
||||
VD y = round(xd * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // = y mod 4
|
||||
constexpr double pi_over_2 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, 0>();
|
||||
r.x = simd_cast<V>(xd - y * pi_over_2);
|
||||
}
|
||||
//std::cout << std::hexfloat << r.x << ' ' << r.quadrant << std::defaultfloat << '\n';
|
||||
return r;
|
||||
|
||||
/*
|
||||
More alternatives to avoid double precision calculation follow. Their main problem
|
||||
is unfortunate rounding on intermediate values of x (after subtraction):
|
||||
|
||||
if (all_of(y <= 0x1000)) {
|
||||
// 0 <= x <= 0x1.921fb6p12
|
||||
// π/4 - 12 bits precision (12 zero bits):
|
||||
//const auto _pi_4_1 = Vc::Detail::floatConstant< 1, 0x491000, -1>();
|
||||
//const auto _pi_4_2 = Vc::Detail::floatConstant<-1, 0x157000, -19>();
|
||||
//const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x6F4B9F, -32>();
|
||||
x -= y * C::_pi_4_hi();
|
||||
x -= y * C::_pi_4_rem1();
|
||||
x -= y * C::_pi_4_rem2();
|
||||
} else { //if (all_of(y <= 0x10000)) {
|
||||
// consider y = 0x1.fffep+16 / x = 0x1.921e24p+16
|
||||
// critical input: 0x1.921fb6p+12 <= x <= 0x1.921fb6p+16
|
||||
// π/4 - 8 bits precision (16 zero bits):
|
||||
const auto _pi_4_1 = Vc::Detail::floatConstant<+1, 0x490000, -1>();
|
||||
const auto _pi_4_2 = Vc::Detail::floatConstant<+1, 0x7E0000, -13>();
|
||||
const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x2C0000, -22>();
|
||||
const auto _pi_4_4 = Vc::Detail::floatConstant<+1, 0x085A31, -31>();
|
||||
x -= y * _pi_4_1; // (x -= 0x1.91fe6ep+16) -> 0x1.fb6000p+4
|
||||
x -= y * _pi_4_2; // (x -= 0x1.fbfe04p+4) -> -0x1.3c0800p-6
|
||||
x -= y * _pi_4_3; // (x -= 0x1.57fea8p-5) -> 0x1.bf5a80p-9
|
||||
x -= y * _pi_4_4; // (x -= 0x1.10b352p-14) -> 0x1.b6e4e6p-9
|
||||
x.setQnan(y > 0x10000);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<double, Abi> foldInput(const Vector<double, Abi> &x)
|
||||
{
|
||||
using V = Vector<double, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<double, Abi> ;
|
||||
|
||||
folded<double, Abi> r;
|
||||
r.x = abs(x);
|
||||
constexpr double pi_over_4 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, -1>();
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < pi_over_4))) {
|
||||
r.quadrant = 0;
|
||||
return r;
|
||||
}
|
||||
const V y = round(r.x / (2 * pi_over_4));
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3);
|
||||
|
||||
if (Vc_IS_LIKELY(all_of(r.x < 1025 * pi_over_4))) {
|
||||
// x - y * pi/2, y uses no more than 11 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x921FB54443000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<-1, 0x73DCB3B39A000, -43>();
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x45C06E0E68948, -86>();
|
||||
} else if (Vc_IS_LIKELY(all_of(y <= Vc::Detail::doubleConstant<1, 0, 30>()))) {
|
||||
// x - y * pi/2, y uses no more than 29 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x921FB40000000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x4442D00000000, -24>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x8469898CC5170, -48>();
|
||||
} else {
|
||||
// x - y * pi/2, y may require all mantissa bits
|
||||
const V y_hi = y & C::highMask(26);
|
||||
const V y_lo = y - y_hi;
|
||||
const auto _pi_2_1 = Vc::Detail::doubleConstant<1, 0x921FB50000000, 0>();
|
||||
const auto _pi_2_2 = Vc::Detail::doubleConstant<1, 0x110B460000000, -26>();
|
||||
const auto _pi_2_3 = Vc::Detail::doubleConstant<1, 0x1A62630000000, -54>();
|
||||
const auto _pi_2_4 = Vc::Detail::doubleConstant<1, 0x8A2E03707344A, -81>();
|
||||
/*
|
||||
std::cout << std::hexfloat;
|
||||
std::cout
|
||||
<< y_hi << '\n'
|
||||
<< y_lo << '\n'
|
||||
<< _pi_2_1 << '\n'
|
||||
<< _pi_2_2 << '\n'
|
||||
<< _pi_2_3 << '\n'
|
||||
<< y_hi * _pi_2_1 << '\n'
|
||||
<< y_hi * _pi_2_2 << '\n'
|
||||
<< y_hi * _pi_2_3 << '\n'
|
||||
<< y * _pi_2_4 << '\n'
|
||||
<< y_lo * _pi_2_1 << '\n'
|
||||
<< y_lo * _pi_2_2 << '\n'
|
||||
<< y_lo * _pi_2_3 << '\n';
|
||||
std::cout << std::defaultfloat;
|
||||
*/
|
||||
r.x = r.x
|
||||
- y_hi * _pi_2_1
|
||||
- max(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- min(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- max(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- min(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- max(y * _pi_2_4, y_lo * _pi_2_3)
|
||||
- min(y * _pi_2_4, y_lo * _pi_2_3);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr double signmask = -0.;
|
||||
constexpr float signmaskf = -0.f;
|
||||
} // anonymous namespace
|
||||
|
||||
/*
|
||||
* algorithm for sine and cosine:
|
||||
*
|
||||
* The result can be calculated with sine or cosine depending on the π/4 section the input is
|
||||
* in.
|
||||
* sine ≈ x + x³
|
||||
* cosine ≈ 1 - x²
|
||||
*
|
||||
* sine:
|
||||
* Map -x to x and invert the output
|
||||
* Extend precision of x - n * π/4 by calculating
|
||||
* ((x - n * p1) - n * p2) - n * p3 (p1 + p2 + p3 = π/4)
|
||||
*
|
||||
* Calculate Taylor series with tuned coefficients.
|
||||
* Fix sign.
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | sinSeries
|
||||
// 1 | cosSeries
|
||||
// 2 | sinSeries, sign flip
|
||||
// 3 | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 527449))) {
|
||||
return simd_cast<V>(sin(simd_cast<Vc::double_v, 0>(x)),
|
||||
sin(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | cosSeries, +
|
||||
// 1 | sinSeries, -
|
||||
// 2 | cosSeries, -
|
||||
// 3 | sinSeries, +
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
return simd_cast<V>(cos(simd_cast<Vc::double_v, 0>(x)),
|
||||
cos(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::double_v &x, Vc::double_v *s,
|
||||
Vc::double_v *c)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | cosine | sine
|
||||
// 0 | cosSeries, + | sinSeries
|
||||
// 1 | sinSeries, - | cosSeries
|
||||
// 2 | cosSeries, - | sinSeries, sign flip
|
||||
// 3 | sinSeries, + | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::float_v &x, Vc::float_v *s,
|
||||
Vc::float_v *c)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
Vc::double_v s0, s1, c0, c1;
|
||||
sincos(simd_cast<Vc::double_v, 0>(x), &s0, &c0);
|
||||
sincos(simd_cast<Vc::double_v, 1>(x), &s1, &c1);
|
||||
*s = simd_cast<V>(s0, s1);
|
||||
*c = simd_cast<V>(c0, c1);
|
||||
return;
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::asin(const Vc::float_v &_x)
|
||||
{
|
||||
typedef Vc::float_v V;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M &negative = _x < V::Zero();
|
||||
|
||||
const V &a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M &small = a < C::smallAsinInput();
|
||||
const M >_0_5 = a > C::_1_2();
|
||||
V x = a;
|
||||
V z = a * a;
|
||||
z(gt_0_5) = (V::One() - a) * C::_1_2();
|
||||
x(gt_0_5) = sqrt(z);
|
||||
z = ((((C::asinCoeff0(0) * z
|
||||
+ C::asinCoeff0(1)) * z
|
||||
+ C::asinCoeff0(2)) * z
|
||||
+ C::asinCoeff0(3)) * z
|
||||
+ C::asinCoeff0(4)) * z * x
|
||||
+ x;
|
||||
z(gt_0_5) = C::_pi_2() - (z + z);
|
||||
z(small) = a;
|
||||
z(negative) = -z;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::asin (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M negative = _x < V::Zero();
|
||||
|
||||
const V a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M small = a < C::smallAsinInput();
|
||||
const M large = a > C::largeAsinInput();
|
||||
|
||||
V zz = V::One() - a;
|
||||
const V r = (((C::asinCoeff0(0) * zz + C::asinCoeff0(1)) * zz + C::asinCoeff0(2)) * zz +
|
||||
C::asinCoeff0(3)) * zz + C::asinCoeff0(4);
|
||||
const V s = (((zz + C::asinCoeff1(0)) * zz + C::asinCoeff1(1)) * zz +
|
||||
C::asinCoeff1(2)) * zz + C::asinCoeff1(3);
|
||||
V sqrtzz = sqrt(zz + zz);
|
||||
V z = C::_pi_4() - sqrtzz;
|
||||
z -= sqrtzz * (zz * r / s) - C::_pi_2_rem();
|
||||
z += C::_pi_4();
|
||||
|
||||
V a2 = a * a;
|
||||
const V p = ((((C::asinCoeff2(0) * a2 + C::asinCoeff2(1)) * a2 + C::asinCoeff2(2)) * a2 +
|
||||
C::asinCoeff2(3)) * a2 + C::asinCoeff2(4)) * a2 + C::asinCoeff2(5);
|
||||
const V q = ((((a2 + C::asinCoeff3(0)) * a2 + C::asinCoeff3(1)) * a2 +
|
||||
C::asinCoeff3(2)) * a2 + C::asinCoeff3(3)) * a2 + C::asinCoeff3(4);
|
||||
z(!large) = a * (a2 * p / q) + a;
|
||||
|
||||
z(negative) = -z;
|
||||
z(small) = _x;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan(const Vc::float_v &_x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
V x = abs(_x);
|
||||
const M >_tan_3pi_8 = x > C::atanThrsHi();
|
||||
const M >_tan_pi_8 = x > C::atanThrsLo() && !gt_tan_3pi_8;
|
||||
V y = V::Zero();
|
||||
y(gt_tan_3pi_8) = C::_pi_2();
|
||||
y(gt_tan_pi_8) = C::_pi_4();
|
||||
x(gt_tan_3pi_8) = -V::One() / x;
|
||||
x(gt_tan_pi_8) = (x - V::One()) / (x + V::One());
|
||||
const V &x2 = x * x;
|
||||
y += (((C::atanP(0) * x2
|
||||
- C::atanP(1)) * x2
|
||||
+ C::atanP(2)) * x2
|
||||
- C::atanP(3)) * x2 * x
|
||||
+ x;
|
||||
y(_x < V::Zero()) = -y;
|
||||
y.setQnan(isnan(_x));
|
||||
return y;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
M sign = _x < V::Zero();
|
||||
V x = abs(_x);
|
||||
M finite = isfinite(_x);
|
||||
V ret = C::_pi_2();
|
||||
V y = V::Zero();
|
||||
const M large = x > C::atanThrsHi();
|
||||
const M gt_06 = x > C::atanThrsLo();
|
||||
V tmp = (x - V::One()) / (x + V::One());
|
||||
tmp(large) = -V::One() / x;
|
||||
x(gt_06) = tmp;
|
||||
y(gt_06) = C::_pi_4();
|
||||
y(large) = C::_pi_2();
|
||||
V z = x * x;
|
||||
const V p = (((C::atanP(0) * z + C::atanP(1)) * z + C::atanP(2)) * z + C::atanP(3)) * z + C::atanP(4);
|
||||
const V q = ((((z + C::atanQ(0)) * z + C::atanQ(1)) * z + C::atanQ(2)) * z + C::atanQ(3)) * z + C::atanQ(4);
|
||||
z = z * p / q;
|
||||
z = x * z + x;
|
||||
V morebits = C::_pi_2_rem();
|
||||
morebits(!large) *= C::_1_2();
|
||||
z(gt_06) += morebits;
|
||||
ret(finite) = y + z;
|
||||
ret(sign) = -ret;
|
||||
ret.setQnan(isnan(_x));
|
||||
return ret;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan2(const Vc::float_v &y,
|
||||
const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(C::_pi(), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan2 (const Vc::double_v &y, const Vc::double_v &x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(V(C::_pi()), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_SSSE3.cpp
vendored
Normal file
614
obj-x86_64-linux-gnu/3rdparty/Vc/trigonometric_SSSE3.cpp
vendored
Normal file
|
|
@ -0,0 +1,614 @@
|
|||
/* This file is part of the Vc library. {{{
|
||||
Copyright © 2012-2015 Matthias Kretz <kretz@kde.org>
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the names of contributing organizations nor the
|
||||
names of its contributors may be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
|
||||
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
|
||||
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
}}}*/
|
||||
|
||||
// enable bit operators for easier portable bit manipulation on floats
|
||||
#define Vc_ENABLE_FLOAT_BIT_OPERATORS 1
|
||||
|
||||
#include <Vc/vector.h>
|
||||
#if defined(Vc_IMPL_SSE) || defined(Vc_IMPL_AVX)
|
||||
#include <Vc/common/macros.h>
|
||||
//#include <Vc/IO>
|
||||
|
||||
namespace Vc_VERSIONED_NAMESPACE
|
||||
{
|
||||
namespace Common
|
||||
{
|
||||
namespace
|
||||
{
|
||||
using Vc::Vector;
|
||||
template <typename T, typename Abi>
|
||||
using Const = typename std::conditional<std::is_same<Abi, VectorAbi::Avx>::value,
|
||||
AVX::Const<T>, SSE::Const<T>>::type;
|
||||
|
||||
template <typename V>
|
||||
using best_int_v_for =
|
||||
typename std::conditional<(V::size() <= Vector<int, VectorAbi::Best<int>>::size()),
|
||||
Vector<int, VectorAbi::Best<int>>,
|
||||
SimdArray<int, V::size()>>::type;
|
||||
template <typename Abi> using float_int_v = best_int_v_for<Vector<float, Abi>>;
|
||||
template <typename Abi> using double_int_v = best_int_v_for<Vector<double, Abi>>;
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> cosSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant< 1, 0x500000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x360800, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x2AAAAB, -5>(); // 1/4!
|
||||
return y * (x2 * x2) - .5f * x2 + 1.f;
|
||||
// alternative (appears neither faster nor more precise):
|
||||
// return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> cosSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant< 1, 0xAC00000000000, -45>(); // 1/16!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x9394000000000, -37>(); // -1/14!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1EED8C0000000, -29>(); // 1/12!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x27E4FB7400000, -22>(); // -1/10!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0xA01A01A018000, -16>(); // 1/8!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x6C16C16C16C00, -10>(); // -1/6!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x5555555555554, -5>(); // 1/4!
|
||||
return (y * x2 - .5f) * x2 + 1.f;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<float, Abi> sinSeries(const Vector<float, Abi> &x)
|
||||
{
|
||||
const Vector<float, Abi> x2 = x * x;
|
||||
Vector<float, Abi> y;
|
||||
y = Vc::Detail::floatConstant<-1, 0x4E6000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::floatConstant< 1, 0x088880, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::floatConstant<-1, 0x2AAAAB, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE Vector<double, Abi> sinSeries(const Vector<double, Abi> &x)
|
||||
{
|
||||
// x = [0, 0.7854 = pi/4]
|
||||
// x² = [0, 0.6169 = pi²/8]
|
||||
const Vector<double, Abi> x2 = x * x;
|
||||
Vector<double, Abi> y;
|
||||
y = Vc::Detail::doubleConstant<-1, 0xACF0000000000, -41>(); // -1/15!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x6124400000000, -33>(); // 1/13!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xAE64567000000, -26>(); // -1/11!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x71DE3A5540000, -19>(); // 1/9!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0xA01A01A01A000, -13>(); // -1/7!
|
||||
y = y * x2 + Vc::Detail::doubleConstant< 1, 0x1111111111110, -7>(); // 1/5!
|
||||
y = y * x2 + Vc::Detail::doubleConstant<-1, 0x5555555555555, -3>(); // -1/3!
|
||||
return y * (x2 * x) + x;
|
||||
}
|
||||
|
||||
/**\internal
|
||||
* Fold \p x into [-¼π, ¼π] and remember the quadrant it came from:
|
||||
* quadrant 0: [-¼π, ¼π]
|
||||
* quadrant 1: [ ¼π, ¾π]
|
||||
* quadrant 2: [ ¾π, 1¼π]
|
||||
* quadrant 3: [1¼π, 1¾π]
|
||||
*
|
||||
* The algorithm determines `y` as the multiple `x - y * ¼π = [-¼π, ¼π]`. Using a bitmask,
|
||||
* `y` is reduced to `quadrant`. `y` can be calculated as
|
||||
* ```
|
||||
* y = trunc(x / ¼π);
|
||||
* y += fmod(y, 2);
|
||||
* ```
|
||||
* This can be simplified by moving the (implicit) division by 2 into the truncation
|
||||
* expression. The `+= fmod` effect can the be achieved by using rounding instead of
|
||||
* truncation:
|
||||
* `y = round(x / ½π) * 2`.
|
||||
* If precision allows, `2/π * x` is better (faster).
|
||||
*/
|
||||
template <class T, class Abi> struct folded {
|
||||
Vector<T, Abi> x, quadrant;
|
||||
};
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<float, Abi> foldInput(const Vector<float, Abi> &x)
|
||||
{
|
||||
using V = Vector<float, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<float, Abi>;
|
||||
|
||||
folded<float, Abi> r;
|
||||
r.x = abs(x);
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < C::_pi_4()))) {
|
||||
r.quadrant = 0;
|
||||
} else if (Vc_IS_LIKELY(all_of(r.x < 33 * C::_pi_4()))) {
|
||||
const float _2_over_pi = Vc::Detail::floatConstant<1, 0x22F983, -1>(); // ½π
|
||||
V y = round(r.x * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // y mod 4
|
||||
r.x -= y * Vc::Detail::floatConstant<1, 0x490fe0, 0>(); // pi/2
|
||||
r.x -= y * Vc::Detail::floatConstant<-1, 0x2bbbd3, -21>(); // pi/2 remainder
|
||||
} else {
|
||||
typedef SimdArray<double, V::size()> VD;
|
||||
VD xd = simd_cast<VD>(abs(r.x));
|
||||
constexpr double _2_over_pi =
|
||||
Vc::Detail::doubleConstant<1, 0x45F306DC9C883, -1>();
|
||||
VD y = round(xd * _2_over_pi);
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3); // = y mod 4
|
||||
constexpr double pi_over_2 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, 0>();
|
||||
r.x = simd_cast<V>(xd - y * pi_over_2);
|
||||
}
|
||||
//std::cout << std::hexfloat << r.x << ' ' << r.quadrant << std::defaultfloat << '\n';
|
||||
return r;
|
||||
|
||||
/*
|
||||
More alternatives to avoid double precision calculation follow. Their main problem
|
||||
is unfortunate rounding on intermediate values of x (after subtraction):
|
||||
|
||||
if (all_of(y <= 0x1000)) {
|
||||
// 0 <= x <= 0x1.921fb6p12
|
||||
// π/4 - 12 bits precision (12 zero bits):
|
||||
//const auto _pi_4_1 = Vc::Detail::floatConstant< 1, 0x491000, -1>();
|
||||
//const auto _pi_4_2 = Vc::Detail::floatConstant<-1, 0x157000, -19>();
|
||||
//const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x6F4B9F, -32>();
|
||||
x -= y * C::_pi_4_hi();
|
||||
x -= y * C::_pi_4_rem1();
|
||||
x -= y * C::_pi_4_rem2();
|
||||
} else { //if (all_of(y <= 0x10000)) {
|
||||
// consider y = 0x1.fffep+16 / x = 0x1.921e24p+16
|
||||
// critical input: 0x1.921fb6p+12 <= x <= 0x1.921fb6p+16
|
||||
// π/4 - 8 bits precision (16 zero bits):
|
||||
const auto _pi_4_1 = Vc::Detail::floatConstant<+1, 0x490000, -1>();
|
||||
const auto _pi_4_2 = Vc::Detail::floatConstant<+1, 0x7E0000, -13>();
|
||||
const auto _pi_4_3 = Vc::Detail::floatConstant<-1, 0x2C0000, -22>();
|
||||
const auto _pi_4_4 = Vc::Detail::floatConstant<+1, 0x085A31, -31>();
|
||||
x -= y * _pi_4_1; // (x -= 0x1.91fe6ep+16) -> 0x1.fb6000p+4
|
||||
x -= y * _pi_4_2; // (x -= 0x1.fbfe04p+4) -> -0x1.3c0800p-6
|
||||
x -= y * _pi_4_3; // (x -= 0x1.57fea8p-5) -> 0x1.bf5a80p-9
|
||||
x -= y * _pi_4_4; // (x -= 0x1.10b352p-14) -> 0x1.b6e4e6p-9
|
||||
x.setQnan(y > 0x10000);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
template <typename Abi>
|
||||
static Vc_ALWAYS_INLINE folded<double, Abi> foldInput(const Vector<double, Abi> &x)
|
||||
{
|
||||
using V = Vector<double, Abi>;
|
||||
using IV = best_int_v_for<V>;
|
||||
using C = Const<double, Abi> ;
|
||||
|
||||
folded<double, Abi> r;
|
||||
r.x = abs(x);
|
||||
constexpr double pi_over_4 = Vc::Detail::doubleConstant<1, 0x921FB54442D18, -1>();
|
||||
if (Vc_IS_UNLIKELY(all_of(r.x < pi_over_4))) {
|
||||
r.quadrant = 0;
|
||||
return r;
|
||||
}
|
||||
const V y = round(r.x / (2 * pi_over_4));
|
||||
r.quadrant = simd_cast<V>(simd_cast<IV>(y) & 3);
|
||||
|
||||
if (Vc_IS_LIKELY(all_of(r.x < 1025 * pi_over_4))) {
|
||||
// x - y * pi/2, y uses no more than 11 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x921FB54443000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<-1, 0x73DCB3B39A000, -43>();
|
||||
r.x -= y * Vc::Detail::doubleConstant< 1, 0x45C06E0E68948, -86>();
|
||||
} else if (Vc_IS_LIKELY(all_of(y <= Vc::Detail::doubleConstant<1, 0, 30>()))) {
|
||||
// x - y * pi/2, y uses no more than 29 mantissa bits
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x921FB40000000, 0>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x4442D00000000, -24>();
|
||||
r.x -= y * Vc::Detail::doubleConstant<1, 0x8469898CC5170, -48>();
|
||||
} else {
|
||||
// x - y * pi/2, y may require all mantissa bits
|
||||
const V y_hi = y & C::highMask(26);
|
||||
const V y_lo = y - y_hi;
|
||||
const auto _pi_2_1 = Vc::Detail::doubleConstant<1, 0x921FB50000000, 0>();
|
||||
const auto _pi_2_2 = Vc::Detail::doubleConstant<1, 0x110B460000000, -26>();
|
||||
const auto _pi_2_3 = Vc::Detail::doubleConstant<1, 0x1A62630000000, -54>();
|
||||
const auto _pi_2_4 = Vc::Detail::doubleConstant<1, 0x8A2E03707344A, -81>();
|
||||
/*
|
||||
std::cout << std::hexfloat;
|
||||
std::cout
|
||||
<< y_hi << '\n'
|
||||
<< y_lo << '\n'
|
||||
<< _pi_2_1 << '\n'
|
||||
<< _pi_2_2 << '\n'
|
||||
<< _pi_2_3 << '\n'
|
||||
<< y_hi * _pi_2_1 << '\n'
|
||||
<< y_hi * _pi_2_2 << '\n'
|
||||
<< y_hi * _pi_2_3 << '\n'
|
||||
<< y * _pi_2_4 << '\n'
|
||||
<< y_lo * _pi_2_1 << '\n'
|
||||
<< y_lo * _pi_2_2 << '\n'
|
||||
<< y_lo * _pi_2_3 << '\n';
|
||||
std::cout << std::defaultfloat;
|
||||
*/
|
||||
r.x = r.x
|
||||
- y_hi * _pi_2_1
|
||||
- max(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- min(y_hi * _pi_2_2, y_lo * _pi_2_1)
|
||||
- max(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- min(y_hi * _pi_2_3, y_lo * _pi_2_2)
|
||||
- max(y * _pi_2_4, y_lo * _pi_2_3)
|
||||
- min(y * _pi_2_4, y_lo * _pi_2_3);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
constexpr double signmask = -0.;
|
||||
constexpr float signmaskf = -0.f;
|
||||
} // anonymous namespace
|
||||
|
||||
/*
|
||||
* algorithm for sine and cosine:
|
||||
*
|
||||
* The result can be calculated with sine or cosine depending on the π/4 section the input is
|
||||
* in.
|
||||
* sine ≈ x + x³
|
||||
* cosine ≈ 1 - x²
|
||||
*
|
||||
* sine:
|
||||
* Map -x to x and invert the output
|
||||
* Extend precision of x - n * π/4 by calculating
|
||||
* ((x - n * p1) - n * p2) - n * p3 (p1 + p2 + p3 = π/4)
|
||||
*
|
||||
* Calculate Taylor series with tuned coefficients.
|
||||
* Fix sign.
|
||||
*/
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | sinSeries
|
||||
// 1 | cosSeries
|
||||
// 2 | sinSeries, sign flip
|
||||
// 3 | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sin(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 527449))) {
|
||||
return simd_cast<V>(sin(simd_cast<Vc::double_v, 0>(x)),
|
||||
sin(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::double_v &x)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | effect
|
||||
// 0 | cosSeries, +
|
||||
// 1 | sinSeries, -
|
||||
// 2 | cosSeries, -
|
||||
// 3 | sinSeries, +
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::cos(const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
return simd_cast<V>(cos(simd_cast<Vc::double_v, 0>(x)),
|
||||
cos(simd_cast<Vc::double_v, 1>(x)));
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
return cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::double_v &x, Vc::double_v *s,
|
||||
Vc::double_v *c)
|
||||
{
|
||||
using V = Vc::double_v;
|
||||
const auto f = foldInput(x);
|
||||
// quadrant | cosine | sine
|
||||
// 0 | cosSeries, + | sinSeries
|
||||
// 1 | sinSeries, - | cosSeries
|
||||
// 2 | cosSeries, - | sinSeries, sign flip
|
||||
// 3 | sinSeries, + | cosSeries, sign flip
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmask;
|
||||
const V cos_sign = ((0.5 - f.quadrant) & (f.quadrant - 2.5)) & signmask;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
void Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::sincos(const Vc::float_v &x, Vc::float_v *s,
|
||||
Vc::float_v *c)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
if (Vc_IS_UNLIKELY(any_of(abs(x) >= 393382))) {
|
||||
Vc::double_v s0, s1, c0, c1;
|
||||
sincos(simd_cast<Vc::double_v, 0>(x), &s0, &c0);
|
||||
sincos(simd_cast<Vc::double_v, 1>(x), &s1, &c1);
|
||||
*s = simd_cast<V>(s0, s1);
|
||||
*c = simd_cast<V>(c0, c1);
|
||||
return;
|
||||
}
|
||||
const auto f = foldInput(x);
|
||||
const V sin_sign = (x ^ (1 - f.quadrant)) & signmaskf;
|
||||
const V cos_sign = ((0.5f - f.quadrant) & (f.quadrant - 2.5f)) & signmaskf;
|
||||
|
||||
const V sin_s = sinSeries(f.x);
|
||||
const V cos_s = cosSeries(f.x);
|
||||
*s = sin_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, sin_s, cos_s);
|
||||
*c = cos_sign ^ iif(f.quadrant == 0 || f.quadrant == 2, cos_s, sin_s);
|
||||
}
|
||||
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::asin(const Vc::float_v &_x)
|
||||
{
|
||||
typedef Vc::float_v V;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M &negative = _x < V::Zero();
|
||||
|
||||
const V &a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M &small = a < C::smallAsinInput();
|
||||
const M >_0_5 = a > C::_1_2();
|
||||
V x = a;
|
||||
V z = a * a;
|
||||
z(gt_0_5) = (V::One() - a) * C::_1_2();
|
||||
x(gt_0_5) = sqrt(z);
|
||||
z = ((((C::asinCoeff0(0) * z
|
||||
+ C::asinCoeff0(1)) * z
|
||||
+ C::asinCoeff0(2)) * z
|
||||
+ C::asinCoeff0(3)) * z
|
||||
+ C::asinCoeff0(4)) * z * x
|
||||
+ x;
|
||||
z(gt_0_5) = C::_pi_2() - (z + z);
|
||||
z(small) = a;
|
||||
z(negative) = -z;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::asin (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M negative = _x < V::Zero();
|
||||
|
||||
const V a = abs(_x);
|
||||
const M outOfRange = a > V::One();
|
||||
const M small = a < C::smallAsinInput();
|
||||
const M large = a > C::largeAsinInput();
|
||||
|
||||
V zz = V::One() - a;
|
||||
const V r = (((C::asinCoeff0(0) * zz + C::asinCoeff0(1)) * zz + C::asinCoeff0(2)) * zz +
|
||||
C::asinCoeff0(3)) * zz + C::asinCoeff0(4);
|
||||
const V s = (((zz + C::asinCoeff1(0)) * zz + C::asinCoeff1(1)) * zz +
|
||||
C::asinCoeff1(2)) * zz + C::asinCoeff1(3);
|
||||
V sqrtzz = sqrt(zz + zz);
|
||||
V z = C::_pi_4() - sqrtzz;
|
||||
z -= sqrtzz * (zz * r / s) - C::_pi_2_rem();
|
||||
z += C::_pi_4();
|
||||
|
||||
V a2 = a * a;
|
||||
const V p = ((((C::asinCoeff2(0) * a2 + C::asinCoeff2(1)) * a2 + C::asinCoeff2(2)) * a2 +
|
||||
C::asinCoeff2(3)) * a2 + C::asinCoeff2(4)) * a2 + C::asinCoeff2(5);
|
||||
const V q = ((((a2 + C::asinCoeff3(0)) * a2 + C::asinCoeff3(1)) * a2 +
|
||||
C::asinCoeff3(2)) * a2 + C::asinCoeff3(3)) * a2 + C::asinCoeff3(4);
|
||||
z(!large) = a * (a2 * p / q) + a;
|
||||
|
||||
z(negative) = -z;
|
||||
z(small) = _x;
|
||||
z.setQnan(outOfRange);
|
||||
|
||||
return z;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan(const Vc::float_v &_x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
V x = abs(_x);
|
||||
const M >_tan_3pi_8 = x > C::atanThrsHi();
|
||||
const M >_tan_pi_8 = x > C::atanThrsLo() && !gt_tan_3pi_8;
|
||||
V y = V::Zero();
|
||||
y(gt_tan_3pi_8) = C::_pi_2();
|
||||
y(gt_tan_pi_8) = C::_pi_4();
|
||||
x(gt_tan_3pi_8) = -V::One() / x;
|
||||
x(gt_tan_pi_8) = (x - V::One()) / (x + V::One());
|
||||
const V &x2 = x * x;
|
||||
y += (((C::atanP(0) * x2
|
||||
- C::atanP(1)) * x2
|
||||
+ C::atanP(2)) * x2
|
||||
- C::atanP(3)) * x2 * x
|
||||
+ x;
|
||||
y(_x < V::Zero()) = -y;
|
||||
y.setQnan(isnan(_x));
|
||||
return y;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan (const Vc::double_v &_x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
M sign = _x < V::Zero();
|
||||
V x = abs(_x);
|
||||
M finite = isfinite(_x);
|
||||
V ret = C::_pi_2();
|
||||
V y = V::Zero();
|
||||
const M large = x > C::atanThrsHi();
|
||||
const M gt_06 = x > C::atanThrsLo();
|
||||
V tmp = (x - V::One()) / (x + V::One());
|
||||
tmp(large) = -V::One() / x;
|
||||
x(gt_06) = tmp;
|
||||
y(gt_06) = C::_pi_4();
|
||||
y(large) = C::_pi_2();
|
||||
V z = x * x;
|
||||
const V p = (((C::atanP(0) * z + C::atanP(1)) * z + C::atanP(2)) * z + C::atanP(3)) * z + C::atanP(4);
|
||||
const V q = ((((z + C::atanQ(0)) * z + C::atanQ(1)) * z + C::atanQ(2)) * z + C::atanQ(3)) * z + C::atanQ(4);
|
||||
z = z * p / q;
|
||||
z = x * z + x;
|
||||
V morebits = C::_pi_2_rem();
|
||||
morebits(!large) *= C::_1_2();
|
||||
z(gt_06) += morebits;
|
||||
ret(finite) = y + z;
|
||||
ret(sign) = -ret;
|
||||
ret.setQnan(isnan(_x));
|
||||
return ret;
|
||||
}
|
||||
template <>
|
||||
template <>
|
||||
Vc::float_v Trigonometric<Vc::Detail::TrigonometricImplementation<
|
||||
Vc::CurrentImplementation::current()>>::atan2(const Vc::float_v &y,
|
||||
const Vc::float_v &x)
|
||||
{
|
||||
using V = Vc::float_v;
|
||||
typedef Const<float, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(C::_pi(), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
template<> template<> Vc::double_v Trigonometric<Vc::Detail::TrigonometricImplementation<Vc::CurrentImplementation::current()>>::atan2 (const Vc::double_v &y, const Vc::double_v &x) {
|
||||
typedef Vc::double_v V;
|
||||
typedef Const<double, V::abi> C;
|
||||
typedef V::Mask M;
|
||||
|
||||
const M xZero = x == V::Zero();
|
||||
const M yZero = y == V::Zero();
|
||||
const M xMinusZero = xZero && isnegative(x);
|
||||
const M yNeg = y < V::Zero();
|
||||
const M xInf = !isfinite(x);
|
||||
const M yInf = !isfinite(y);
|
||||
|
||||
V a = copysign(V(C::_pi()), y);
|
||||
a.setZero(x >= V::Zero());
|
||||
|
||||
// setting x to any finite value will have atan(y/x) return sign(y/x)*pi/2, just in case x is inf
|
||||
V _x = x;
|
||||
_x(yInf) = copysign(V::One(), x);
|
||||
|
||||
a += atan(y / _x);
|
||||
|
||||
// if x is +0 and y is +/-0 the result is +0
|
||||
a.setZero(xZero && yZero);
|
||||
|
||||
// for x = -0 we add/subtract pi to get the correct result
|
||||
a(xMinusZero) += copysign(C::_pi(), y);
|
||||
|
||||
// atan2(-Y, +/-0) = -pi/2
|
||||
a(xZero && yNeg) = -C::_pi_2();
|
||||
|
||||
// if both inputs are inf the output is +/- (3)pi/4
|
||||
a(xInf && yInf) += copysign(C::_pi_4(), x ^ ~y);
|
||||
|
||||
// correct the sign of y if the result is 0
|
||||
a(a == V::Zero()) = copysign(a, y);
|
||||
|
||||
// any NaN input will lead to NaN output
|
||||
a.setQnan(isnan(y) || isnan(x));
|
||||
|
||||
return a;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
Loading…
Add table
Add a link
Reference in a new issue