Skip to content

Add a special Numeric structure or INumeric Interface #25058

Closed

Description

@ghost

Intrinsic numeric types (byte, int, float, ….etc) are structures, so unfortunately, they have no base class that can gather all basic methods and operators!
So I suggest to add a special Numeric structure that can accept numeric values of any type. It should contain the value and the underling type of it, as Object class does. Numeric structure should have the basic arithmetic and logical operators. Or it can be an interace (say INumeric) that all numeric data types implement. This will not break any existing code.

Usage:
Numeric structure (or INumeric Inrerface) can be used in generic methods where T is expected to be a numeric type, to carry out the basic arithmetic and logical operators without writing a dozen if statements for each individual numeric type. Objects doesn't have the basic arithmetic and logical operators and have to be converted to numeric types to work with. For example, I tried to do this but failed:

public struct Numeric
  {
      public object Value { get; set; }

      public static Numeric operator +(Numeric n1, Numeric n2)
      {
          return (Numeric)(n1.Value + n2.Value);
      }
  }

C# refuses n1.Value + n2.Value because it doesn't know how to add objects. Many if statements are needed to deal with different types of n1 and n2, regarding the larger type to use as the output type. This is a hell of code to something that should be easy and direct if there was a general numeric type that can contain any numeric type.

I face the same situation when I use the base Enum class in any method, because Enum can have any numeric type as an underling type!

I saw similar problem in the source code of Vector, where tons of code lines are repeated just to cover all cases of each numeric type!

Note: If you add the numeric type, C# and VB.net should add a new constraint for generic parameters to be numerics.
where T: numeric
this means T can only be of type Numeric or any intrinsic numeric data types.

So, If this Numeric Type or INumeric Interface exists, one can write

public static Enum SetFlag(Enum value, Enum flag)
{
var result = (Numeric)value | (Numeric)flag);
return (Enum)result; 
}

instead of:

public static Enum SetFlags(Enum value, Enum flag)
{
   if (Enum.GetUnderlyingType(value.GetType()) == typeof(byte))
      return (Enum)(object)((byte)(object)value | (byte)(object)flag);

   if (Enum.GetUnderlyingType(value.GetType()) == typeof(int))
      return (Enum)(object)((int)(object)value | (int)(object)flag); 
   //
   //
   // etc
}

and the same when writing generic types that deal with numeric data types only.
This can be extended based on this proposal dotnet/csharplang#1233, I also suggest to add these interfaces:
numeric constraint.
IIntegral: for integer tyoes (byte, sbyte, int, uint, short, ushort, long, ulong)
IFloating: (float, double, decemal)
IValue: (numerics and dates).
ILetiral (Char and string).
IPrimitive (numerics, dates, char, string)

These Interfaces will make writting generics easier. IPerimative can help you
format or parsing or using + operation depending on where T: IPerimative.
Also it can be usefull when defining generic lists and dictionaries that can contain different values. This will make them more specialized than using objects.

