0

Here I have a minimal example of a class that should model a vector (to be used for linear algebra computations). It includes a type T that will be an integral or floating point type (like for instance int or double). Now I wanted to implement a method CheckIfZeroAt that checks whether a certain entry contains a zero or not. The problem is that I want to leave the type T variable, but as far as I know I have no way to tell the compiler that I T is a numeric type where the typecast is available. Unfortunately there also does not seem to be an interface for numeric types that I could restrict T to.

Is there any elegant way to solve this problem?

I included a few naive ways one could try to implement this method as comments, but none of them work.

class MyVector<T> // T is an integral or floating point type
{
    T[] vector;

    public MyVector(T[] array)
    {
        vector = array; //just a reference 
    }

    public bool CheckIfZeroAt(int i)
    {
        // return vector[0] == (T)0; //"Cast is redundant"
        // return vector[0] == 0; // Operator "==" cannot be applied to operands of type "T" and "int"
        // return vector[0] == 2 * vector[0]; // Operator "*" cannot be applied to operands of type "T" and "int"
    }

}
Sebastian D'Agostino
  • 1,575
  • 2
  • 27
  • 44
flawr
  • 10,814
  • 3
  • 41
  • 71
  • Have you thought about getting the `type` of `T` and then casting/switching/if...else based on that? – JayV Jun 09 '18 at 20:42
  • I guess you would need to have some sort of "Numeric" interface or something like that – Sebastian D'Agostino Jun 09 '18 at 20:42
  • @JayV ah right, this might work, thanks a lot! – flawr Jun 09 '18 at 20:42
  • @sebadagostino that is what I already suggested, but to my knowledge that does not exist. – flawr Jun 09 '18 at 20:43
  • Couldnt you just check if its equal to `default(T)`? – maccettura Jun 09 '18 at 20:47
  • Have you cast the vector instead?. Check this one as a workaround to limit generic type https://stackoverflow.com/a/64142/5929494 – RizkiDPrast Jun 09 '18 at 20:47
  • 5
    .NET's implementation of generics is not very suitable for this. Works okay with type erasure, the C++ or Java kind of generics, but not the reified kind that .NET uses. Lots of things you can't do generically, something as basic as adding or multiplying two values of T won't work. Testing for zero is actually possible, somewhat by accident, using default(T). It is however not efficient. You need to strongly consider specific non-generic classes, there won't be that many of them in practice. – Hans Passant Jun 09 '18 at 20:50
  • 1
    It’s not perfect, but have you considered using `IConvertible` ? – John Wu Jun 09 '18 at 21:01
  • @HansPassant What would you suggest as an alternative approach for this case, where the implementations for e.g. `double` and `int` largely share the same code? – flawr Jun 09 '18 at 21:41
  • 1
    I have to recommend that you actually finish your code. Then you'll inevitably discover what you need to do. At that point you're guaranteed to drop the word "elegant" from your question. Good thing, every .NET programmer has to discover this by himself. – Hans Passant Jun 09 '18 at 21:49
  • @HansPassant Could you elaborate why using default(T) is "not efficient"? It should be a JIT compile time constant and I would have expected the result of the JIT compilation to be optimal (a xor and a cmp). – dialer Jun 09 '18 at 22:30
  • Because it has to call Equals() and can't use the CMP instruction built into the processor. CMP takes a quarter of a processor cycle on modern processors, when paired with other instructions that can execute concurrently. Stuff you can't find out until you actually finish the code and profile it. – Hans Passant Jun 09 '18 at 22:36
  • You're right, I missed that. It won't optimize that call. In that case, if you really pursue high performance, I'd suggest the same as Hans: Finish your code and find out what you *actually* need. Then it might come down to implementing a bunch of very similar classes (maybe use T4 code generation for that)... – dialer Jun 09 '18 at 22:50

2 Answers2

2

Numerical types in .NET have a default of 0, so just check if its equal to default(T)

public bool CheckIfZeroAt(int i)
{
    return vector[i].Equals(default(T));
}

Fiddle here

As Hans points out in the comments this isn't really the best solution. Seems like you should just skip generics all together since there aren't many out of the box numeric types in .NET.

maccettura
  • 10,514
  • 3
  • 28
  • 35
1

You can use IConvertible. This allows all the numeric value types. It does also allow DateTime, string, and bit, but if someone chooses to use a MyVector<bool>, who are you to argue? It's still a number, sort of.

Note: Since floating point types may have an error, you probably want to allow a tolerance. In my example the tolerance is 0.1. (If you're OK with a tolerance of 0.5, you can just convert to an int instead of using Math.Abs).

class MyVector<T> where T : IConvertible
{
    T[] vector;

    public MyVector(T[] array)
    {
        vector = array; //just a reference 
    }

    public bool CheckIfZeroAt(int i, decimal tolerance = 0.1M)
    {
        return Math.Abs(Convert.ToDecimal(vector[i])) < tolerance;
    }

    public bool CheckIfZeroAt(int i)
    {
        return Convert.ToInt32(vector[i])) == 0;
    }

}
John Wu
  • 50,556
  • 8
  • 44
  • 80
  • Thanks, this looks like the most practical approach. In my case I was trying to implement a small linear algebra library (just as an exercise) where I'd count the numbers that are exactly zero, e.g. for deciding whether a sparse format would be more suitable. (Similar to `count_nonzero` in numpy or `nnz` in Octave/MATLAB.) – flawr Jun 09 '18 at 21:33
  • This will throw if the value is outside of the range of Int32. – dialer Jun 09 '18 at 22:24