Update nearly all of the basic/standalone C4Script functions to use C4Numeric/C4Real

Drop ArcSin/ArcCos until someone needs them
floating-point
Julius Michaelis 2012-03-27 15:13:55 +02:00
parent 7e0a51c0c7
commit d56843cc9c
4 changed files with 164 additions and 68 deletions

View File

@ -6,6 +6,7 @@
* Copyright (c) 2010 Benjamin Herr
* Copyright (c) 2010 Nicolas Hake
* Copyright (c) 2010 Günther Brammer
* Copyright (c) 2012 Julius Michaelis
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
*
* Portions might be copyrighted by other authors who have contributed
@ -25,6 +26,9 @@
#include "StdCompiler.h"
const C4Real::StorageType C4Real::PI = {0x40490fdb};
const C4Real::StorageType C4Real::E = {0x402df854};
void CompileFunc(C4Real &rValue, StdCompiler *pComp)
{
// Read value (as float)
@ -77,9 +81,69 @@ C4Real Cos(const C4Real &real)
}
C4Real Pow(const C4Real &x, const C4Real &y)
{
if(_mm_comieq_ss(x.value, C4Real::float_hexconst(0x0))) return 0;
bool sign = _mm_comilt_ss(x.value, C4Real::float_hexconst(0x0));
if(sign)
{
__m128 sig = _mm_or_ps(_mm_and_ps(y.value, C4Real::float_hexconst(0x80000000)), C4Real::float_hexconst(0x4b000000)); // generate (sign?+:-)2^23
__m128 trunc = _mm_sub_ps(_mm_add_ps(y.value, sig), sig); // truncate bits after comma
if(_mm_comineq_ss(y.value, trunc))
{
C4Real nan;
nan.value = C4Real::float_hexconst(0x7f800001); // signaling nan
return nan;
}
}
__m128 abs = _mm_and_ps(x.value, C4Real::float_hexconst(0x7fffffff));
C4Real val;
val.value = log_ps(x.value);
val.value = log_ps(abs);
val *= y;
val.value = exp_ps(val.value);
return val;
if(sign)
{
val.value = _mm_or_ps(x.value, C4Real::float_hexconst(0x80000000));
return val;
}
else
return val;
}
C4Real Sqrt(const C4Real &v)
{
C4Real nrv;
nrv.value = _mm_sqrt_ps(v.value);
return nrv;
}
C4Real Log(const C4Real &v)
{
C4Real ret;
ret.value = log_ps(v.value);
return ret;
}
C4Real Atan2(const C4Real & y, const C4Real & x)
{
__m128 t0, t1, t3, t4;
t1 = _mm_and_ps(x.value, C4Real::float_hexconst(0x7fffffff));
t3 = _mm_and_ps(y.value, C4Real::float_hexconst(0x7fffffff));
bool axltay = _mm_comilt_ss(t1, t3);
if(axltay)
t3 = _mm_mul_ps(t1, _mm_rcp_ss(t3));
else
t3 = _mm_mul_ps(t3, _mm_rcp_ss(t1));
t4 = _mm_mul_ps(t3, t3);
t0 = C4Real::float_hexconst(0xbc5cdd30);
t0 = _mm_add_ps(_mm_mul_ps(t0, t4), C4Real::float_hexconst(0x3d6b6d55));
t0 = _mm_add_ps(_mm_mul_ps(t0, t4), C4Real::float_hexconst(0xbdf84c31));
t0 = _mm_add_ps(_mm_mul_ps(t0, t4), C4Real::float_hexconst(0x3e4854c9));
t0 = _mm_add_ps(_mm_mul_ps(t0, t4), C4Real::float_hexconst(0xbeaa7e45));
t0 = _mm_add_ps(_mm_mul_ps(t0, t4), C4Real::float_hexconst(0x3f7fffb7));
t3 = _mm_mul_ps(t0, t3);
t3 = (axltay) ? _mm_sub_ps(C4Real::float_hexconst(0x3fc90fdb), t3) : t3;
t3 = _mm_comilt_ss(x.value, C4Real::float_hexconst(0)) ? _mm_sub_ps(C4Real::float_hexconst(0x40490fdb), t3) : t3;
t3 = _mm_comilt_ss(y.value, C4Real::float_hexconst(0)) ? _mm_xor_ps(t3, C4Real::float_hexconst(0x80000000)) : t3;
C4Real ret;
ret.value = _mm_mul_ps(t3, _mm_div_ss(C4Real::float_hexconst(0x43340000), C4Real(C4Real::PI).value));
return ret;
}

