Browse Source

Implemented vectorized version of Tan (#187)

Jorrit Rouwe 3 years ago
parent
commit
3447528a66
4 changed files with 61 additions and 1 deletions
  1. 1 1
      Jolt/Math/Trigonometry.h
  2. 3 0
      Jolt/Math/Vec4.h
  3. 33 0
      Jolt/Math/Vec4.inl
  4. 24 0
      UnitTests/Math/Vec4Tests.cpp

+ 1 - 1
Jolt/Math/Trigonometry.h

@@ -26,7 +26,7 @@ JPH_INLINE float Cos(float inX)
 /// Tangent of x
 /// Tangent of x
 JPH_INLINE float Tan(float inX)
 JPH_INLINE float Tan(float inX)
 {
 {
-	return tan(inX);
+	return Vec4::sReplicate(inX).Tan().GetX();
 }
 }
 
 
 /// Arc sine of x
 /// Arc sine of x

+ 3 - 0
Jolt/Math/Vec4.h

@@ -237,6 +237,9 @@ public:
 	/// Calcluate the sine and cosine for each element of this vector
 	/// Calcluate the sine and cosine for each element of this vector
 	inline void					SinCos(Vec4 &outSin, Vec4 &outCos) const;
 	inline void					SinCos(Vec4 &outSin, Vec4 &outCos) const;
 
 
+	/// Calcluate the tangent for each element of this vector
+	inline Vec4					Tan() const;
+
 	/// To String
 	/// To String
 	friend ostream &			operator << (ostream &inStream, Vec4Arg inV)
 	friend ostream &			operator << (ostream &inStream, Vec4Arg inV)
 	{
 	{

+ 33 - 0
Jolt/Math/Vec4.inl

@@ -777,4 +777,37 @@ void Vec4::SinCos(Vec4 &outSin, Vec4 &outCos) const
 	outCos = Vec4::sXor(c, cos_sign.ReinterpretAsFloat());
 	outCos = Vec4::sXor(c, cos_sign.ReinterpretAsFloat());
 }
 }
 
 
+Vec4 Vec4::Tan() const
+{
+	// Implementation based on tanf.c from the cephes library, see Vec4::SinCos for further details
+	// Original implementation by Stephen L. Moshier (See: http://www.moshier.net/)
+
+	// Make argument positive
+	UVec4 tan_sign = UVec4::sAnd(ReinterpretAsInt(), UVec4::sReplicate(0x80000000U));
+	Vec4 x = Vec4::sXor(*this, tan_sign.ReinterpretAsFloat());
+
+	// x / (PI / 2) rounded to nearest int gives us the quadrant closest to x
+	UVec4 quadrant = (0.6366197723675814f * x + Vec4::sReplicate(0.5f)).ToInt();
+
+	// Remap x to range [-PI / 4, PI / 4], see Vec4::SinCos
+	Vec4 float_quadrant = quadrant.ToFloat();
+	x = ((x - float_quadrant * 1.5703125f) - float_quadrant * 0.0004837512969970703125f) - float_quadrant * 7.549789948768648e-8f;
+
+	// Calculate x2 = x^2	
+	Vec4 x2 = x * x;
+
+	// Roughly equivalent to the Taylor expansion:
+	// Tan(x) = x + x^3/3 + 2*x^5/15 + 17*x^7/315 + 62*x^9/2835 + ...
+	Vec4 tan =
+		(((((9.38540185543e-3f * x2 + Vec4::sReplicate(3.11992232697e-3f)) * x2 + Vec4::sReplicate(2.44301354525e-2f)) * x2
+		+ Vec4::sReplicate(5.34112807005e-2f)) * x2 + Vec4::sReplicate(1.33387994085e-1f)) * x2 + Vec4::sReplicate(3.33331568548e-1f)) * x2 * x + x;
+
+	// For the 2nd and 4th quadrant we need to invert the value
+	UVec4 bit1 = quadrant.LogicalShiftLeft<31>();
+	tan = Vec4::sSelect(tan, Vec4::sReplicate(-1.0f) / tan, bit1);
+
+	// Put the sign back
+	return Vec4::sXor(tan, tan_sign.ReinterpretAsFloat());
+}
+
 JPH_NAMESPACE_END
 JPH_NAMESPACE_END

+ 24 - 0
UnitTests/Math/Vec4Tests.cpp

@@ -538,4 +538,28 @@ TEST_SUITE("Vec4Tests")
 		CHECK(ms < 1.0e-7f);
 		CHECK(ms < 1.0e-7f);
 		CHECK(mc < 1.0e-7f);
 		CHECK(mc < 1.0e-7f);
 	}
 	}
+
+	TEST_CASE("TestVec4Tan")
+	{
+		double mt = 0.0;
+
+		for (float x = -100.0f * JPH_PI; x < 100.0f * JPH_PI; x += 1.0e-3f)
+		{
+			// Create a vector with intermediate values
+			Vec4 xv = Vec4::sReplicate(x) + Vec4(0.0e-4f, 2.5e-4f, 5.0e-4f, 7.5e-4f);
+
+			// Calculate tan
+			Vec4 vt = xv.Tan();
+
+			for (int i = 0; i < 4; ++i)
+			{
+				// Check accuracy of tan
+				double t1 = tan((double)xv[i]), t2 = (double)vt[i];
+				double dt = abs(t2 - t1);
+				mt = max(mt, dt) / max(1.0, abs(t1)); // Take relative error
+			}
+		}
+
+		CHECK(mt < 1.5e-7f);
+	}
 }
 }