Reputation: 2446
The following code is very repetitive:
public static double Interpolate(double x1, double y1, double x2, double y2, double x)
{
return y1 + (x - x1) * (y2 - y1) / (x2 - x1);
}
public static decimal Interpolate(decimal x1, decimal y1, decimal x2, decimal y2, decimal x)
{
return y1 + (x - x1) * (y2 - y1) / (x2 - x1);
}
However, my attempt to use generics does not compile:
public static T Interpolate<T>(T x1, T y1, T x2, T y2, T x)
{
return y1 + (x - x1) * (y2 - y1) / (x2 - x1);
}
The error message is as follows:
Error 2 Operator '-' cannot be applied to operands of type 'T' and 'T' C:\Git...\LinearInterpolator.cs
How should I reuse my code?
Edit: fast runtime is important for this modules.
Upvotes: 9
Views: 383
Reputation: 1192
You can use IConvertible
.
But the performance difference can be considerable depending on what you need.
Compared with a method that use Single
, the difference may reach almost 50% more.
Doing 100000 iterations, with single takes 109ms and 156ms using Generic.
See this code (.Net 2):
using System;
using System.Text;
using NUnit.Framework;
namespace ProofOfConcept.GenericInterpolation
{
/// <summary>
/// Proof of concept for a generic Interpolate.
/// </summary>
[TestFixture]
public class GenericInterpolationTest
{
/// <summary>
/// Interpolate test.
/// </summary>
[Test]
public void InterpolateTest()
{
Int16 interpolInt16 = Interpolate<Int16>(2, 4, 5, 6, 7);
Int32 interpolInt32 = Interpolate<Int32>(2, 4, 5, 6, 7);
Double interpolDouble = Interpolate<Double>(2, 4, 5, 6, 7);
Decimal interpolDecimal = Interpolate<Decimal>(2, 4, 5, 6, 7);
Assert.AreEqual((Int16)interpolInt32, (Int16)interpolInt16);
Assert.AreEqual((Double)interpolDouble, (Double)interpolDecimal);
//performance test
int qtd = 100000;
DateTime beginDt = DateTime.Now;
TimeSpan totalTimeTS = TimeSpan.Zero;
for (int i = 0; i < qtd; i++)
{
interpolDouble = Interpolate(2, 4, 5, 6, 7);
}
totalTimeTS = DateTime.Now.Subtract(beginDt);
Console.WriteLine(
"Non Generic Single, Total time (ms): " +
totalTimeTS.TotalMilliseconds);
beginDt = DateTime.Now;
for (int i = 0; i < qtd; i++)
{
interpolDouble = Interpolate<Double>(2, 4, 5, 6, 7);
}
totalTimeTS = DateTime.Now.Subtract(beginDt);
Console.WriteLine(
"Generic, Total time (ms): " +
totalTimeTS.TotalMilliseconds);
}
/// <summary>
/// Interpolates the specified x1.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="x1">The x1.</param>
/// <param name="y1">The y1.</param>
/// <param name="x2">The x2.</param>
/// <param name="y2">The y2.</param>
/// <param name="x">The x.</param>
/// <returns></returns>
public static T Interpolate<T>(T x1, T y1, T x2, T y2, T x) where T : IConvertible
{
IConvertible x1C = x1 as IConvertible;
IConvertible y1C = y1 as IConvertible;
IConvertible x2C = x2 as IConvertible;
IConvertible y2C = y2 as IConvertible;
IConvertible xC = x as IConvertible;
Decimal retDec = y1C.ToDecimal(null) +
(xC.ToDecimal(null) - x1C.ToDecimal(null)) *
(y2C.ToDecimal(null) - y1C.ToDecimal(null)) /
(x2C.ToDecimal(null) - x1C.ToDecimal(null));
return (T)((IConvertible)retDec).ToType(typeof(T), null);
}
/// <summary>
/// Interpolates the specified x1.
/// </summary>
/// <param name="x1">The x1.</param>
/// <param name="y1">The y1.</param>
/// <param name="x2">The x2.</param>
/// <param name="y2">The y2.</param>
/// <param name="x">The x.</param>
/// <returns></returns>
public static Single Interpolate(Single x1, Single y1, Single x2, Single y2, Single x)
{
Single retSing = y1 + (x - x1) * (y2 - y1) / (x2 - x1);
return retSing;
}
}
}
Upvotes: 2
Reputation: 42343
You can't use operators in generics without specifying a base class constraint on the T
.
You might do something like this: Creating a Math library using Generics in C# but personally, unless you really have lots of different formulae then I don't see much point.
There's also the possibility of dynamically compiling expression trees for T
, rewriting the types between double
/decimal
from a 'model' tree generated by the C# compiler (keeping the arithmetic precedence etc)... if you're really interested in that I can post the code for such a solution (I'll need an hour or so though!). Runtime performance will be slower.
Upvotes: 3
Reputation: 86718
Whenever I need to do this, I simply resort to dynamic
. This will work just fine, but may be somewhat slower than the overloads you currently have.
public static T Interpolate<T>(T x1, T y1, T x2, T y2, T x)
{
return y1 + ((dynamic)x - x1) * ((dynamic)y2 - y1) / ((dynamic)x2 - x1);
}
Upvotes: 4
Reputation: 499002
Your current code is fine as it is - possibly the best you can achieve without casting doubles to decimals.
A generic solution is possible, but would involve quite a lot of infrastructure - so much so that it would make things way too complex.
Upvotes: 4