Пересобранный и видоизмененный conky для ОСГОС

This commit is contained in:
commit 3a48cf8c86
738 changed files with 217718 additions and 0 deletions

View 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

View 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

View 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

View 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

View 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

View 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

View 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 &gt_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 &gt_tan_3pi_8 = x > C::atanThrsHi();
const M &gt_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

View 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 &gt_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 &gt_tan_3pi_8 = x > C::atanThrsHi();
const M &gt_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

View 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 &gt_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 &gt_tan_3pi_8 = x > C::atanThrsHi();
const M &gt_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

View 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 &gt_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 &gt_tan_3pi_8 = x > C::atanThrsHi();
const M &gt_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

View 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 &gt_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 &gt_tan_3pi_8 = x > C::atanThrsHi();
const M &gt_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

View 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 &gt_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 &gt_tan_3pi_8 = x > C::atanThrsHi();
const M &gt_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

View 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 &gt_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 &gt_tan_3pi_8 = x > C::atanThrsHi();
const M &gt_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

View 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 &gt_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 &gt_tan_3pi_8 = x > C::atanThrsHi();
const M &gt_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

View 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 &gt_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 &gt_tan_3pi_8 = x > C::atanThrsHi();
const M &gt_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

View 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 &gt_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 &gt_tan_3pi_8 = x > C::atanThrsHi();
const M &gt_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

View 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 &gt_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 &gt_tan_3pi_8 = x > C::atanThrsHi();
const M &gt_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

BIN
obj-x86_64-linux-gnu/3rdparty/toluapp/toluapp vendored Executable file

Binary file not shown.

View file

@ -0,0 +1,8 @@
#ifndef __BUILD_H
#define __BUILD_H
/* Conky build info */
#define BUILD_ARCH "Linux x86_64"
#endif /* __BUILD_H */

View file

@ -0,0 +1,152 @@
/*
* Auto-generated header for CMake. See config.h.in if you need to modify the
* original.
*/
#ifndef _conky_config_h_
#define _conky_config_h_
/* #undef DEBUG */
#define SYSTEM_NAME "Linux"
#define PACKAGE_NAME "conky"
#define VERSION "1.21.3-pre-aa7de839"
#define SYSTEM_CONFIG_FILE "/etc/conky/conky.conf"
#define PACKAGE_LIBDIR "/usr/lib/conky"
#define DEFAULTNETDEV "eno1"
#define XDG_CONFIG_FILE "$HOME/.config/conky/conky.conf"
#define CONFIG_FILE "$HOME/.conkyrc"
#define LOCALE_DIR "/usr/share/locale"
#define MAX_USER_TEXT_DEFAULT 16384
#define DEFAULT_TEXT_BUFFER_SIZE 256
#define MAX_NET_INTERFACES 256
#define HTTPPORT 10080
#define BUILD_I18N 1
#define HAVE_SYS_STATFS_H 1
#define HAVE_SYS_PARAM_H 1
#define HAVE_SYS_INOTIFY_H 1
#define HAVE_DIRENT_H 1
#define HAVE_SOME_SOUNDCARD_H 1
#define HAVE_LINUX_SOUNDCARD_H 1
#define HAVE_STRNDUP 1
/* #undef HAVE_FOPENCOOKIE */
/* #undef HAVE_FUNOPEN */
/* #undef HAVE_PIPE2 */
#define HAVE_O_CLOEXEC 1
#define HAVE_CLOCK_GETTIME 1
#define HAVE_XCB 1
/* #undef HAVE_XCB_ERRORS */
/* #undef BUILD_WAYLAND */
#define BUILD_X11 1
#define OWN_WINDOW 1
#define BUILD_MOUSE_EVENTS 1
#define BUILD_XDAMAGE 1
#define BUILD_XINERAMA 1
#define BUILD_XFT 1
#define BUILD_XSHAPE 1
#define BUILD_XFIXES 1
/* #undef BUILD_XINPUT */
#define BUILD_ARGB 1
#define BUILD_XDBE 1
#define BUILD_PORT_MONITORS 1
/* #undef BUILD_AUDACIOUS */
/* #undef NEW_AUDACIOUS_FOUND */
#define BUILD_MPD 1
/* #undef BUILD_MYSQL */
#define BUILD_MOC 1
/* #undef BUILD_NVIDIA */
/* #undef BUILD_XMMS2 */
#define BUILD_HDDTEMP 1
/* #undef BUILD_LIBXML2 */
/* #undef BUILD_CURL */
#define BUILD_IMLIB2 1
#define BUILD_MATH 1
#define BUILD_BUILTIN_CONFIG 1
#define BUILD_OLD_CONFIG 1
#define BUILD_NCURSES 1
/* #undef LEAKFREE_NCURSES */
#define BUILD_APCUPSD 1
#define BUILD_IOSTATS 1
/* #undef BUILD_IPGFREQ */
/* #undef BUILD_WLAN */
/* #undef BUILD_ICAL */
/* #undef BUILD_IRC */
/* #undef BUILD_PULSEAUDIO */
/* #undef BUILD_INTEL_BACKLIGHT */
#define BUILD_IPV6 1
/* #undef BUILD_HTTP */
#define BUILD_GUI 1
/* #undef BUILD_ICONV */
/* #undef BUILD_LUA_CAIRO */
/* #undef BUILD_LUA_IMLIB2 */
/* #undef BUILD_LUA_RSVG */
#define BUILD_IBM 1
/* #undef BUILD_RSS */
#define BUILD_CMUS 1
/* #undef BUILD_JOURNAL */
/* #undef BUILD_HSV_GRADIENT */
#define BUILD_COLOUR_NAME_MAP 1
#define HAVE_STATFS64 1
#ifndef HAVE_STATFS64
#define statfs64 statfs
#endif
#endif /* _conky_config_h_ */

View file