Activity

  1. jnm2 commented on Feb 16, 2018

    @jnm2
    Contributor

    I'm pretty sure dotnet/csharplang#164 is going to be the answer for this.

  2. ghost changed the title [-]Add a special Numeric structure [/-] [+]Add a special Numeric structure or INumeric Interface[/+] on Feb 16, 2018
  3. yaakov-h commented on Feb 16, 2018

    @yaakov-h
    Member

    Wouldn't that be doable with just dotnet/csharplang#104?

    public static TEnum SetFlag<TEnum>(TEnum value, TEnum flag) where TEnum : Enum, struct
        => value | flag;
  4. jnm2 commented on Feb 16, 2018

    @jnm2
    Contributor

    @MohammadHamdyGhanem

    Could you explain how I do some thing like this with shapes?

    @yaakov-h's suggestion is better, but off the top of my head you'd make the method take <T> where T : SEnum and define the | operator in an SEnum shape.

  5. jnm2 commented on Feb 16, 2018

    @jnm2
    Contributor

    IIntegral, IFloating, ILiteral, IPrimitive

    As soon as you use any of these things as parameter or return types, you end up boxing all these tiny value types on the heap. Not good for performance.

    Also, DateTime and decimal are not primitives as far as the type system is concerned.

    All of that begs the question, what would you do with an instance of ILiteral or IPrimitive if you had one?

  6. jnm2 commented on Feb 16, 2018

    @jnm2
    Contributor

    But what can you do with where T : IPrimitive that you can't do without that constraint?

  7. jnm2 commented on Feb 16, 2018

    @jnm2
    Contributor

    I proposed the other as a general solution "just in case".

    "Just in case" proposals are often considered an antipattern.

  8. aaronfranke commented on Feb 28, 2018

    @aaronfranke

    I am interested in numeric constraints so that I can create math classes that can use any numeric data type. For example, to make a Vector3 that can use float, double, or decimal. With only <T> and T x, y, z; I am unable to do things like z = x + y; or if (x > y)

    dotnet/csharplang#1345

  9. eerhardt commented on Mar 15, 2018

    @eerhardt
    Member

    The original proposals of having a struct Numeric that wraps an object, or having an INumeric interface, are not feasible. This would cause boxing of every number, which will not perform satisfactorily.

    However, the issue described here is an issue that we would like to fix some point. We have the same need in the Tensor<T> type in corefxlab: https://github.com/dotnet/corefxlab/blob/master/src/System.Numerics.Tensors/System/Numerics/TensorArithmetic.tt.

    We would probably need a combination of new C# language features, and a built-in "Arthimetic" shape/type-class.

    /cc @ericstj

  10. 1 remaining item

  11. aaronfranke commented on Mar 16, 2018

    @aaronfranke

    @MohammadHamdyGhanem I think it would make more sense to call it number instead of arith

  12. khellang commented on Mar 16, 2018

    @khellang
    Member
  13. ericstj commented on Mar 16, 2018

    @ericstj
    Member

    @MohammadHamdyGhanem indeed, folks care about this problem very much. It's no coincidence that multiple language proposals show how they intend to help solve this problem. I wasn't around for the initial IArithmetic experiments but my understanding is that an interface and instance methods had both poor performance and bad semantics (because they cannot use operators).

    For operators you need static interfaces, static interfaces could also solve the performance problem since the JIT could assume an impl based on the T and specialize to optimize away the method call without actually having to box the primitive type. This was alluded to in @MattWindsor91's writeup: https://github.com/MattWindsor91/roslyn/blob/master/concepts/docs/csconcepts.md#static-interface-methods-for-the-clr

    As mentioned static interfaces require changes in both runtime and frameworks so its less than ideal for reach.

    I think the current C# proposal for type classes/shapes has much more promise. It has the potential to work with purely compiler features, though we might add some default implementations of the shapes/concepts/whatever-they-get-named in the framework itself to facilitate ease of use and type exchange.

    I'd highly recommend you look through the type class / shapes issues in CSharpLang. I believe those are good generalizations of this type of problem and have the best chance at getting a solution that can reach to the largest number of C# developers.

  14. transferred this issue fromdotnet/corefxon Jan 31, 2020
  15. added this to the 5.0 milestone on Jan 31, 2020
  16. modified the milestones: 5.0.0, Future on Jun 23, 2020
  17. tannergooding commented on Apr 22, 2021

    @tannergooding
    Member

    Closing this. As mentioned in other issues (such as #50647) we are looking into the correct surface area to expose here as part of the static abstracts in interfaces feature the language and runtime are adding.

    This is currently being tracked in our dotnet/designs repo and I've added the initial rough draft of the surface area here: dotnet/designs#205

    Further discussion or suggestions should happen there and in future PRs that further refine the proposed surface area.

  18. ghost locked as resolved and limited conversation to collaborators on May 23, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions