How can I do simple math with a generic INumber in .NET 7 / C#11?

445 Views Asked by At

I like to program a histogram function using a class with a generic data type bound to an INumber in .NET 7/C# 11 as

public class Histogram<TValue> where TValue : INumber<TValue>

public List<KeyValuePair<int,  int>> CalcHistogram(List<TValue> Values, TValue CorrectValue, TValue BinSize)
{
        // ...

        foreach (TValue value in Values)
        {
                // version 1
                // calc bin number as TValue type
                TValue bn = (value - CorrectValue) / BinSize;

                // round to nearest int without Math.Round
                int binNumber = bn < 0 ? (int)(bn - 0.5) : (int)(bn + (TValue)0.5);

                // version 2
                // calc bin number
                int binNumber2  = (int)Math.Round((value - CorrectValue) / BinSize, MidpointRounding.AwayFromZero);

                // ....

But I get multiple errors I don't understand:

  1. bn < 0 results in a CS0019 error (Operator '<' cannot be applied to operands of type 'TValue' and 'int'), although I thought that IComparable should do that. Casting 0 to TValue does not work as in 3.

  2. bn - 0.5 results in a CS0019 error (Operator '-' cannot be applied to operands of type 'TValue' and 'double'), although I thought I could subtract two "numbers".

  3. (TValue)0.5 results in a CS0030 error (Cannot convert type 'double' to 'TValue'), although I thought that casting a double to a "number" should be possible, after all, double implements INumber.

  4. (binNumber * BinSize) results in a CS0019 error (Operator '*' cannot be applied to operands of type 'int' and 'TValue').

  5. (int)Math.Round((value - CorrectValue) / BinSize, MidpointRounding.AwayFromZero) results in CS1503 (Argument 1: cannot convert from TValue to decimal) and CS1503 (Argument 2: cannot convert from System.MidpointRouting to int).

Can't I use the generic INumber as a "number" and do whatever I could do with a float or a double like casting, arithmetic operations +-*/, comparing?

1

There are 1 best solutions below

2
Guru Stron On BEST ANSWER

Generic math allows to operate on numbers of the same type (check out the interfaces, they are usually defined using curiously recurring template pattern like INumber<TSelf> : IComparable<TSelf>), so you need to match types for operations.

bn < 0 results in a CS0019 error (Operator '<' cannot be applied to operands of type 'TValue' and 'int'), although I thought that IComparable should do that

This can be bn < TValue.Zero

bn - 0.5 results in a CS0019 error (Operator '-' cannot be applied to operands of type 'TValue' and 'double'), although I thought I could subtract two "numbers".

This one - bn - TValue.CreateChecked(0.5) (or other INumber<TSelf>.CreateX methods)

(TValue)0.5

TValue.CreateChecked again

(binNumber * BinSize)

(TValue.CreateChecked(binNumber) * BinSize)

(int)Math.Round((value - CorrectValue) / BinSize, MidpointRounding.AwayFromZero)

rounding is defined for IFloatingPoint<TSelf> - IFloatingPoint<TSelf>.Round

And "cast" from the TValue to int can be done like this:

int.CreateChecked(binNumber)

Note that CreateChecked will throw in case of overflow.