@ -0,0 +1,754 @@
struct rgb { const char *name; uint8_t red; uint8_t green; uint8_t blue; };
%%
alice blue, 240, 248, 255
AliceBlue, 240, 248, 255
antique white, 250, 235, 215
AntiqueWhite, 250, 235, 215
AntiqueWhite1, 255, 239, 219
AntiqueWhite2, 238, 223, 204
AntiqueWhite3, 205, 192, 176
AntiqueWhite4, 139, 131, 120
aquamarine, 127, 255, 212
aquamarine1, 127, 255, 212
aquamarine2, 118, 238, 198
aquamarine3, 102, 205, 170
aquamarine4, 69, 139, 116
azure, 240, 255, 255
azure1, 240, 255, 255
azure2, 224, 238, 238
azure3, 193, 205, 205
azure4, 131, 139, 139
beige, 245, 245, 220
bisque, 255, 228, 196
bisque1, 255, 228, 196
bisque2, 238, 213, 183
bisque3, 205, 183, 158
bisque4, 139, 125, 107
black, 0, 0, 0
blanched almond, 255, 235, 205
BlanchedAlmond, 255, 235, 205
blue, 0, 0, 255
blue violet, 138, 43, 226
blue1, 0, 0, 255
blue2, 0, 0, 238
blue3, 0, 0, 205
blue4, 0, 0, 139
BlueViolet, 138, 43, 226
brown, 165, 42, 42
brown1, 255, 64, 64
brown2, 238, 59, 59
brown3, 205, 51, 51
brown4, 139, 35, 35
burlywood, 222, 184, 135
burlywood1, 255, 211, 155
burlywood2, 238, 197, 145
burlywood3, 205, 170, 125
burlywood4, 139, 115, 85
cadet blue, 95, 158, 160
CadetBlue, 95, 158, 160
CadetBlue1, 152, 245, 255
CadetBlue2, 142, 229, 238
CadetBlue3, 122, 197, 205
CadetBlue4, 83, 134, 139
chartreuse, 127, 255, 0
chartreuse1, 127, 255, 0
chartreuse2, 118, 238, 0
chartreuse3, 102, 205, 0
chartreuse4, 69, 139, 0
chocolate, 210, 105, 30
chocolate1, 255, 127, 36
chocolate2, 238, 118, 33
chocolate3, 205, 102, 29
chocolate4, 139, 69, 19
coral, 255, 127, 80
coral1, 255, 114, 86
coral2, 238, 106, 80
coral3, 205, 91, 69
coral4, 139, 62, 47
cornflower blue, 100, 149, 237
CornflowerBlue, 100, 149, 237
cornsilk, 255, 248, 220
cornsilk1, 255, 248, 220
cornsilk2, 238, 232, 205
cornsilk3, 205, 200, 177
cornsilk4, 139, 136, 120
cyan, 0, 255, 255
cyan1, 0, 255, 255
cyan2, 0, 238, 238
cyan3, 0, 205, 205
cyan4, 0, 139, 139
dark blue, 0, 0, 139
dark cyan, 0, 139, 139
dark goldenrod, 184, 134, 11
dark gray, 169, 169, 169
dark green, 0, 100, 0
dark grey, 169, 169, 169
dark khaki, 189, 183, 107
dark magenta, 139, 0, 139
dark olive green, 85, 107, 47
dark orange, 255, 140, 0
dark orchid, 153, 50, 204
dark red, 139, 0, 0
dark salmon, 233, 150, 122
dark sea green, 143, 188, 143
dark slate blue, 72, 61, 139
dark slate gray, 47, 79, 79
dark slate grey, 47, 79, 79
dark turquoise, 0, 206, 209
dark violet, 148, 0, 211
DarkBlue, 0, 0, 139
DarkCyan, 0, 139, 139
DarkGoldenrod, 184, 134, 11
DarkGoldenrod1, 255, 185, 15
DarkGoldenrod2, 238, 173, 14
DarkGoldenrod3, 205, 149, 12
DarkGoldenrod4, 139, 101, 8
DarkGray, 169, 169, 169
DarkGreen, 0, 100, 0
DarkGrey, 169, 169, 169
DarkKhaki, 189, 183, 107
DarkMagenta, 139, 0, 139
DarkOliveGreen, 85, 107, 47
DarkOliveGreen1, 202, 255, 112
DarkOliveGreen2, 188, 238, 104
DarkOliveGreen3, 162, 205, 90
DarkOliveGreen4, 110, 139, 61
DarkOrange, 255, 140, 0
DarkOrange1, 255, 127, 0
DarkOrange2, 238, 118, 0
DarkOrange3, 205, 102, 0
DarkOrange4, 139, 69, 0
DarkOrchid, 153, 50, 204
DarkOrchid1, 191, 62, 255
DarkOrchid2, 178, 58, 238
DarkOrchid3, 154, 50, 205
DarkOrchid4, 104, 34, 139
DarkRed, 139, 0, 0
DarkSalmon, 233, 150, 122
DarkSeaGreen, 143, 188, 143
DarkSeaGreen1, 193, 255, 193
DarkSeaGreen2, 180, 238, 180
DarkSeaGreen3, 155, 205, 155
DarkSeaGreen4, 105, 139, 105
DarkSlateBlue, 72, 61, 139
DarkSlateGray, 47, 79, 79
DarkSlateGray1, 151, 255, 255
DarkSlateGray2, 141, 238, 238
DarkSlateGray3, 121, 205, 205
DarkSlateGray4, 82, 139, 139
DarkSlateGrey, 47, 79, 79
DarkTurquoise, 0, 206, 209
DarkViolet, 148, 0, 211
deep pink, 255, 20, 147
deep sky blue, 0, 191, 255
DeepPink, 255, 20, 147
DeepPink1, 255, 20, 147
DeepPink2, 238, 18, 137
DeepPink3, 205, 16, 118
DeepPink4, 139, 10, 80
DeepSkyBlue, 0, 191, 255
DeepSkyBlue1, 0, 191, 255
DeepSkyBlue2, 0, 178, 238
DeepSkyBlue3, 0, 154, 205
DeepSkyBlue4, 0, 104, 139
dim gray, 105, 105, 105
dim grey, 105, 105, 105
DimGray, 105, 105, 105
DimGrey, 105, 105, 105
dodger blue, 30, 144, 255
DodgerBlue, 30, 144, 255
DodgerBlue1, 30, 144, 255
DodgerBlue2, 28, 134, 238
DodgerBlue3, 24, 116, 205
DodgerBlue4, 16, 78, 139
firebrick, 178, 34, 34
firebrick1, 255, 48, 48
firebrick2, 238, 44, 44
firebrick3, 205, 38, 38
firebrick4, 139, 26, 26
floral white, 255, 250, 240
FloralWhite, 255, 250, 240
forest green, 34, 139, 34
ForestGreen, 34, 139, 34
gainsboro, 220, 220, 220
ghost white, 248, 248, 255
GhostWhite, 248, 248, 255
gold, 255, 215, 0
gold1, 255, 215, 0
gold2, 238, 201, 0
gold3, 205, 173, 0
gold4, 139, 117, 0
goldenrod, 218, 165, 32
goldenrod1, 255, 193, 37
goldenrod2, 238, 180, 34
goldenrod3, 205, 155, 29
goldenrod4, 139, 105, 20
gray, 190, 190, 190
gray0, 0, 0, 0
gray1, 3, 3, 3
gray10, 26, 26, 26
gray100, 255, 255, 255
gray11, 28, 28, 28
gray12, 31, 31, 31
gray13, 33, 33, 33
gray14, 36, 36, 36
gray15, 38, 38, 38
gray16, 41, 41, 41
gray17, 43, 43, 43
gray18, 46, 46, 46
gray19, 48, 48, 48
gray2, 5, 5, 5
gray20, 51, 51, 51
gray21, 54, 54, 54
gray22, 56, 56, 56
gray23, 59, 59, 59
gray24, 61, 61, 61
gray25, 64, 64, 64
gray26, 66, 66, 66
gray27, 69, 69, 69
gray28, 71, 71, 71
gray29, 74, 74, 74
gray3, 8, 8, 8
gray30, 77, 77, 77
gray31, 79, 79, 79
gray32, 82, 82, 82
gray33, 84, 84, 84
gray34, 87, 87, 87
gray35, 89, 89, 89
gray36, 92, 92, 92
gray37, 94, 94, 94
gray38, 97, 97, 97
gray39, 99, 99, 99
gray4, 10, 10, 10
gray40, 102, 102, 102
gray41, 105, 105, 105
gray42, 107, 107, 107
gray43, 110, 110, 110
gray44, 112, 112, 112
gray45, 115, 115, 115
gray46, 117, 117, 117
gray47, 120, 120, 120
gray48, 122, 122, 122
gray49, 125, 125, 125
gray5, 13, 13, 13
gray50, 127, 127, 127
gray51, 130, 130, 130
gray52, 133, 133, 133
gray53, 135, 135, 135
gray54, 138, 138, 138
gray55, 140, 140, 140
gray56, 143, 143, 143
gray57, 145, 145, 145
gray58, 148, 148, 148
gray59, 150, 150, 150
gray6, 15, 15, 15
gray60, 153, 153, 153
gray61, 156, 156, 156
gray62, 158, 158, 158
gray63, 161, 161, 161
gray64, 163, 163, 163
gray65, 166, 166, 166
gray66, 168, 168, 168
gray67, 171, 171, 171
gray68, 173, 173, 173
gray69, 176, 176, 176
gray7, 18, 18, 18
gray70, 179, 179, 179
gray71, 181, 181, 181
gray72, 184, 184, 184
gray73, 186, 186, 186
gray74, 189, 189, 189
gray75, 191, 191, 191
gray76, 194, 194, 194
gray77, 196, 196, 196
gray78, 199, 199, 199
gray79, 201, 201, 201
gray8, 20, 20, 20
gray80, 204, 204, 204
gray81, 207, 207, 207
gray82, 209, 209, 209
gray83, 212, 212, 212
gray84, 214, 214, 214
gray85, 217, 217, 217
gray86, 219, 219, 219
gray87, 222, 222, 222
gray88, 224, 224, 224
gray89, 227, 227, 227
gray9, 23, 23, 23
gray90, 229, 229, 229
gray91, 232, 232, 232
gray92, 235, 235, 235
gray93, 237, 237, 237
gray94, 240, 240, 240
gray95, 242, 242, 242
gray96, 245, 245, 245
gray97, 247, 247, 247
gray98, 250, 250, 250
gray99, 252, 252, 252
green, 0, 255, 0
green yellow, 173, 255, 47
green1, 0, 255, 0
green2, 0, 238, 0
green3, 0, 205, 0
green4, 0, 139, 0
GreenYellow, 173, 255, 47
grey, 190, 190, 190
grey0, 0, 0, 0
grey1, 3, 3, 3
grey10, 26, 26, 26
grey100, 255, 255, 255
grey11, 28, 28, 28
grey12, 31, 31, 31
grey13, 33, 33, 33
grey14, 36, 36, 36
grey15, 38, 38, 38
grey16, 41, 41, 41
grey17, 43, 43, 43
grey18, 46, 46, 46
grey19, 48, 48, 48
grey2, 5, 5, 5
grey20, 51, 51, 51
grey21, 54, 54, 54
grey22, 56, 56, 56
grey23, 59, 59, 59
grey24, 61, 61, 61
grey25, 64, 64, 64
grey26, 66, 66, 66
grey27, 69, 69, 69
grey28, 71, 71, 71
grey29, 74, 74, 74
grey3, 8, 8, 8
grey30, 77, 77, 77
grey31, 79, 79, 79
grey32, 82, 82, 82
grey33, 84, 84, 84
grey34, 87, 87, 87
grey35, 89, 89, 89
grey36, 92, 92, 92
grey37, 94, 94, 94
grey38, 97, 97, 97
grey39, 99, 99, 99
grey4, 10, 10, 10
grey40, 102, 102, 102
grey41, 105, 105, 105
grey42, 107, 107, 107
grey43, 110, 110, 110
grey44, 112, 112, 112
grey45, 115, 115, 115
grey46, 117, 117, 117
grey47, 120, 120, 120
grey48, 122, 122, 122
grey49, 125, 125, 125
grey5, 13, 13, 13
grey50, 127, 127, 127
grey51, 130, 130, 130
grey52, 133, 133, 133
grey53, 135, 135, 135
grey54, 138, 138, 138
grey55, 140, 140, 140
grey56, 143, 143, 143
grey57, 145, 145, 145
grey58, 148, 148, 148
grey59, 150, 150, 150
grey6, 15, 15, 15
grey60, 153, 153, 153
grey61, 156, 156, 156
grey62, 158, 158, 158
grey63, 161, 161, 161
grey64, 163, 163, 163
grey65, 166, 166, 166
grey66, 168, 168, 168
grey67, 171, 171, 171
grey68, 173, 173, 173
grey69, 176, 176, 176
grey7, 18, 18, 18
grey70, 179, 179, 179
grey71, 181, 181, 181
grey72, 184, 184, 184
grey73, 186, 186, 186
grey74, 189, 189, 189
grey75, 191, 191, 191
grey76, 194, 194, 194
grey77, 196, 196, 196
grey78, 199, 199, 199
grey79, 201, 201, 201
grey8, 20, 20, 20
grey80, 204, 204, 204
grey81, 207, 207, 207
grey82, 209, 209, 209
grey83, 212, 212, 212
grey84, 214, 214, 214
grey85, 217, 217, 217
grey86, 219, 219, 219
grey87, 222, 222, 222
grey88, 224, 224, 224
grey89, 227, 227, 227
grey9, 23, 23, 23
grey90, 229, 229, 229
grey91, 232, 232, 232
grey92, 235, 235, 235
grey93, 237, 237, 237
grey94, 240, 240, 240
grey95, 242, 242, 242
grey96, 245, 245, 245
grey97, 247, 247, 247
grey98, 250, 250, 250
grey99, 252, 252, 252
honeydew, 240, 255, 240
honeydew1, 240, 255, 240
honeydew2, 224, 238, 224
honeydew3, 193, 205, 193
honeydew4, 131, 139, 131
hot pink, 255, 105, 180
HotPink, 255, 105, 180
HotPink1, 255, 110, 180
HotPink2, 238, 106, 167
HotPink3, 205, 96, 144
HotPink4, 139, 58, 98
indian red, 205, 92, 92
IndianRed, 205, 92, 92
IndianRed1, 255, 106, 106
IndianRed2, 238, 99, 99
IndianRed3, 205, 85, 85
IndianRed4, 139, 58, 58
ivory, 255, 255, 240
ivory1, 255, 255, 240
ivory2, 238, 238, 224
ivory3, 205, 205, 193
ivory4, 139, 139, 131
khaki, 240, 230, 140
khaki1, 255, 246, 143
khaki2, 238, 230, 133
khaki3, 205, 198, 115
khaki4, 139, 134, 78
lavender, 230, 230, 250
lavender blush, 255, 240, 245
LavenderBlush, 255, 240, 245
LavenderBlush1, 255, 240, 245
LavenderBlush2, 238, 224, 229
LavenderBlush3, 205, 193, 197
LavenderBlush4, 139, 131, 134
lawn green, 124, 252, 0
LawnGreen, 124, 252, 0
lemon chiffon, 255, 250, 205
LemonChiffon, 255, 250, 205
LemonChiffon1, 255, 250, 205
LemonChiffon2, 238, 233, 191
LemonChiffon3, 205, 201, 165
LemonChiffon4, 139, 137, 112
light blue, 173, 216, 230
light coral, 240, 128, 128
light cyan, 224, 255, 255
light goldenrod, 238, 221, 130
light goldenrod yellow, 250, 250, 210
light gray, 211, 211, 211
light green, 144, 238, 144
light grey, 211, 211, 211
light pink, 255, 182, 193
light salmon, 255, 160, 122
light sea green, 32, 178, 170
light sky blue, 135, 206, 250
light slate blue, 132, 112, 255
light slate gray, 119, 136, 153
light slate grey, 119, 136, 153
light steel blue, 176, 196, 222
light yellow, 255, 255, 224
LightBlue, 173, 216, 230
LightBlue1, 191, 239, 255
LightBlue2, 178, 223, 238
LightBlue3, 154, 192, 205
LightBlue4, 104, 131, 139
LightCoral, 240, 128, 128
LightCyan, 224, 255, 255
LightCyan1, 224, 255, 255
LightCyan2, 209, 238, 238
LightCyan3, 180, 205, 205
LightCyan4, 122, 139, 139
LightGoldenrod, 238, 221, 130
LightGoldenrod1, 255, 236, 139
LightGoldenrod2, 238, 220, 130
LightGoldenrod3, 205, 190, 112
LightGoldenrod4, 139, 129, 76
LightGoldenrodYellow, 250, 250, 210
LightGray, 211, 211, 211
LightGreen, 144, 238, 144
LightGrey, 211, 211, 211
LightPink, 255, 182, 193
LightPink1, 255, 174, 185
LightPink2, 238, 162, 173
LightPink3, 205, 140, 149
LightPink4, 139, 95, 101
LightSalmon, 255, 160, 122
LightSalmon1, 255, 160, 122
LightSalmon2, 238, 149, 114
LightSalmon3, 205, 129, 98
LightSalmon4, 139, 87, 66
LightSeaGreen, 32, 178, 170
LightSkyBlue, 135, 206, 250
LightSkyBlue1, 176, 226, 255
LightSkyBlue2, 164, 211, 238
LightSkyBlue3, 141, 182, 205
LightSkyBlue4, 96, 123, 139
LightSlateBlue, 132, 112, 255
LightSlateGray, 119, 136, 153
LightSlateGrey, 119, 136, 153
LightSteelBlue, 176, 196, 222
LightSteelBlue1, 202, 225, 255
LightSteelBlue2, 188, 210, 238
LightSteelBlue3, 162, 181, 205
LightSteelBlue4, 110, 123, 139
LightYellow, 255, 255, 224
LightYellow1, 255, 255, 224
LightYellow2, 238, 238, 209
LightYellow3, 205, 205, 180
LightYellow4, 139, 139, 122
lime green, 50, 205, 50
LimeGreen, 50, 205, 50
linen, 250, 240, 230
magenta, 255, 0, 255
magenta1, 255, 0, 255
magenta2, 238, 0, 238
magenta3, 205, 0, 205
magenta4, 139, 0, 139
maroon, 176, 48, 96
maroon1, 255, 52, 179
maroon2, 238, 48, 167
maroon3, 205, 41, 144
maroon4, 139, 28, 98
medium aquamarine, 102, 205, 170
medium blue, 0, 0, 205
medium orchid, 186, 85, 211
medium purple, 147, 112, 219
medium sea green, 60, 179, 113
medium slate blue, 123, 104, 238
medium spring green, 0, 250, 154
medium turquoise, 72, 209, 204
medium violet red, 199, 21, 133
MediumAquamarine, 102, 205, 170
MediumBlue, 0, 0, 205
MediumOrchid, 186, 85, 211
MediumOrchid1, 224, 102, 255
MediumOrchid2, 209, 95, 238
MediumOrchid3, 180, 82, 205
MediumOrchid4, 122, 55, 139
MediumPurple, 147, 112, 219
MediumPurple1, 171, 130, 255
MediumPurple2, 159, 121, 238
MediumPurple3, 137, 104, 205
MediumPurple4, 93, 71, 139
MediumSeaGreen, 60, 179, 113
MediumSlateBlue, 123, 104, 238
MediumSpringGreen, 0, 250, 154
MediumTurquoise, 72, 209, 204
MediumVioletRed, 199, 21, 133
midnight blue, 25, 25, 112
MidnightBlue, 25, 25, 112
mint cream, 245, 255, 250
MintCream, 245, 255, 250
misty rose, 255, 228, 225
MistyRose, 255, 228, 225
MistyRose1, 255, 228, 225
MistyRose2, 238, 213, 210
MistyRose3, 205, 183, 181
MistyRose4, 139, 125, 123
moccasin, 255, 228, 181
navajo white, 255, 222, 173
NavajoWhite, 255, 222, 173
NavajoWhite1, 255, 222, 173
NavajoWhite2, 238, 207, 161
NavajoWhite3, 205, 179, 139
NavajoWhite4, 139, 121, 94
navy, 0, 0, 128
navy blue, 0, 0, 128
NavyBlue, 0, 0, 128
old lace, 253, 245, 230
OldLace, 253, 245, 230
olive drab, 107, 142, 35
OliveDrab, 107, 142, 35
OliveDrab1, 192, 255, 62
OliveDrab2, 179, 238, 58
OliveDrab3, 154, 205, 50
OliveDrab4, 105, 139, 34
orange, 255, 165, 0
orange red, 255, 69, 0
orange1, 255, 165, 0
orange2, 238, 154, 0
orange3, 205, 133, 0
orange4, 139, 90, 0
OrangeRed, 255, 69, 0
OrangeRed1, 255, 69, 0
OrangeRed2, 238, 64, 0
OrangeRed3, 205, 55, 0
OrangeRed4, 139, 37, 0
orchid, 218, 112, 214
orchid1, 255, 131, 250
orchid2, 238, 122, 233
orchid3, 205, 105, 201
orchid4, 139, 71, 137
pale goldenrod, 238, 232, 170
pale green, 152, 251, 152
pale turquoise, 175, 238, 238
pale violet red, 219, 112, 147
PaleGoldenrod, 238, 232, 170
PaleGreen, 152, 251, 152
PaleGreen1, 154, 255, 154
PaleGreen2, 144, 238, 144
PaleGreen3, 124, 205, 124
PaleGreen4, 84, 139, 84
PaleTurquoise, 175, 238, 238
PaleTurquoise1, 187, 255, 255
PaleTurquoise2, 174, 238, 238
PaleTurquoise3, 150, 205, 205
PaleTurquoise4, 102, 139, 139
PaleVioletRed, 219, 112, 147
PaleVioletRed1, 255, 130, 171
PaleVioletRed2, 238, 121, 159
PaleVioletRed3, 205, 104, 137
PaleVioletRed4, 139, 71, 93
papaya whip, 255, 239, 213
PapayaWhip, 255, 239, 213
peach puff, 255, 218, 185
PeachPuff, 255, 218, 185
PeachPuff1, 255, 218, 185
PeachPuff2, 238, 203, 173
PeachPuff3, 205, 175, 149
PeachPuff4, 139, 119, 101
peru, 205, 133, 63
pink, 255, 192, 203
pink1, 255, 181, 197
pink2, 238, 169, 184
pink3, 205, 145, 158
pink4, 139, 99, 108
plum, 221, 160, 221
plum1, 255, 187, 255
plum2, 238, 174, 238
plum3, 205, 150, 205
plum4, 139, 102, 139
powder blue, 176, 224, 230
PowderBlue, 176, 224, 230
purple, 160, 32, 240
purple1, 155, 48, 255
purple2, 145, 44, 238
purple3, 125, 38, 205
purple4, 85, 26, 139
red, 255, 0, 0
red1, 255, 0, 0
red2, 238, 0, 0
red3, 205, 0, 0
red4, 139, 0, 0
rosy brown, 188, 143, 143
RosyBrown, 188, 143, 143
RosyBrown1, 255, 193, 193
RosyBrown2, 238, 180, 180
RosyBrown3, 205, 155, 155
RosyBrown4, 139, 105, 105
royal blue, 65, 105, 225
RoyalBlue, 65, 105, 225
RoyalBlue1, 72, 118, 255
RoyalBlue2, 67, 110, 238
RoyalBlue3, 58, 95, 205
RoyalBlue4, 39, 64, 139
saddle brown, 139, 69, 19
SaddleBrown, 139, 69, 19
salmon, 250, 128, 114
salmon1, 255, 140, 105
salmon2, 238, 130, 98
salmon3, 205, 112, 84
salmon4, 139, 76, 57
sandy brown, 244, 164, 96
SandyBrown, 244, 164, 96
sea green, 46, 139, 87
SeaGreen, 46, 139, 87
SeaGreen1, 84, 255, 159
SeaGreen2, 78, 238, 148
SeaGreen3, 67, 205, 128
SeaGreen4, 46, 139, 87
seashell, 255, 245, 238
seashell1, 255, 245, 238
seashell2, 238, 229, 222
seashell3, 205, 197, 191
seashell4, 139, 134, 130
sienna, 160, 82, 45
sienna1, 255, 130, 71
sienna2, 238, 121, 66
sienna3, 205, 104, 57
sienna4, 139, 71, 38
sky blue, 135, 206, 235
SkyBlue, 135, 206, 235
SkyBlue1, 135, 206, 255
SkyBlue2, 126, 192, 238
SkyBlue3, 108, 166, 205
SkyBlue4, 74, 112, 139
slate blue, 106, 90, 205
slate gray, 112, 128, 144
slate grey, 112, 128, 144
SlateBlue, 106, 90, 205
SlateBlue1, 131, 111, 255
SlateBlue2, 122, 103, 238
SlateBlue3, 105, 89, 205
SlateBlue4, 71, 60, 139
SlateGray, 112, 128, 144
SlateGray1, 198, 226, 255
SlateGray2, 185, 211, 238
SlateGray3, 159, 182, 205
SlateGray4, 108, 123, 139
SlateGrey, 112, 128, 144
snow, 255, 250, 250
snow1, 255, 250, 250
snow2, 238, 233, 233
snow3, 205, 201, 201
snow4, 139, 137, 137
spring green, 0, 255, 127
SpringGreen, 0, 255, 127
SpringGreen1, 0, 255, 127
SpringGreen2, 0, 238, 118
SpringGreen3, 0, 205, 102
SpringGreen4, 0, 139, 69
steel blue, 70, 130, 180
SteelBlue, 70, 130, 180
SteelBlue1, 99, 184, 255
SteelBlue2, 92, 172, 238
SteelBlue3, 79, 148, 205
SteelBlue4, 54, 100, 139
tan, 210, 180, 140
tan1, 255, 165, 79
tan2, 238, 154, 73
tan3, 205, 133, 63
tan4, 139, 90, 43
thistle, 216, 191, 216
thistle1, 255, 225, 255
thistle2, 238, 210, 238
thistle3, 205, 181, 205
thistle4, 139, 123, 139
tomato, 255, 99, 71
tomato1, 255, 99, 71
tomato2, 238, 92, 66
tomato3, 205, 79, 57
tomato4, 139, 54, 38
turquoise, 64, 224, 208
turquoise1, 0, 245, 255
turquoise2, 0, 229, 238
turquoise3, 0, 197, 205
turquoise4, 0, 134, 139
violet, 238, 130, 238
violet red, 208, 32, 144
VioletRed, 208, 32, 144
VioletRed1, 255, 62, 150
VioletRed2, 238, 58, 140
VioletRed3, 205, 50, 120
VioletRed4, 139, 34, 82
wheat, 245, 222, 179
wheat1, 255, 231, 186
wheat2, 238, 216, 174
wheat3, 205, 186, 150
wheat4, 139, 126, 102
white, 255, 255, 255
white smoke, 245, 245, 245
WhiteSmoke, 245, 245, 245
yellow, 255, 255, 0
yellow green, 154, 205, 50
yellow1, 255, 255, 0
yellow2, 238, 238, 0
yellow3, 205, 205, 0
yellow4, 139, 139, 0
YellowGreen, 154, 205, 50

