|
- /* Modified version of https://github.com/to-miz/sse_mathfun_extension for VCV Rack.
-
- The following changes were made.
- - Make functions inline.
- - Change global constants to function-scope.
-
- This derived source file is released under the zlib license.
- */
-
- /*
- sse_mathfun_extension.h - zlib license
- Written by Tolga Mizrak 2016
- Extension of sse_mathfun.h, which is written by Julien Pommier
-
- Based on the corresponding algorithms of the cephes math library
-
- This is written as an extension to sse_mathfun.h instead of modifying it, just because I didn't want
- to maintain a modified version of the original library. This way switching to a newer version of the
- library won't be a hassle.
-
- Note that non SSE2 implementations of tan_ps, atan_ps, cot_ps and atan2_ps are not implemented yet.
- As such, currently you need to #define USE_SSE2 to compile.
-
- With tan_ps, cot_ps you get good precision on input ranges that are further away from the domain
- borders (-PI/2, PI/2 for tan and 0, 1 for cot). See the results on the deviations for these
- functions on my machine:
- checking tan on [-0.25*Pi, 0.25*Pi]
- max deviation from tanf(x): 1.19209e-07 at 0.250000006957*Pi, max deviation from cephes_tan(x):
- 5.96046e-08
- ->> precision OK for the tan_ps <<-
-
- checking tan on [-0.49*Pi, 0.49*Pi]
- max deviation from tanf(x): 3.8147e-06 at -0.490000009841*Pi, max deviation from cephes_tan(x):
- 9.53674e-07
- ->> precision OK for the tan_ps <<-
-
- checking cot on [0.2*Pi, 0.7*Pi]
- max deviation from cotf(x): 1.19209e-07 at 0.204303119606*Pi, max deviation from cephes_cot(x):
- 1.19209e-07
- ->> precision OK for the cot_ps <<-
-
- checking cot on [0.01*Pi, 0.99*Pi]
- max deviation from cotf(x): 3.8147e-06 at 0.987876517942*Pi, max deviation from cephes_cot(x):
- 9.53674e-07
- ->> precision OK for the cot_ps <<-
-
- With atan_ps and atan2_ps you get pretty good precision, atan_ps max deviation is < 2e-7 and
- atan2_ps max deviation is < 2.5e-7
- */
-
- /* Copyright (C) 2016 Tolga Mizrak
-
- This software is provided 'as-is', without any express or implied
- warranty. In no event will the authors be held liable for any damages
- arising from the use of this software.
-
- Permission is granted to anyone to use this software for any purpose,
- including commercial applications, and to alter it and redistribute it
- freely, subject to the following restrictions:
-
- 1. The origin of this software must not be misrepresented; you must not
- claim that you wrote the original software. If you use this software
- in a product, an acknowledgment in the product documentation would be
- appreciated but is not required.
- 2. Altered source versions must be plainly marked as such, and must not be
- misrepresented as being the original software.
- 3. This notice may not be removed or altered from any source distribution.
-
- (this is the zlib license)
- */
- #pragma once
- #include "sse_mathfun.h"
-
-
- inline __m128 sse_mathfun_tancot_ps(__m128 x, int cotFlag) {
- __m128 p0 = _mm_set_ps1(9.38540185543E-3);
- __m128 p1 = _mm_set_ps1(3.11992232697E-3);
- __m128 p2 = _mm_set_ps1(2.44301354525E-2);
- __m128 p3 = _mm_set_ps1(5.34112807005E-2);
- __m128 p4 = _mm_set_ps1(1.33387994085E-1);
- __m128 p5 = _mm_set_ps1(3.33331568548E-1);
-
- __m128 xmm1, xmm2 = _mm_setzero_ps(), xmm3, sign_bit, y;
-
- __m128i emm2;
- sign_bit = x;
- /* take the absolute value */
- __m128 sign_mask = _mm_castsi128_ps(_mm_set1_epi32(0x80000000));
- __m128 inv_sign_mask = _mm_castsi128_ps(_mm_set1_epi32(~0x80000000));
- x = _mm_and_ps(x, inv_sign_mask);
- /* extract the sign bit (upper one) */
- sign_bit = _mm_and_ps(sign_bit, sign_mask);
-
- /* scale by 4/Pi */
- __m128 cephes_FOPI = _mm_set_ps1(1.27323954473516);
- y = _mm_mul_ps(x, cephes_FOPI);
-
- /* store the integer part of y in mm0 */
- emm2 = _mm_cvttps_epi32(y);
- /* j=(j+1) & (~1) (see the cephes sources) */
- emm2 = _mm_add_epi32(emm2, _mm_set1_epi32(1));
- emm2 = _mm_and_si128(emm2, _mm_set1_epi32(~1));
- y = _mm_cvtepi32_ps(emm2);
-
- emm2 = _mm_and_si128(emm2, _mm_set1_epi32(2));
- emm2 = _mm_cmpeq_epi32(emm2, _mm_setzero_si128());
-
- __m128 poly_mask = _mm_castsi128_ps(emm2);
- /* The magic pass: "Extended precision modular arithmetic"
- x = ((x - y * DP1) - y * DP2) - y * DP3; */
- __m128 minus_cephes_DP1 = _mm_set_ps1(-0.78515625);
- __m128 minus_cephes_DP2 = _mm_set_ps1(-2.4187564849853515625e-4);
- __m128 minus_cephes_DP3 = _mm_set_ps1(-3.77489497744594108e-8);
- xmm1 = minus_cephes_DP1;
- xmm2 = minus_cephes_DP2;
- xmm3 = minus_cephes_DP3;
- xmm1 = _mm_mul_ps(y, xmm1);
- xmm2 = _mm_mul_ps(y, xmm2);
- xmm3 = _mm_mul_ps(y, xmm3);
- __m128 z = _mm_add_ps(x, xmm1);
- z = _mm_add_ps(z, xmm2);
- z = _mm_add_ps(z, xmm3);
-
- __m128 zz = _mm_mul_ps(z, z);
-
- y = p0;
- y = _mm_mul_ps(y, zz);
- y = _mm_add_ps(y, p1);
- y = _mm_mul_ps(y, zz);
- y = _mm_add_ps(y, p2);
- y = _mm_mul_ps(y, zz);
- y = _mm_add_ps(y, p3);
- y = _mm_mul_ps(y, zz);
- y = _mm_add_ps(y, p4);
- y = _mm_mul_ps(y, zz);
- y = _mm_add_ps(y, p5);
- y = _mm_mul_ps(y, zz);
- y = _mm_mul_ps(y, z);
- y = _mm_add_ps(y, z);
-
- __m128 y2;
- if (cotFlag) {
- y2 = _mm_xor_ps(y, sign_mask);
- /* y = _mm_rcp_ps(y); */
- /* using _mm_rcp_ps here loses on way too much precision, better to do a div */
- y = _mm_div_ps(_mm_set_ps1(1.f), y);
- }
- else {
- /* y2 = _mm_rcp_ps(y); */
- /* using _mm_rcp_ps here loses on way too much precision, better to do a div */
- y2 = _mm_div_ps(_mm_set_ps1(1.f), y);
- y2 = _mm_xor_ps(y2, sign_mask);
- }
-
- /* select the correct result from the two polynoms */
- xmm3 = poly_mask;
- y = _mm_and_ps(xmm3, y);
- y2 = _mm_andnot_ps(xmm3, y2);
- y = _mm_or_ps(y, y2);
-
- /* update the sign */
- y = _mm_xor_ps(y, sign_bit);
-
- return y;
- }
-
- inline __m128 sse_mathfun_tan_ps(__m128 x) {
- return sse_mathfun_tancot_ps(x, 0);
- }
-
- inline __m128 sse_mathfun_cot_ps(__m128 x) {
- return sse_mathfun_tancot_ps(x, 1);
- }
-
-
- inline __m128 sse_mathfun_atan_ps(__m128 x) {
- __m128 sign_mask = _mm_castsi128_ps(_mm_set1_epi32(0x80000000));
- __m128 inv_sign_mask = _mm_castsi128_ps(_mm_set1_epi32(~0x80000000));
-
- __m128 atanrange_hi = _mm_set_ps1(2.414213562373095);
- __m128 atanrange_lo = _mm_set_ps1(0.4142135623730950);
- __m128 cephes_PIO2F = _mm_set_ps1(1.5707963267948966192);
- __m128 cephes_PIO4F = _mm_set_ps1(0.7853981633974483096);
-
- __m128 atancof_p0 = _mm_set_ps1(8.05374449538e-2);
- __m128 atancof_p1 = _mm_set_ps1(1.38776856032E-1);
- __m128 atancof_p2 = _mm_set_ps1(1.99777106478E-1);
- __m128 atancof_p3 = _mm_set_ps1(3.33329491539E-1);
-
- __m128 sign_bit, y;
-
- sign_bit = x;
- /* take the absolute value */
- x = _mm_and_ps(x, inv_sign_mask);
- /* extract the sign bit (upper one) */
- sign_bit = _mm_and_ps(sign_bit, sign_mask);
-
- /* range reduction, init x and y depending on range */
- /* x > 2.414213562373095 */
- __m128 cmp0 = _mm_cmpgt_ps(x, atanrange_hi);
- /* x > 0.4142135623730950 */
- __m128 cmp1 = _mm_cmpgt_ps(x, atanrange_lo);
-
- /* x > 0.4142135623730950 && !(x > 2.414213562373095) */
- __m128 cmp2 = _mm_andnot_ps(cmp0, cmp1);
-
- /* -(1.0/x) */
- __m128 y0 = _mm_and_ps(cmp0, cephes_PIO2F);
- __m128 x0 = _mm_div_ps(_mm_set_ps1(1.f), x);
- x0 = _mm_xor_ps(x0, sign_mask);
-
- __m128 y1 = _mm_and_ps(cmp2, cephes_PIO4F);
- /* (x-1.0)/(x+1.0) */
- __m128 x1_o = _mm_sub_ps(x, _mm_set_ps1(1.f));
- __m128 x1_u = _mm_add_ps(x, _mm_set_ps1(1.f));
- __m128 x1 = _mm_div_ps(x1_o, x1_u);
-
- __m128 x2 = _mm_and_ps(cmp2, x1);
- x0 = _mm_and_ps(cmp0, x0);
- x2 = _mm_or_ps(x2, x0);
- cmp1 = _mm_or_ps(cmp0, cmp2);
- x2 = _mm_and_ps(cmp1, x2);
- x = _mm_andnot_ps(cmp1, x);
- x = _mm_or_ps(x2, x);
-
- y = _mm_or_ps(y0, y1);
-
- __m128 zz = _mm_mul_ps(x, x);
- __m128 acc = atancof_p0;
- acc = _mm_mul_ps(acc, zz);
- acc = _mm_sub_ps(acc, atancof_p1);
- acc = _mm_mul_ps(acc, zz);
- acc = _mm_add_ps(acc, atancof_p2);
- acc = _mm_mul_ps(acc, zz);
- acc = _mm_sub_ps(acc, atancof_p3);
- acc = _mm_mul_ps(acc, zz);
- acc = _mm_mul_ps(acc, x);
- acc = _mm_add_ps(acc, x);
- y = _mm_add_ps(y, acc);
-
- /* update the sign */
- y = _mm_xor_ps(y, sign_bit);
-
- return y;
- }
-
- inline __m128 sse_mathfun_atan2_ps(__m128 y, __m128 x) {
- __m128 sign_mask = _mm_castsi128_ps(_mm_set1_epi32(0x80000000));
- __m128 x_eq_0 = _mm_cmpeq_ps(x, _mm_setzero_ps());
- __m128 x_gt_0 = _mm_cmpgt_ps(x, _mm_setzero_ps());
- __m128 x_le_0 = _mm_cmple_ps(x, _mm_setzero_ps());
- __m128 y_eq_0 = _mm_cmpeq_ps(y, _mm_setzero_ps());
- __m128 x_lt_0 = _mm_cmplt_ps(x, _mm_setzero_ps());
- __m128 y_lt_0 = _mm_cmplt_ps(y, _mm_setzero_ps());
- __m128 cephes_PIF = _mm_set_ps1(3.141592653589793238);
- __m128 cephes_PIO2F = _mm_set_ps1(1.5707963267948966192);
-
- __m128 zero_mask = _mm_and_ps(x_eq_0, y_eq_0);
- __m128 zero_mask_other_case = _mm_and_ps(y_eq_0, x_gt_0);
- zero_mask = _mm_or_ps(zero_mask, zero_mask_other_case);
-
- __m128 pio2_mask = _mm_andnot_ps(y_eq_0, x_eq_0);
- __m128 pio2_mask_sign = _mm_and_ps(y_lt_0, sign_mask);
- __m128 pio2_result = cephes_PIO2F;
- pio2_result = _mm_xor_ps(pio2_result, pio2_mask_sign);
- pio2_result = _mm_and_ps(pio2_mask, pio2_result);
-
- __m128 pi_mask = _mm_and_ps(y_eq_0, x_le_0);
- __m128 pi = cephes_PIF;
- __m128 pi_result = _mm_and_ps(pi_mask, pi);
-
- __m128 swap_sign_mask_offset = _mm_and_ps(x_lt_0, y_lt_0);
- swap_sign_mask_offset = _mm_and_ps(swap_sign_mask_offset, sign_mask);
-
- __m128 offset0 = _mm_setzero_ps();
- __m128 offset1 = cephes_PIF;
- offset1 = _mm_xor_ps(offset1, swap_sign_mask_offset);
-
- __m128 offset = _mm_andnot_ps(x_lt_0, offset0);
- offset = _mm_and_ps(x_lt_0, offset1);
-
- __m128 arg = _mm_div_ps(y, x);
- __m128 atan_result = sse_mathfun_atan_ps(arg);
- atan_result = _mm_add_ps(atan_result, offset);
-
- /* select between zero_result, pio2_result and atan_result */
-
- __m128 result = _mm_andnot_ps(zero_mask, pio2_result);
- atan_result = _mm_andnot_ps(pio2_mask, atan_result);
- atan_result = _mm_andnot_ps(pio2_mask, atan_result);
- result = _mm_or_ps(result, atan_result);
- result = _mm_or_ps(result, pi_result);
-
- return result;
- }
|