View File

@ -6,6 +6,7 @@
* Copyright (c) 2002, 2004-2005, 2007, 2009 Peter Wortmann
* Copyright (c) 2005, 2007 Günther Brammer
* Copyright (c) 2010 Nicolas Hake
* Copyright (c) 2010 Günther Brammer
* Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
*
* Portions might be copyrighted by other authors who have contributed
@ -46,6 +47,9 @@ class C4Real
friend C4Real Sin(const C4Real &);
friend C4Real Cos(const C4Real &);
friend C4Real Pow(const C4Real &, const C4Real &);
friend C4Real Sqrt(const C4Real &);
friend C4Real Log(const C4Real &);
friend C4Real Atan2(const C4Real &, const C4Real &);
public:
inline C4Real(float val = 0.0f) : value(_mm_set_ss(val)) { }
@ -68,7 +72,7 @@ public:
inline C4Real &operator -= (const C4Real &rhs) { value = _mm_sub_ps(value, rhs.value); return *this; }
inline C4Real &operator *= (const C4Real &rhs) { value = _mm_mul_ps(value, rhs.value); return *this; }
inline C4Real &operator /= (const C4Real &rhs) { value = _mm_div_ss(value, rhs.value); return *this; }
private: inline __m128 float_hexconst(int32_t c) { union {float f;int i;} fi; fi.i=c; return _mm_set_ps1(fi.f); } public: // can't use a reinterpret_cast here because the parameter is type-punned
private: static inline __m128 float_hexconst(int32_t c) { union {float f;int i;} fi; fi.i=c; return _mm_set_ps1(fi.f); } public: // can't use a reinterpret_cast here because the parameter is type-punned
inline C4Real &operator %= (const C4Real &rhs) {
// unfortunately, there is no _mm_mod_ss. The trick is to abuse the 23 bit significancy by adding (or substracting) a value, which will leave the result truncated. We can work with that
__m128 div = _mm_div_ss(value, rhs.value);
@ -143,8 +147,12 @@ public:
friend bool operator==(StorageType lhs, StorageType rhs) { return lhs.v == rhs.v; }
C4Real(StorageType rhs) : value(_mm_load_ss(reinterpret_cast<float*>(&rhs.v))) {}
operator StorageType() const { StorageType nrv; _mm_store_ss(reinterpret_cast<float*>(&nrv.v), value); return nrv; }
inline C4Real(StorageType rhs) : value(_mm_load_ss(reinterpret_cast<float*>(&rhs.v))) {}
inline operator StorageType() const { StorageType nrv; _mm_store_ss(reinterpret_cast<float*>(&nrv.v), value); return nrv; }
public:
static const StorageType PI;
static const StorageType E;
};
// conversion
@ -168,6 +176,9 @@ void CompileFunc(C4Real &rValue, StdCompiler *pComp);
C4Real Sin(const C4Real &);
C4Real Cos(const C4Real &);
C4Real Pow(const C4Real &, const C4Real &);
C4Real Pow(const C4Real &base, const C4Real &exponen);
C4Real Sqrt(const C4Real &);
C4Real Log(const C4Real &);
C4Real Atan2(const C4Real &y, const C4Real &x);
#endif //C4REAL_H_INC

View File