View file

@ -0,0 +1,162 @@
const char convertconf[] =
"#! /usr/bin/lua\n"
"\n"
"local usage = [[\n"
"Usage: convert.lua old_conkyrc [new_conkyrc]\n"
"\n"
"Tries to convert conkyrc from the old v1.x format to the new, lua-based format.\n"
"\n"
"Keep in mind that there is no guarantee that the output will work correctly\n"
"with conky, or that it will be able to convert every conkyrc. However, it\n"
"should provide a good starting point.\n"
"\n"
"Although you can use this script with only 1 arg and let it overwrite the old\n"
"config, it's suggested to use 2 args so that the new config is written in a new\n"
"file (so that you have backup if something went wrong).\n"
"\n"
"Optional: Install dos2unix. We will attempt to use this if it is available\n"
"because Conky configs downloaded from Internet sometimes are created on DOS/Windows\n"
"machines with different line endings than Conky configs created on Unix/Linux.\n"
"\n"
"For more information about the new format, read the wiki page\n"
"<https://github.com/brndnmtthws/conky/wiki>\n"
"]];\n"
"\n"
"local function quote(s)\n"
" if not s:find(\"[\\n'\\\\]\") then\n"
" return \"'\" .. s .. \"'\";\n"
" end;\n"
" local q = '';\n"
" while s:find(']' .. q .. ']', 1, true) do\n"
" q = q .. '=';\n"
" end;\n"
" return string.format('[%s[\\n%s]%s]', q, s, q);\n"
"end;\n"
"\n"
"local bool_setting = {\n"
" background = true, disable_auto_reload = true, double_buffer = true, draw_borders = true,\n"
" draw_graph_borders = true, draw_outline = true, draw_shades = true, extra_newline = true,\n"
" format_human_readable = true, no_buffers = true, out_to_console = true,\n"
" out_to_wayland = true,\n"
" out_to_ncurses = true, out_to_stderr = true, out_to_x = true, override_utf8_locale = true,\n"
" own_window = true, own_window_argb_visual = true, own_window_transparent = true,\n"
" short_units = true, show_graph_range = true, show_graph_scale = true,\n"
" times_in_seconds = true, top_cpu_separate = true, uppercase = true, use_xft = true,\n"
" draw_blended = true, forced_redraw = true\n"
"};\n"
"\n"
"local num_setting = {\n"
" border_inner_margin = true, border_outer_margin = true, border_width = true,\n"
" cpu_avg_samples = true, diskio_avg_samples = true, gap_x = true, gap_y = true,\n"
" imlib_cache_flush_interval = true, imlib_cache_size = true,\n"
" max_port_monitor_connections = true, max_text_width = true, max_user_text = true,\n"
" maximum_width = true, mpd_port = true, music_player_interval = true, net_avg_samples = true,\n"
" own_window_argb_value = true, pad_percents = true, stippled_borders = true,\n"
" text_buffer_size = true, top_name_width = true, total_run_times = true,\n"
" update_interval = true, update_interval_on_battery = true, xftalpha = true,\n"
" xinerama_head = true,\n"
"};\n"
"\n"
"local split_setting = {\n"
" default_bar_size = true, default_gauge_size = true, default_graph_size = true,\n"
" minimum_size = true\n"
"};\n"
"\n"
"local colour_setting = {\n"
" color0 = true, color1 = true, color2 = true, color3 = true, color4 = true, color5 = true,\n"
" color6 = true, color7 = true, color8 = true, color9 = true, default_color = true,\n"
" default_outline_color = true, default_shade_color = true, own_window_colour = true\n"
"};\n"
"\n"
"local function alignment_map(value)\n"
" local map = { m = 'middle', t = 'top', b = 'bottom', r = 'right', l = 'left' };\n"
" if map[value] == nil then\n"
" return value;\n"
" else\n"
" return map[value];\n"
" end;\n"
"end;\n"
"\n"
"local function handle(setting, value)\n"
" setting = setting:lower();\n"
" if setting == '' then\n"
" return '';\n"
" end;\n"
" if split_setting[setting] then\n"
" local x, y = value:match('^(%S+)%s*(%S*)$');\n"
" local ret = setting:gsub('_size', '_width = ') .. x .. ',';\n"
" if y ~= '' then\n"
" ret = ret .. ' ' .. setting:gsub('_size', '_height = ') .. y .. ',';\n"
" end;\n"
" return '\\t' .. ret;\n"
" end;\n"
" if bool_setting[setting] then\n"
" value = value:lower();\n"
" if value == 'yes' or value == 'true' or value == '1' or value == '' then\n"
" value = 'true';\n"
" else\n"
" value = 'false';\n"
" end;\n"
" elseif not num_setting[setting] then\n"
" if setting == 'alignment' and value:len() == 2 then\n"
" value = alignment_map(value:sub(1,1)) .. '_' .. alignment_map(value:sub(2,2));\n"
" elseif colour_setting[setting] and value:match('^[0-9a-fA-F]+$') then\n"
" value = '#' .. value;\n"
" elseif setting == 'xftfont' then\n"
" setting = 'font';\n"
" end;\n"
" value = quote(value);\n"
" end;\n"
" return '\\t' .. setting .. ' = ' .. value .. ',';\n"
"end;\n"
"\n"
"local function convert(s)\n"
" local setting, comment = s:match('^([^#]*)#?(.*)\\n$');\n"
" if comment ~= '' then\n"
" comment = '--' .. comment;\n"
" end;\n"
" comment = comment .. '\\n';\n"
" return handle(setting:match('^%s*(%S*)%s*(.-)%s*$')) .. comment;\n"
"end;\n"
"\n"
"local input;\n"
"local output;\n"
"\n"
"if conky == nil then --> standalone program\n"
" -- 1 arg: arg is input and outputfile\n"
" -- 2 args: 1st is inputfile, 2nd is outputfile\n"
" -- 0, 3 or more args: print usage to STDERR and quit\n"
" if #arg == 1 or #arg == 2 then\n"
" if os.execute('command -v dos2unix 2>&1 >/dev/null') == 0 then\n"
" os.execute('dos2unix ' .. arg[1]);\n"
" end\n"
" input = io.input(arg[1]);\n"
" else\n"
" io.stderr:write(usage);\n"
" return;\n"
" end;\n"
"else\n"
" -- we are called from conky, the filename is the first argument\n"
" input = io.open(..., 'r');\n"
"end;\n"
"\n"
"\n"
"local config = input:read('*a');\n"
"input:close();\n"
"\n"
"local settings, text = config:match('^(.-)TEXT\\n(.*)$');\n"
"\n"
"local converted = 'conky.config = {\\n' .. settings:gsub('.-\\n', convert) .. '};\\n\\nconky.text = ' ..\n"
" quote(text) .. ';\\n';\n"
"\n"
"if conky == nil then\n"
" if #arg == 2 then\n"
" output = io.output(arg[2]);\n"
" else\n"
" output = io.output(arg[1]);\n"
" end\n"
" output:write(converted);\n"
" output:close();\n"
"else\n"
" return assert(load(converted, 'converted config'));\n"
"end;\n";

