When scaling to integer coordinates (especially pixel coordinates), using rounding to binary fractions, or rounding to powers of some approximate differential length, you can use GetRational()
to return an array of candidate rational values Q and R such that Q/R offers the scaling you require. This function returns if Ndigits
decimal digits to the right of the decimal point in the check-value calculated at each iteration are all zeros or all nines, which is normally the case when the ratio approaches close to integer Q and R. If that proves unsatisfactory, you can increase Ndigits
.
The function arguments are:
X
: Real value to be given a rational approximation.ArrayItems
: Length of array to be returned.Ndigits
: Number of digits expected.
The function return is: a D3tuple
array, each element of which contains the fields Numer
, Denom
, and Ratio
.
public D3tuple[] GetRational(double X, int ArrayItems, int Ndigits)
{
int d = ArrayItems+1;
D3tuple[] res = new D3tuple[d]; res[0] = new D3tuple();
int I;
double A,E,F,G,W;
double localEta = Math.Pow(10.0,-Math.Abs(Ndigits));
I = 0; F = 0; G = 1;
E = Math.Abs(X); A = E;
if (X != E) { G = -1; }
while ((I < d) & (F <= 23))
{
W = A - Math.Truncate(A);
if ((W <= localEta) | ((1.0-W) < localEta))
{
return res;
}
else
{
A = 1 / W;
if (A > Eta)
{
F = F + Math.Log(A);
if (F > 23) { return res; }
else if (I < d)
{
I++; res[0].Ratio = (double)I;
res[I] = new D3tuple();
res[I].Numer = G*Math.Exp(F + Math.Log(E)); res[I].Denom = Math.Exp(F);
if (F > 0) res[I].Ratio = res[I].Numer / res[I].Denom;
}
}
else { return res; }
}
}
return res;
}
Only two things are missing above. The first is the constant Eta
, which you may adjust as you see fit:
const double Eta = 1.0E-15;
The second is the definition of the class which returns the elements of the function result array:
public class D3tuple
{
double xx; double yy; double zz;
public D3tuple() { xx = 0; yy = 0; zz = 0; }
public D3tuple(double x, double y, double z)
{ xx = x; yy = y; zz = z; }
public void Set(double x, double y, double z)
{ xx = x; yy = y; zz = z; }
public void Get(ref double x, ref double y, ref double z)
{ x = xx; y = yy; z = zz; }
public double X
{ get { return xx; } set { xx = (double)value; } }
public double Y
{ get { return yy; } set { yy = (double)value; } }
public double Z
{ get { return zz; } set { zz = (double)value; } }
public double Numer
{ get { return xx; } set { xx = (double)value; } }
public double Denom
{ get { return yy; } set { yy = (double)value; } }
public double Ratio
{ get { return zz; } set { zz = (double)value; } }
public double this[int i]
{
get
{
double res = new double();
if ((i >= 1) & (i <= 3)) res = a4[i];
return res;
}
}
public double[] a
{
get { double[] v = new double[3];
v[0] = xx; v[1] = yy; v[2] = zz; return v; }
set
{
double[] v = value;
if (value.Length == 4)
{ xx = v[1]; yy = v[2]; zz = v[3]; }
else if (value.Length == 3)
{ xx = v[0]; yy = v[1]; zz = v[2]; }
}
}
public double[] a4
{
get { double[] v = new double[4]; v[1] = xx; v[2] = yy; v[3] = zz; return v; }
set
{
double[] v = value;
if (value.Length == 4) { xx = v[1]; yy = v[2]; zz = v[3]; }
}
}
public D3tuple Square()
{
D3tuple res = new D3tuple();
res.X = X*X; res.Y = Y*Y; res.Z = Z*Z;
return res;
}
public double Sum()
{
double res = new double();
res = X+Y+Z;
return res;
}
public void Zero() { xx = 0; yy = 0; zz = 0; }
public bool IsZero() { return ((X*X+Y*Y+Z*Z) == 0); }
public bool Eq(D3tuple d)
{
return ((X==d.X)&&(Y==d.Y)&&(Z==d.Z));
}
public static bool operator==(D3tuple d1, D3tuple d2)
{
return d1.Eq(d2);
}
public static bool operator!=(D3tuple d1, D3tuple d2)
{
return !d1.Eq(d2);
}
public override bool Equals(object obj)
{
return base.Equals(obj);
}
public override int GetHashCode()
{
return base.GetHashCode();
}
public static double operator~(D3tuple d1)
{
double d; d = Math.Sqrt(d1.X*d1.X+d1.Y*d1.Y+d1.Z*d1.Z);
return d;
}
public static D3tuple operator!(D3tuple d1)
{
D3tuple d = new D3tuple();
double L = ~d1;
if (L != 0) { d.X = d1.X / L; d.Y = d1.Y / L; d.Z = d1.Z / L; }
return d;
}
public static D3tuple operator-(D3tuple d1)
{
return new D3tuple(-d1.X, -d1.Y, -d1.Z);
}
public static D3tuple operator+(D3tuple d1, D3tuple d2)
{
return new D3tuple(d1.X+d2.X, d1.Y+d2.Y, d1.Z+d2.Z);
}
public static D3tuple operator-(D3tuple d1, D3tuple d2)
{
return new D3tuple(d1.X-d2.X, d1.Y-d2.Y, d1.Z-d2.Z);
}
public static double operator*(D3tuple d1, D3tuple d2)
{
double d = new double();
d = d1.xx*d2.xx+d1.yy*d2.yy+d1.zz*d2.zz;
return d;
}
public static D3tuple operator*(double h, D3tuple d1)
{
D3tuple dr = new D3tuple(h*d1.X,h*d1.Y,h*d1.Z);
return dr;
}
public static D3tuple operator/(D3tuple d1, D3tuple d2)
{
D3tuple d = new D3tuple();
double L = Math.Sqrt(d2.X*d2.X+d2.Y*d2.Y+d2.Z*d2.Z);
if (L != 0) { d.X = d1.X / L; d.Y = d1.Y / L; d.Z = d1.Z / L; }
return d;
}
public static D3tuple operator/(D3tuple d1, double v)
{
D3tuple d = new D3tuple(d1.X,d1.Y,d1.Z);
if (v != 0) { d.X = d1.X / v; d.Y = d1.Y / v; d.Z = d1.Z / v; }
return d;
}
public static D3tuple operator^(D3tuple d1, D3tuple d2)
{
return new D3tuple(
d1.yy*d2.zz-d2.yy*d1.zz,
d1.zz*d2.xx-d2.zz*d1.xx,
d1.xx*d2.yy-d2.xx*d1.yy);
}
public static D3tuple operator&(D3tuple d1, D3tuple d2)
{
return new D3tuple(d1.X*d2.X, d1.Y*d2.Y, d1.Z*d2.Z);
}
public static D3tuple operator>>(D3tuple d1, int k)
{
int c = k % 3; D3tuple d = new D3tuple(d1.X,d1.Y,d1.Z);
if ((c == 1)|(c == -2)) d.Set(d1.Y, d1.Z, d1.X);
else if ((c == 2)|(c == -1)) d.Set(d1.Z, d1.X, d1.Y);
return d;
}
public static D3tuple operator<<(D3tuple d1, int k)
{
int c = k % 3; D3tuple d = new D3tuple(d1.X,d1.Y,d1.Z);
if ((c == -1)|(c == 2)) d.Set(d1.Y, d1.Z, d1.X);
else if ((c == -2)|(c == 1)) d.Set(d1.Z, d1.X, d1.Y);
return d;
}
bool WriteSquare = false;
string sf(double d)
{
string res = ""; if (WriteSquare) d = d * d;
res = string.Format("{0,18:F15}", d);
int p = res.IndexOf(' '),pm=res.IndexOf('-'),L=res.Length;
if (p >= 0)
{
while ((p < L) & (res[p] == ' ')) p++;
if ((p > 0) & (p == pm)) { res.Remove(p-1, 1); }
else if (pm < 0) { }
}
else if (pm < 0) { res = " " + res; }
return res;
}
public override string ToString()
{
return "["+sf(xx)+","+sf(yy)+","+sf(zz)+"]";
}
public string ToSqString()
{
WriteSquare = true; string s=this.ToString(); WriteSquare = false;
return s;
}
}
There's no sense in including the D3tuple
class without showing how its major features can be used. So here's a demonstration:
public void AssignmentsExample()
{
D3tuple Diag = new D3tuple(1,2,3);
double[] v = new double[3];
v = Diag.a;
v[0] = 0.1; v[1] = 0.2; v[2] = 0.3;
Diag.a = v;
v[1] = Diag.a[1];
Diag.a[2] = v[2];
D3tuple
x1=new D3tuple(1,-2,3),
x2=new D3tuple(2,3,-4),
x3=new D3tuple(1,0,0);
Diag.Set(
x1.a[1]*x2.a[2]-x1.a[2]*x2.a[1],
x1.a[2]*x2.a[0]-x1.a[0]*x2.a[2],
x1.a[0]*x2.a[1]-x1.a[1]*x2.a[0]);
Diag = x1^x2;
v[0] = x1*x2;
v[0] = Diag*(x1^x2);
Diag = x1 & x2;
Diag = 5.0 * x1;
Diag = x1 / 2;
Diag = x1 / x2;
v[0] = ~x1;
Diag = !x1;
if (!x1 == (x1 / x1)) { x1 = (~x1) * !x1; }
Diag = (x1^x2) / (x3 * (x1^x2));
Diag = (x2^x3) / (x1 * (x2^x3));
Diag = (x3^x1) / (x2 * (x3^x1));
Diag = x1^x2;
Diag = ((x1>>1)&(x2>>2)) - ((x1>>2)&(x2>>1));
Diag = ((x1>>1)&(x2>>2)) - ((x1<<1)&(x2<<2));
}
I hope you have fun with it!
Writer, designer, inventor, musician, observer, and critic with many years of experience in each of these areas. I can do without the approval of "experts" because I believe candid statements and penetrating analysis bring greater rewards than a "pat on the back". And if I have something to say when you're not listening, I tell someone else about it.