I am writing an app that requires the calculation of the Gamma function.
A code (part of a class) snippet is below:
namespace PB.Utilities.Math
{
// class definition
public class SpecialFunctions
{
// Private Fields
// Instance Constructor
public SpecialFunctions() {}
// Public Method for Gamma Function
// x = input value; x MUST BE > 0
// GammaLn = secondary output value equal to natural log of Gamma Function
public double Gamma(double x, out double GammaLn)
{
try
{
if (x <= 0) throw new System.ArgumentException("arg <= 0 in GammaFunction", "x");
}
catch
{
System.Console.WriteLine("argument <= 0 in GammaFunction");
System.Console.ReadKey();
}
double gammaln;
double _gamma = gamma(x, out gammaln);
GammaLn = gammaln;
return _gamma;
}
// private method for Gamma Function
private double gamma(double xx, out double gammaln)
{
// private constants
int j;
double x,tmp,y,ser;
const double k1 = 5.24218750000000000;
const double k2 = 0.999999999999997092;
const double k3 = 2.5066282746310005;
double[] cof = new double[14]
{
57.1562356658629235, -59.5979603554754912, 14.1360979747417471,
-0.491913816097620199, 0.339946499848118887e-4, 0.465236289270485756e-4,
-0.983744753048795646e-4, 0.158088703224912494e-3, -0.210264441724104883e-3,
0.217439618115212643e-3, -0.164318106536763890e-3, 0.844182239838527433e-4,
-0.261908384015814087e-4, 0.368991826595316234e-5
};
y = x = xx;
tmp = x + k1;
tmp = (x + 0.5) * System.Math.Log(tmp) - tmp;
ser = k2;
for (j = 0; j < 14; j++) ser += cof[j]/++y;
gammaln = tmp + System.Math.Log(k3*ser/x);
return System.Math.Exp(gammaln);
}
}
}
public class BSA
{
static void Main()
{
// Create an object of type PB.Utilities.Math.SpecialFunctions
PB.Utilities.Math.SpecialFunctions Function = new PB.Utilities.Math.SpecialFunctions();
// Call the public method GammaFunction.
double GammaLn1;
double GammaLn2;
double GammaLn3;
double g1 = Function.Gamma(3.5, out GammaLn1);
double g2 = Function.Gamma(1.5, out GammaLn2);
double g3 = Function.Gamma(1/7, out GammaLn3);
System.Console.WriteLine("g(7/2) = "+g1);
System.Console.WriteLine("g(3/2) = "+g2);
System.Console.WriteLine("g(1/7) = "+g3);
}
}
The issue is that at compilation, the parameter x in Gamma (even though x is being assigned the value 3.5 in the calling component) is assigned a value of 0 which triggers the exception. Can anyone please suggest how I can get around this? Thank you.
Seems to be 3.5 in my test cases. Are you sure you haven't excluded some information that might be the issue?
using System;
namespace Doubletesting
{
class Program
{
static void Main(string[] args)
{
double d = Doubletesting.TestDouble(3.5);
Console.WriteLine(d.ToString());
Console.ReadKey();
}
public static double TestDouble(double x)
{
double result;
result = x;
return result;
}
}
}
Result
3.5
UPDATED
The Error is caused by your Function.Gamma(1 / 7, out GammaLn3). This is because both 1 and 7 are INT and dividing (int)1 by (int)7 is zero. Try Function.Gamma(1f / 7f, out GammaLn3).
Related
to start with I made a simple code that checks if 2 lines collide based on 4 points that are given by their x and y coordinates. It checks if the angle (variable k in my code) of both lines is the same, in that case they are parallel, otherwise they collide. The angle (k) was calculated based on the math equasion Click here [k = (y2-y1)/(x2-x1)]. Now I am not sure how to get the point they are colliding in. I would appreciate it very much if you could help me. Thank you in advance.
My Code: (method I call to calculate angle)
static void MetodaTrazenjaPresjeka(Duzina d1, Duzina d2)
{
int k11 = d1.Krajy - d1.Pocy; //y2-y1 - first line
int k12 = d1.Krajx - d1.Pocx; //x2-x1 - first line
double k1 = (double)k11 / k12; //angle of the first line
int k21 = d2.Krajy - d2.Pocy; //y2-y1 - second line
int k22 = d2.Krajx - d2.Pocx; //x2-x1 - second line
double k2 = (double)k21 / k22; //angle of the second line
if (k1 == k2)
{
//they are parallel
Console.WriteLine("MOJA METODA:");
Console.WriteLine("-----------------------------------");
Console.Write("Pravci zadani tockama su paralelni!");
}
else
{
//lines are colliding
Console.WriteLine("MOJA METODA:");
Console.WriteLine("-----------------------------------");
Console.Write("Pravci zadani tockama se sijeku!");
}
}
Code in class Duzina:
class Duzina
{
private int pocx, pocy, krajx, krajy;
//read/write attribute for the x coordinate of the first point
public int Pocx
{
get { return pocx; }
set { pocx = value; }
}
//read/write attribute for the y coordinate of the first point
public int Pocy
{
get { return pocy; }
set { pocy = value; }
}
//read/write attribute for the x coordinate of the second point
public int Krajx
{
get { return krajx; }
set { krajx = value; }
}
//read/write attribute for the y coordinate of the second point
public int Krajy
{
get { return krajy; }
set { krajy = value; }
}
//method that will print out coordinates of the given points
public void Ispis()
{
Console.Write("Pocetna tocka: ({0},{1})",Pocx,Pocy);
Console.Write("Krajnja tocka: ({0},{1})", Krajx, Krajy);
}
}
line equation: y = m * x + b
1) m_d1 = (d1.y2 - d1.y1) / (d1.x2 - d1.x1)
b_d1 = d1.y1 - m_d1 * d1.x1
(same for m_d2 and b_d2)
2) intersect.y = m_d1 * intersect.x + b_d1
intersect.y = m_d2 * intersect.x + b_d2
3) m_d1 * intersect.x + b_d1 = m_d2 * intersect.x + b_d2
4) intersect.x = (b_d2 - b_d1) / (m_d1 - m_d2)
now plug intersect.x that was obtained from 4) back into either equation in 2) to get intersection.y
are there any way to get the values from Parameters in "functionone" and calculate it in the "functiontwo" without writing that again that's a small code for example what i mean
public void functionone(int x, int y)
{
x = 1;
y = 2;
}
public void functiontwo(int a , int b )
{
a=x+y;
b=x-y;
Console.WriteLine(a);
Console.WriteLine(b);
}
You are implementing functionone wrongly I guess
doing this:
public void functionone(int x, int y)
{
x = 1;
y = 2;
}
is normally not the way to pass parameters and change its values in the method,
or saying in another way, x and y should be holding the values you pass as parameters, and no getting assigned inside the method..
define a global x and global y, then you can access to it everywhere in that scope..
Example:
class Abc{
int globalX;
int globalY;
....
public void functionone(int x, int y)
{
globalX = 1 + x;
globalY = 2 + y;
}
public void functiontwo(int a , int b )
{
a=globalX + globalY;
b=globalX - globalY;
Console.WriteLine(a);
Console.WriteLine(b);
}
}
To explain my comment:
int globalX;
int globalY;
public void functionone(ref int x, ref int y)
{
x = 1;
y = 2;
}
public void functiontwo(ref int a , ref int b)
{
a = globalX + globalY;
b = globalX - globalY;
Console.WriteLine(a);
Console.WriteLine(b);
}
// in main
functionone(ref globalX, ref globalY);
// globalX and globalY are now 1 and 2
functiontwo(ref a, ref b);
// a = 3 and b = -1 -> 'globalX +/- globalY'
This way you can set the values of any variables you pass to functionone or functiontwo.
However it doesn't look good and in my opinion it's not a good code. Your concept seems wrong, so maybe you can post a description of the problem you encountered?
I have the following code in C#:
public class ELL
{
public struct RVector
{
private int ndim;
private double[] vector;
public RVector(double[] vector) => (ndim, this.vector) = (vector.Length, vector);
public double this[int i] { get => vector[i]; set => vector[i] = value; }
public override string ToString()
{
string str = "(";
for (int i = 0; i < ndim - 1; i++)
str += vector[i].ToString() + ", ";
str += vector[ndim - 1].ToString() + ")";
return str;
}
}
private static void SwapVectorEntries(RVector b, int m, int n)
{
double temp = b[m];
b[m] = b[n];
b[n] = temp;
}
public static void M(string[] args)
{
var a = new double[4] { 1, 2, 3, 4 };
var b = new RVector(a);
Console.WriteLine(b);
SwapVectorEntries(b, 1, 2); // Why after this command, b will be changed?
Console.WriteLine(b);
}
}
In this program, i creates a struct RVector. After that, i use a method SwapVectorEntries which have a struct parameter. Because, Struct is a value type, so i think the method SwapVectorEntries will not change the struct parameter. But, in the program, after the command SwapVectorEntries(b, 1, 2);, b has changed. Please explain me about this. Thank you !
Problem is in this.You have an array wich is reference type.When you create your
double[] a = new double[4] { 1, 2, 3, 4 };
RVector b = new RVector(a);
you have two references to that array.After when you pass your object into the method,
SwapVectorEntries(b, 1, 2);
your object is copied,BUT your new object have the same reference to that array.Here your have only one array and many references to it.
B itself is not passed as a reference, but the copy of b has a Reference to the same double[].
I'm having trouble implementing the lm optimizer in the alglib library. I'm not sure why the parameters are hardly changing at all while still receiving an exit code of 4. I have been unable to determine what i am doing wrong with the documentation for alglib. Below is the full source I am running:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
namespace FBkineticsFitter
{
class Program
{
public static int Main(string[] args)
{
/*
* This code finds the parameters ka, kd, and Bmax from the minimization of the residuals using "V" mode of the Levenberg-Marquardt optimizer (alglib library).
* This optimizer is used because the equation is non-linear and this particular version of the optimizer does not require the ab inito calculation of partial
* derivatives, a jacobian matrix, or other parameter-space definitions, so it's implementation is simple.
*
* The equations being solved represent a model of a protein-protein interaction where protein in solution is interacting with immobilized protein on a sensor
* in a 1:1 stoichiometery. Mass transport limit is not taken into account. The detials of this equation are described in:
* R.B.M. Schasfoort and Anna J. Tudos Handbook of Surface Plasmon Resonance, 2008, Chapter 5, ISBN: 978-0-85404-267-8
*
* Y=((ka*Cpro*Bmax)/(ka*Cpro+kd))*(1-exp(-1*X*(ka*Cpro+kd))) ; this equation describes the association phase
*
* Y=Req*exp(-1*X*kd) ; this equation describes the dissociation phase
*
* The data are fit globally such that Bmax and Req parameters are linked and kd parameters are linked during simultaneous optimization for the most robust fit
*
* Y= signal
* X= time
* ka= association constant
* kd= dissociation constant
* Bmax= maximum binding capacity at equilibrium
* Req=(Cpro/(Cpro+kobs))*Bmax :. in this case Req=Bmax because Cpro=0 during the dissociation step
* Cpro= concentration of protein in solution
*
* additional calculations:
* kobs=ka*Cpro
* kD=kd/ka
*/
GetRawDataXY(#"C:\Results.txt");
double epsg = .0000001;
double epsf = 0;
double epsx = 0;
int maxits = 0;
alglib.minlmstate state;
alglib.minlmreport rep;
alglib.minlmcreatev(2, GlobalVariables.param, 0.0001, out state);
alglib.minlmsetcond(state, epsg, epsf, epsx, maxits);
alglib.minlmoptimize(state, Calc_residuals, null, null);
alglib.minlmresults(state, out GlobalVariables.param, out rep);
System.Console.WriteLine("{0}", rep.terminationtype); ////1=relative function improvement is no more than EpsF. 2=relative step is no more than EpsX. 4=gradient norm is no more than EpsG. 5=MaxIts steps was taken. 7=stopping conditions are too stringent,further improvement is impossible, we return best X found so far. 8= terminated by user
System.Console.WriteLine("{0}", alglib.ap.format(GlobalVariables.param, 20));
System.Console.ReadLine();
return 0;
}
public static void Calc_residuals(double[] param, double[] fi, object obj)
{
/*calculate the difference of the model and the raw data at each X (I.E. residuals)
* the sum of the square of the residuals is returned to the optimized function to be minimized*/
fi[0] = 0;
fi[1] = 0;
for (int i = 0; i < GlobalVariables.rawXYdata[0].Count();i++ )
{
if (GlobalVariables.rawXYdata[1][i] <= GlobalVariables.breakpoint)
{
fi[0] += System.Math.Pow((kaEQN(GlobalVariables.rawXYdata[0][i]) - GlobalVariables.rawXYdata[1][i]), 2);
}
else
{
fi[1] += System.Math.Pow((kdEQN(GlobalVariables.rawXYdata[0][i]) - GlobalVariables.rawXYdata[1][i]), 2);
}
}
}
public static double kdEQN(double x)
{
/*Calculate kd Y value based on the incremented parameters*/
return GlobalVariables.param[2] * Math.Exp(-1 * x * GlobalVariables.param[1]);
}
public static double kaEQN(double x)
{
/*Calculate ka Y value based on the incremented parameters*/
return ((GlobalVariables.param[0] * GlobalVariables.Cpro * GlobalVariables.param[2]) / (GlobalVariables.param[0] * GlobalVariables.Cpro + GlobalVariables.param[1])) * (1 - Math.Exp(-1 * x * (GlobalVariables.param[0] * GlobalVariables.Cpro + GlobalVariables.param[1])));
}
public static void GetRawDataXY(string filename)
{
/*Read in Raw data From tab delim txt*/
string[] elements = { "x", "y" };
int count = 0;
GlobalVariables.rawXYdata[0] = new double[1798];
GlobalVariables.rawXYdata[1] = new double[1798];
using (StreamReader sr = new StreamReader(filename))
{
while (sr.Peek() >= 0)
{
elements = sr.ReadLine().Split('\t');
GlobalVariables.rawXYdata[0][count] = Convert.ToDouble(elements[0]);
GlobalVariables.rawXYdata[1][count] = Convert.ToDouble(elements[1]);
count++;
}
}
}
public class GlobalVariables
{
public static double[] param = new double[] { 1, .02, 0.13 }; ////ka,kd,Bmax these are initial guesses for the algorithm
public static double[][] rawXYdata = new double[2][];
public static double Cpro = 100E-9;
public static double kD = 0;
public static double breakpoint = 180;
}
}
}
According to Sergey Bochkanova The issue is the following:
"You should use param[] array which is provided to you by optimizer. It creates its internal copy of your param, and updates this copy - not your param array.
From the optimizer point of view, it has function which never changes when it changes its internal copy of param. So, it terminates right after first iteration."
Here is the updated and working example code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Threading.Tasks;
namespace FBkineticsFitter
{
class Program
{
public static int Main(string[] args)
{
/*
* This code finds the parameters ka, kd, and Bmax from the minimization of the residuals using "V" mode of the Levenberg-Marquardt optimizer (alglib library).
* This optimizer is used because the equation is non-linear and this particular version of the optimizer does not require the ab inito calculation of partial
* derivatives, a jacobian matrix, or other parameter-space definitions, so it's implementation is simple.
*
* The equations being solved represent a model of a protein-protein interaction where protein in solution is interacting with immobilized protein on a sensor
* in a 1:1 stoichiometery. Mass transport limit is not taken into account. The detials of this equation are described in:
* R.B.M. Schasfoort and Anna J. Tudos Handbook of Surface Plasmon Resonance, 2008, Chapter 5, ISBN: 978-0-85404-267-8
*
* Y=((Cpro*Rmax)/(Cpro+kd))*(1-exp(-1*X*(ka*Cpro+kd))) ; this equation describes the association phase
*
* Y=Req*exp(-1*X*kd)+NS ; this equation describes the dissociation phase
*
* According to ForteBio's Application Notes #14 the amplitudes of the data can be correctly accounted for by modifying the above equations as follows:
*
* Y=(Rmax*(1/(1+(kd/(ka*Cpro))))*(1-exp(((-1*Cpro)+kd)*X)) ; this equation describes the association phase
*
* Y=Y0*(exp(-1*kd*(X-X0))) ; this equation describes the dissociation phase
*
*
*
* The data are fit simultaneously such that all fitting parameters are linked during optimization for the most robust fit
*
* Y= signal
* X= time
* ka= association constant [fitting parameter 0]
* kd= dissociation constant [fitting parameter 1]
* Rmax= maximum binding capacity at equilibrium [fitting parameter 2]
* KD=kd/ka
* kobs=ka*Cpro+kd
* Req=(Cpro/(Cpro+KD))*Rmax
* Cpro= concentration of protein in solution
* NS= non-specific binding at time=infinity (constant correction for end point of fit) [this is taken into account in the amplitude corrected formula: Y0=Ylast]
* Y0= the initial value of Y for the first point of the dissociation curve (I.E. the last point of the association phase)
* X0= the initial value of X for the first point of the dissociation phase
*
*/
GetRawDataXY(#"C:\Results.txt");
double epsg = .00001;
double epsf = 0;
double epsx = 0;
int maxits = 10000;
alglib.minlmstate state;
alglib.minlmreport rep;
double[] param = new double[] { 1000000, .0100, 0.20};////ka,kd,Rmax these are initial guesses for the algorithm and should be mid range for the expected data., The last parameter Rmax should be guessed as the maximum Y-value of Ka
double[] scaling= new double[] { 1E6,1,1};
alglib.minlmcreatev(2, param, 0.001, out state);
alglib.minlmsetcond(state, epsg, epsf, epsx, maxits);
alglib.minlmsetgradientcheck(state, 1);
alglib.minlmsetscale(state, scaling);
alglib.minlmoptimize(state, Calc_residuals, null, V.rawXYdata);
alglib.minlmresults(state, out param, out rep);
System.Console.WriteLine("{0}", rep.terminationtype); ////1=relative function improvement is no more than EpsF. 2=relative step is no more than EpsX. 4=gradient norm is no more than EpsG. 5=MaxIts steps was taken. 7=stopping conditions are too stringent,further improvement is impossible, we return best X found so far. 8= terminated by user
System.Console.WriteLine("{0}", alglib.ap.format(param, 25));
System.Console.ReadLine();
return 0;
}
public static void Calc_residuals(double[] param, double[] fi, object obj)
{
/*calculate the difference of the model and the raw data at each X (I.E. residuals)
* the sum of the square of the residuals is returned to the optimized function to be minimized*/
CalcVariables(param);
fi[0] = 0;
fi[1] = 0;
for (int i = 0; i < V.rawXYdata[0].Count(); i++)
{
if (V.rawXYdata[0][i] <= V.breakpoint)
{
fi[0] += System.Math.Pow((kaEQN(V.rawXYdata[0][i], param) - V.rawXYdata[1][i]), 2);
}
else
{
if (!V.breakpointreached)
{
V.breakpointreached = true;
V.X_0 = V.rawXYdata[0][i];
V.Y_0 = V.rawXYdata[1][i];
}
fi[1] += System.Math.Pow((kdEQN(V.rawXYdata[0][i], param) - V.rawXYdata[1][i]), 2);
}
}
if (param[0] <= 0 || param[1] <=0 || param[2] <= 0)////Exponentiates the error if the parameters go negative to favor positive non-zero values
{
fi[0] = Math.Pow(fi[0], 2);
fi[1] = Math.Pow(fi[1], 2);
}
System.Console.WriteLine("{0}"+" "+V.Cpro+" -->"+fi[0], alglib.ap.format(param, 5));
Console.WriteLine((kdEQN(V.rawXYdata[0][114], param)));
}
public static double kdEQN(double X, double[] param)
{
/*Calculate kd Y value based on the incremented parameters*/
return (V.Rmax * (1 / (1 + (V.kd / (V.ka * V.Cpro)))) * (1 - Math.Exp((-1 * V.ka * V.Cpro) * V.X_0))) * Math.Exp((-1 * V.kd) * (X - V.X_0));
}
public static double kaEQN(double X, double[] param)
{
/*Calculate ka Y value based on the incremented parameters*/
return ((V.Cpro * V.Rmax) / (V.Cpro + V.kd)) * (1 - Math.Exp(-1 * X * ((V.ka * V.Cpro) + V.kd)));
}
public static void GetRawDataXY(string filename)
{
/*Read in Raw data From tab delim txt*/
string[] elements = { "x", "y" };
int count = 0;
V.rawXYdata[0] = new double[226];
V.rawXYdata[1] = new double[226];
using (StreamReader sr = new StreamReader(filename))
{
while (sr.Peek() >= 0)
{
elements = sr.ReadLine().Split('\t');
V.rawXYdata[0][count] = Convert.ToDouble(elements[0]);
V.rawXYdata[1][count] = Convert.ToDouble(elements[1]);
count++;
}
}
}
public class V
{
/*Global Variables*/
public static double[][] rawXYdata = new double[2][];
public static double Cpro = 100E-9;
public static bool breakpointreached = false;
public static double X_0 = 0;
public static double Y_0 = 0;
public static double ka = 0;
public static double kd = 0;
public static double Rmax = 0;
public static double KD = 0;
public static double Kobs = 0;
public static double Req = 0;
public static double breakpoint = 180;
}
public static void CalcVariables(double[] param)
{
V.ka = param[0];
V.kd = param[1];
V.Rmax = param[2];
V.KD = param[1] / param[0];
V.Kobs = param[0] * V.Cpro + param[1];
V.Req = (V.Cpro / (V.Cpro + param[0] * V.Cpro + param[1])) * param[2];
}
}
}
I have a C# code which is working good when the "optimize code" option is off, but fails otherwise. Is there any function or class attribute which can prevent the optimisation of a function or class, but let the compiler optimize the others ?
(I tried unsafe or MethodImpl, but without success)
Thanks
Edit :
I have done some more test...
The code is like this :
double arg = (Math.PI / 2d - Math.Atan2(a, d));
With a = 1 and d = 0, arg should be 0.
Thid code is a function which is called by Excel via ExcelDNA.
Calling an identical code from an optimized console app : OK
Calling this code from Excel without optimization : OK
Calling this code from Excel with optimization : Not OK, arg == 0 is false (instead arg is a very small value near 0, but not 0)
Same result with [MethodImpl(MethodImplOptions.NoOptimization)] before the called function.
This is very likely to do with the floating point mode which Excel likely has set - meaning that your program is calculating floating points slightly different because of the program (Excel) hosting your assembly (DLL). This might impact how your results are calculated, or how/what values are automatically coerced to zero.
To be absolutely sure you are not going to run into issues with different floating point modes and/or errors you should check for equality rather by checking if the values are very close together. This is not really a hack.
public class AlmostDoubleComparer : IComparer<double>
{
public static readonly AlmostDoubleComparer Default = new AlmostDoubleComparer();
public const double Epsilon = double.Epsilon * 64d; // 0.{322 zeroes}316
public static bool IsZero(double x)
{
return Compare(x, 0) == 0;
}
public static int Compare(double x, double y)
{
// Very important that cmp(x, y) == cmp(y, x)
if (Double.IsNaN(x) || Double.IsNaN(y))
return 1;
if (Double.IsInfinity(x) || Double.IsInfinity(y))
return 1;
var absX = Math.Abs(x);
var absY = Math.Abs(y);
var diff = absX > absY ? absX - absY : absY - absX;
if (diff < Epsilon)
return 0;
if (x < y)
return -1;
else
return 1;
}
int IComparer<double>.Compare(double x, double y)
{
return Compare(x, y);
}
}
// E.g.
double arg = (Math.PI / 2d - Math.Atan2(a, d));
if (AlmostDoubleComparer.IsZero(arg))
// Regard it as zero.
I also ported the re-interpret integer comparison, in case you find that more suitable (it deals with larger values more consistently).
public class AlmostDoubleComparer : IComparer<double>
{
public static readonly AlmostDoubleComparer Default = new AlmostDoubleComparer();
public const double MaxUnitsInTheLastPlace = 3;
public static bool IsZero(double x)
{
return Compare(x, 0) == 0;
}
public static int Compare(double x, double y)
{
// Very important that cmp(x, y) == cmp(y, x)
if (Double.IsNaN(x) || Double.IsNaN(y))
return 1;
if (Double.IsInfinity(x) || Double.IsInfinity(y))
return 1;
var ix = DoubleInt64.Reinterpret(x);
var iy = DoubleInt64.Reinterpret(y);
var diff = Math.Abs(ix - iy);
if (diff < MaxUnitsInTheLastPlace)
return 0;
if (ix < iy)
return -1;
else
return 1;
}
int IComparer<double>.Compare(double x, double y)
{
return Compare(x, y);
}
}
[StructLayout(LayoutKind.Explicit)]
public struct DoubleInt64
{
[FieldOffset(0)]
private double _double;
[FieldOffset(0)]
private long _int64;
private DoubleInt64(long value)
{
_double = 0d;
_int64 = value;
}
private DoubleInt64(double value)
{
_int64 = 0;
_double = value;
}
public static double Reinterpret(long value)
{
return new DoubleInt64(value)._double;
}
public static long Reinterpret(double value)
{
return new DoubleInt64(value)._int64;
}
}
Alternatively you could try and NGen the assembly and see if you can work around the either the mode Excel has, or how it is hosting the CLR.
That is what you get when working with floating point datatypes. You don't get exactly 0, but a very close value, since a double has limited precision and not every value can be represented and sometimes those tiny precision errors add up. You either need to expect that (check that the value is close enough to 0).