View file

@ -0,0 +1,105 @@
const char defconfig[] =
"-- Определяем функцию для получения фактора масштабирования окна\n"
"function get_window_scaling_factor()\n"
" -- Определяем текущего пользователя\n"
" local user_handle = io.popen(\"whoami\")\n"
" local current_user = user_handle:read(\"*a\"):gsub(\"%s+\", \"\") -- Удаляем пробелы и символы новой строки\n"
" user_handle:close()\n"
"\n"
" local scale_factor\n"
"\n"
" if current_user == \"root\" then\n"
" -- Если текущий пользователь root, определяем пользователя, который запустил X-сессию\n"
" local session_user_handle = io.popen(\"who | grep '(:0)' | awk '{print $1}'\")\n"
" local session_user = session_user_handle:read(\"*a\"):gsub(\"%s+\", \"\") -- Удаляем пробелы и символы новой строки\n"
" session_user_handle:close()\n"
"\n"
" -- Если не удалось определить пользователя, возвращаем масштаб по умолчанию\n"
" if not session_user or session_user == \"\" then return 1 end\n"
"\n"
" -- Выполняем команду от имени этого пользователя\n"
" local command = string.format(\"sudo -u %s xfconf-query -c xsettings -p /Gdk/WindowScalingFactor\", session_user)\n"
" local handle = io.popen(command)\n"
" scale_factor = tonumber(handle:read(\"*a\"))\n"
" handle:close()\n"
" else\n"
" -- Если текущий пользователь не root, просто получаем масштабирование\n"
" local handle = io.popen(\"xfconf-query -c xsettings -p /Gdk/WindowScalingFactor\")\n"
" scale_factor = tonumber(handle:read(\"*a\"))\n"
" handle:close()\n"
" end\n"
"\n"
" return scale_factor or 1\n"
"end\n"
"\n"
"-- Получаем текущий фактор масштаба окон\n"
"local scale_factor = get_window_scaling_factor()\n"
"\n"
"-- Применяем масштаб к шрифту и элементам интерфейса\n"
"conky.config = {\n"
" update_interval = 1,\n"
" cpu_avg_samples = 2,\n"
" net_avg_samples = 2,\n"
" out_to_console = false,\n"
" override_utf8_locale = true,\n"
" double_buffer = true,\n"
" no_buffers = true,\n"
" text_buffer_size = 32768,\n"
" imlib_cache_size = 0,\n"
" own_window = true,\n"
" own_window_type = 'desktop',\n"
" own_window_argb_value = 200,\n"
" own_window_argb_visual = true,\n"
" own_window_transparent = false,\n"
" own_window_hints = 'undecorated,below,sticky,skip_taskbar,skip_pager', -- Новая строка для предотвращения скрытия\n"
" border_inner_margin = 15 * scale_factor,\n"
" border_outer_margin = 0 * scale_factor,\n"
" xinerama_head = 1,\n"
" alignment = 'bottom_right',\n"
" gap_x = 80 * scale_factor,\n"
" gap_y = 80 * scale_factor,\n"
" draw_shades = false,\n"
" draw_outline = false,\n"
" draw_borders = false,\n"
" draw_graph_borders = false,\n"
" use_xft = true,\n"
" font = string.format('Comfortaa:size=%d', 12 * scale_factor),\n"
" xftalpha = 0.8,\n"
" uppercase = false,\n"
" default_color = 'white',\n"
" own_window_colour = '003333',\n"
" minimum_width = 300 * scale_factor, minimum_height = 0,\n"
" alignment = 'top_right',\n"
" color1 = '000000',\n"
" stippled_border = 100 * scale_factor,\n"
"};\n"
"\n"
"-- Variables: https://conky.cc/variables\n"
"conky.text = string.format([[\n"
" \n"
"${voffset -10}\n"
"${font Comfortaa:bold:size=%d}Система ${hr 2}\n"
"${font Comfortaa:normal:size=%d}Пользователь:${alignr}${USER}\n"
"Система:${alignr}${font Comfortaa:normal:size=%d}$sysname $kernel \n"
"Хост:$alignr$nodename\n"
"Время работы:$alignr$uptime\n"
"Процессы: $alignr ${execi 1000 ps aux | wc -l}\n"
"\n"
"${font Comfortaa:bold:size=%d}Процессор ${hr 2}\n"
"${font Comfortaa:normal:size=%d}${execi 1000 grep model /proc/cpuinfo | cut -d : -f2 | tail -1 | sed 's/\\\\s//'}${voffset 5}\n"
"Нагрузка: ${cpu cpu1}%% ${cpubar cpu1}${voffset 5}\n"
"Температура: ${acpitemp}°С\n"
"\n"
"${font Comfortaa:bold:size=%d}Память ${hr 2}\n"
"${font Comfortaa:normal:size=%d}Оперативная память $alignc $mem / $memmax $alignr $memperc%%${voffset 5}\n"
"Файлы подкачки $alignc ${swap} / ${swapmax} $alignr ${swapperc}%%\n"
"\n"
"${font Comfortaa:bold:size=%d}Диск ${hr 2}\n"
"${font Comfortaa:normal:size=%d}/ $alignc ${fs_used /} / ${fs_size /} $alignr ${fs_used_perc /}%%\n"
"${fs_bar /}\n"
"\n"
"${font Ubuntu:bold:size=%d}Сеть ${hr 2}\n"
"${font Comfortaa:normal:size=%d}Локальный IP:${alignr}Внешний IP:\n"
"${execi 1000 ip a | grep inet | grep -vw lo | grep -v inet6 | cut -d \\\\/ -f1 | sed 's/[^0-9\\\\.]*//g'} ${alignr}${execi 1000 wget -q -O- http://ipecho.net/plain; echo}\n"
"${font Comfortaa:normal:size=%d}Down: ${downspeed enp0s3} ${alignr}Up: ${upspeed enp0s3} \n"
"]], 10 * scale_factor, 8 * scale_factor, 8 * scale_factor, 10 * scale_factor, 8 * scale_factor, 10 * scale_factor, 8 * scale_factor, 10 * scale_factor, 8 * scale_factor, 10 * scale_factor, 8 * scale_factor, 10 * scale_factor, 8 * scale_factor, 10 * scale_factor, 8 * scale_factor, 10 * scale_factor)\n";

View file

@ -0,0 +1,7 @@
/usr/share/applications/conky.desktop
/usr/share/icons/hicolor/scalable/apps/conky-logomark-violet.svg
/usr/bin/conky
/usr/lib/libtcp-portmon.a
/usr/share/doc/conky-1.21.3/convert.lua
/usr/share/doc/conky-1.21.3/conky_no_x11.conf
/usr/share/doc/conky-1.21.3/conky.conf

File diff suppressed because it is too large Load diff

BIN
obj-x86_64-linux-gnu/src/conky Executable file

Binary file not shown.