@ -56,7 +56,9 @@ class C4Numeric {
inline C4Numeric &operator op##= (const C4Real &rhs) { val op##= C4Value(rhs); return *this; } \
inline C4Numeric operator op (const C4Real &rhs) const { C4Value ret = val; ret op##= C4Value(rhs); return C4Numeric(ret); } \
inline C4Numeric &operator op##= (const int32_t &rhs) { val op##= C4Value(rhs); return *this; } \
inline C4Numeric operator op (const int32_t &rhs) const { C4Value ret = val; ret op##= C4Value(rhs); return C4Numeric(ret); }
inline C4Numeric operator op (const int32_t &rhs) const { C4Value ret = val; ret op##= C4Value(rhs); return C4Numeric(ret); } \
inline C4Numeric &operator op##= (const long &rhs) { val op##= C4Value((int32_t)rhs); return *this; } \
inline C4Numeric operator op (const long &rhs) const { C4Value ret = val; ret op##= C4Value((int32_t)rhs); return C4Numeric(ret); }
C4NUMERIC_OPERATOR(+)
C4NUMERIC_OPERATOR(-)
C4NUMERIC_OPERATOR(*)
@ -68,7 +70,7 @@ class C4Numeric {
C4Numeric operator++(int) { C4Numeric old = *this; ++val; return old; }
C4Numeric & operator--() {--val; return *this; }
C4Numeric operator--(int) { C4Numeric old = *this; --val; return old; }
C4Numeric operator-() { C4Value val = -val; return C4Numeric(val); }
C4Numeric operator-() { C4Value val = -this->val; return C4Numeric(val); }
C4Numeric & operator+() { return *this; }
C4Numeric Pow(const C4Numeric &rhs) const { return C4Numeric(val.Pow(rhs.val)); }
@ -96,7 +98,8 @@ class C4Numeric {
COMP(C4Numeric, C4Numeric) { return V1.getVal() == V2.getVal(); }
BICOMP(C4Numeric, C4Value) { return V1.getVal() == V2; }
BICOMP(C4Numeric, int32_t) { return V1.getInt() == V2; }
BICOMP(C4Numeric, C4Real) { return V1.getFloat() == V2; }
BICOMP(C4Numeric, long) { return V1.getInt() == V2; }
BICOMP(C4Numeric, C4Real) { return V1.getFloat() == V2; }
BICOMP(C4Numeric, bool) { return V1.getBool() == V2; }
#undef BICOMP
#undef COMPND
@ -105,6 +108,8 @@ BICOMP(C4Numeric, bool) { return V1.getBool() == V2; }
#define RC4NUMERIC_OPERATOR(op) \
inline int32_t & operator op##= (int32_t & lhs, const C4Numeric & rhs) { return lhs += rhs.getInt(); } \
inline C4Numeric operator op (const int32_t & lhs, const C4Numeric & rhs) { return C4Numeric(lhs) op##= rhs; } \
inline long & operator op##= (long & lhs, const C4Numeric & rhs) { return lhs += rhs.getInt(); } \
inline C4Numeric operator op (const long & lhs, const C4Numeric & rhs) { return C4Numeric((int32_t)lhs) op##= rhs; } \
inline C4Real & operator op##= (C4Real & lhs, const C4Numeric & rhs) { return lhs += rhs.getFloat(); } \
inline C4Numeric operator op (const C4Real & lhs, const C4Numeric & rhs) { return C4Numeric(lhs) op##= rhs; }
RC4NUMERIC_OPERATOR(+)
@ -116,7 +121,23 @@ BICOMP(C4Numeric, bool) { return V1.getBool() == V2; }
#define C4_NUMERIC_H
#endif
#define BI_INEQ_COMP(t, op) \
inline bool operator op (const t & v1, const C4Numeric & v2) { if(v2.GetType() == C4V_Float) return C4Real(v1) op v2.getFloat(); return v1 op v2.getInt(); } \
inline bool operator op (const C4Numeric & v1, const t & v2) { if(v1.GetType() == C4V_Float) return v1.getFloat() op C4Real(v2); return v1.getInt() op v2; }
#define INEQ_COMP(op) \
inline bool operator op (const C4Numeric & n1, const C4Numeric & n2) { return n1.getVal() op n2.getVal(); } \
BI_INEQ_COMP(int32_t, op) \
BI_INEQ_COMP(long, op) \
BI_INEQ_COMP(C4Real, op)
INEQ_COMP(<)
INEQ_COMP(>)
INEQ_COMP(<=)
INEQ_COMP(>=)
#undef INEQ_COMP
#undef BI_INEQ_COMP
inline C4Real Sin(const C4Numeric &real) { return Sin(real.getFloat()); }
inline C4Real Cos(const C4Numeric &real) { return Cos(real.getFloat()); }
inline C4Numeric Pow(const C4Numeric &x, const C4Numeric &y) { return x.Pow(y); }
C4Numeric Sqrt(const C4Numeric & v);

View File

@ -230,69 +230,55 @@ static C4ID FnC4Id(C4AulContext *cthr, C4String *szID)
return(C4ID(FnStringPar(szID)));
}
static long FnAbs(C4AulContext *cthr, long iVal)
static C4Numeric FnAbs(C4AulContext *cthr, C4Numeric nVal)
{
return Abs(iVal);
return Abs(nVal);
}
static long FnSin(C4AulContext *cthr, long iAngle, long iRadius, long iPrec)
static C4Numeric FnSin(C4AulContext *cthr, C4Numeric nAngle, C4Numeric nRadius, long iPrec)
{
if (!iPrec) iPrec = 1;
// Precalculate the modulo operation so the C4Fixed argument to Sin does not overflow
iAngle %= 360 * iPrec;
// Let itofix and fixtoi handle the division and multiplication because that can handle higher ranges
return fixtoi(Sin(itofix(iAngle, iPrec)), iRadius);
bool retint = nAngle.GetType() != C4V_Float && nRadius.GetType() != C4V_Float;
if(!nRadius)
return Sin(nAngle);
if(!iPrec)
return retint? (Sin(nAngle)*nRadius).getInt() : Sin(nAngle)*nRadius;
nAngle %= 360 * iPrec;
C4Numeric v = Sin(nAngle.getFloat()/(int)iPrec)*nRadius;
if(!retint)
return v;
return v.getInt();
}
static long FnCos(C4AulContext *cthr, long iAngle, long iRadius, long iPrec)
static C4Numeric FnCos(C4AulContext *cthr, C4Numeric nAngle, C4Numeric nRadius, long iPrec)
{
if (!iPrec) iPrec = 1;
iAngle %= 360 * iPrec;
return fixtoi(Cos(itofix(iAngle, iPrec)), iRadius);
long iOrtho = 90;
if (iPrec) iOrtho *= iPrec;
return FnSin(cthr, iOrtho - nAngle, nRadius, iPrec);
}
static long FnSqrt(C4AulContext *cthr, long iValue)
static C4Numeric FnSqrt(C4AulContext *cthr, C4Numeric nValue)
{
if (iValue<0) return 0;
long iSqrt = long(sqrt(double(iValue)));
if (iSqrt * iSqrt < iValue) iSqrt++;
if (iSqrt * iSqrt > iValue) iSqrt--;
return iSqrt;
return Sqrt(nValue);
}
static long FnAngle(C4AulContext *cthr, long iX1, long iY1, long iX2, long iY2, long iPrec)
static C4Real FnLn(C4AulContext *cthr, C4Real nVal)
{
long iAngle;
return Log(nVal);
}
static C4Numeric FnAngle(C4AulContext *cthr, C4Numeric iX1, C4Numeric iY1, C4Numeric iX2, C4Numeric iY2, C4Numeric iPrec)
{
C4Real iAngle;
bool retint = iPrec.GetType() != C4V_Float && iPrec;
// Standard prec
if (!iPrec) iPrec = 1;
long dx=iX2-iX1,dy=iY2-iY1;
if (!dx)
{
if (dy>0) return 180 * iPrec;
else return 0;
}
if (!dy)
{
if (dx>0) return 90 * iPrec;
else return 270 * iPrec;
}
C4Numeric dx=iX2-iX1,dy=iY1-iY2;
iAngle = static_cast<long>(180.0 * iPrec * atan2(static_cast<double>(Abs(dy)), static_cast<double>(Abs(dx))) / M_PI);
iAngle = (Atan2(dx.getFloat(), dy.getFloat()) * iPrec.getFloat());
if (iX2>iX1 )
{
if (iY2<iY1) iAngle = (90 * iPrec) - iAngle;
else iAngle = (90 * iPrec) + iAngle;
}
else
{
if (iY2<iY1) iAngle = (270 * iPrec) + iAngle;
else iAngle = (270 * iPrec) - iAngle;
}
return iAngle;
return retint ? C4Numeric(iAngle).getInt() : C4Numeric(iAngle);
}
static long FnArcSin(C4AulContext *cthr, long iVal, long iRadius)
@ -319,39 +305,45 @@ static long FnArcCos(C4AulContext *cthr, long iVal, long iRadius)
return (long) floor(f1+0.5);
}
static long FnMin(C4AulContext *cthr, long iVal1, long iVal2)
static C4Numeric FnMin(C4AulContext *cthr, C4Numeric nVal1, C4Numeric nVal2)
{
return Min(iVal1,iVal2);
return Min(nVal1,nVal2);
}
static long FnMax(C4AulContext *cthr, long iVal1, long iVal2)
static C4Numeric FnMax(C4AulContext *cthr, C4Numeric iVal1, C4Numeric iVal2)
{
return Max(iVal1,iVal2);
}
static long FnDistance(C4AulContext *cthr, long iX1, long iY1, long iX2, long iY2)
static C4Numeric FnDistance(C4AulContext *cthr, C4Numeric nX1, C4Numeric nY1, C4Numeric nX2, C4Numeric nY2)
{
return Distance(iX1,iY1,iX2,iY2);
return Sqrt((nX1-nX2).Pow(2) + (nY1-nY2).Pow(2));
}
static long FnBoundBy(C4AulContext *cthr, long iVal, long iRange1, long iRange2)
static C4Numeric FnBoundBy(C4AulContext *cthr, C4Numeric nVal, C4Numeric nRange1, C4Numeric nRange2)
{
return BoundBy(iVal,iRange1,iRange2);
return BoundBy(nVal,nRange1,nRange2);
}
static bool FnInside(C4AulContext *cthr, long iVal, long iRange1, long iRange2)
static bool FnInside(C4AulContext *cthr, C4Numeric nVal, C4Numeric nRange1, C4Numeric nRange2)
{
return Inside(iVal,iRange1,iRange2);
return Inside(nVal,nRange1,nRange2);
}
static long FnRandom(C4AulContext *cthr, long iRange)
static C4Numeric FnRandom(C4AulContext *cthr, C4Numeric iRange)
{
return Random(iRange);
if(!iRange)
return (Random(1<<11)<<12^Random(1<<12))/C4Real(1<<23);
else
return Random(iRange.getInt());
}
static long FnAsyncRandom(C4AulContext *cthr, long iRange)
static C4Numeric FnAsyncRandom(C4AulContext *cthr, C4Numeric iRange)
{
return SafeRandom(iRange);
if(!iRange)
return (SafeRandom(1<<11)<<12^SafeRandom(1<<12))/C4Real(1<<23);
else
return SafeRandom(iRange.getInt());
}
static int FnGetType(C4AulContext *cthr, const C4Value & Value)
@ -577,6 +569,9 @@ static Nillable<C4String *> FnGetConstantNameByValue(C4AulContext *ctx, int valu
return C4Void();
}
static int FnInt (C4AulContext *ctx, int i) { return i; }
static C4Real FnFloat(C4AulContext *ctx, C4Real f) { return f; }
//=========================== C4Script Function Map ===================================
C4ScriptConstDef C4ScriptConstMap[]=
@ -631,6 +626,8 @@ void InitCoreFunctionMap(C4AulScriptEngine *pEngine)
assert(pCDef->ValType == C4V_Int); // only int supported currently
pEngine->RegisterGlobalConstant(pCDef->Identifier, C4VInt(pCDef->Data));
}
pEngine->RegisterGlobalConstant("E", C4Value(C4Real::E));
pEngine->RegisterGlobalConstant("PI", C4Value(C4Real::PI));
// add all def script funcs
for (C4ScriptFnDef *pDef = &C4ScriptFnMap[0]; pDef->Identifier; pDef++)
@ -641,8 +638,9 @@ void InitCoreFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "Sin", FnSin);
AddFunc(pEngine, "Cos", FnCos);
AddFunc(pEngine, "Sqrt", FnSqrt);
AddFunc(pEngine, "ArcSin", FnArcSin);
AddFunc(pEngine, "ArcCos", FnArcCos);
AddFunc(pEngine, "Ln", FnLn);
//AddFunc(pEngine, "ArcSin", FnArcSin); If anyone should ever require these functions, fix them with C4Reals
//AddFunc(pEngine, "ArcCos", FnArcCos);
AddFunc(pEngine, "BoundBy", FnBoundBy);
AddFunc(pEngine, "Inside", FnInside);
AddFunc(pEngine, "Random", FnRandom);
@ -651,6 +649,8 @@ void InitCoreFunctionMap(C4AulScriptEngine *pEngine)
AddFunc(pEngine, "CreateArray", FnCreateArray);
AddFunc(pEngine, "CreatePropList", FnCreatePropList);
AddFunc(pEngine, "C4Id", FnC4Id, false);
AddFunc(pEngine, "Int", FnInt);
AddFunc(pEngine, "Float", FnFloat);
AddFunc(pEngine, "Distance", FnDistance);
AddFunc(pEngine, "Angle", FnAngle);
AddFunc(pEngine, "GetChar", FnGetChar);