-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Basic number interfaces #50647
Comments
Tagging subscribers to this area: @tannergooding, @pgovind Issue DetailsBackground and MotivationThere are lots of type about number in .NET so it is hardly to introduce a way to pass these types in our codes except implement lots of overload methods separately for them. public static class A
{
public static bool IsPositiveNumber(uint i) => i > 0;
public static bool IsPositiveNumber(int i) => i > 0;
public static bool IsPositiveNumber(long i) => i > 0;
public static bool IsPositiveNumber(double i) => i > 0;
// And for other many many types...
} In fact, these types are all about number: Proposed APIAdd interfaces for integer and number. namespace System.Numerics
{
public interface INumber
{
bool IsFixedLength { get; }
int BitLength { get; }
bool IsComplex { get; }
}
public interface IRealNumber : INumber
{
bool IsInteger { get; } // e.g. (1.0).IsInteger == true
bool IsFloatingNumber { get; }
IInteger IntegerPart { get; } // e.g. (1.0).IntegerPart => 1
// bool IsComplex => false;
}
public interface IInteger : IRealNumber
{
BigInteger ToBigInteger();
long ToInt64();
int ToInt32();
bool TryToInt64(out long result);
bool TryToInt32(out int result);
// bool IsInteger => true;
// IInteger IntegerPart => this;
}
public interface IFloatingNumber : IRealNumber
{
IFloatingNumber FractionalPart { get; }
double ToDouble();
bool TryToDouble(out double result);
}
} So we can update the current types to implement these interfaces. namespace System
{
- public struct Int32 : IComparable, IComparable<int>, IConvertible, IEquatable<int>, IFormattable { }
+ public struct Int32 : IInteger, IComparable, IComparable<int>, IConvertible, IEquatable<int>, IFormattable { }
- public struct Int64 : IComparable, IComparable<long>, IConvertible, IEquatable<long>, IFormattable { }
+ public struct Int64 : IInteger, IComparable, IComparable<long>, IConvertible, IEquatable<long>, IFormattable { }
- public struct Double : IComparable, IComparable<double>, IConvertible, IEquatable<double>, IFormattable { }
+ public struct Double : IFloatingNumber, IComparable, IComparable<double>, IConvertible, IEquatable<double>, IFormattable { }
// ...
}
namespace System.Numerics
{
- public struct BigInteger : IComparable, IComparable<BigInteger>, IEquatable<BigInteger>, IFormattable { }
+ public struct BigInteger : IInteger, IComparable, IComparable<BigInteger>, IEquatable<BigInteger>, IFormattable { }
- public struct Complex : IComparable, IComparable<Complex>, IEquatable<Complex>, IFormattable { }
+ public struct Complex : INumber, IComparable, IComparable<Complex>, IEquatable<Complex>, IFormattable { }
} For example, following is the implementation of public struct Int32 : IInteger, IComparable, IComparable<int>, IConvertible, IEquatable<int>, IFormattable
{
bool IInteger.IsFixedLength => true;
int IInteger.BitLength => 32;
bool IInteger.IsComplex => false;
BigInteger IInteger.ToBigInteger() => new BigInteger(this);
long IInteger.ToInt64() => this;
int IInteger.ToInt32() => this;
bool IInteger.TryToInt64(out long result)
{
result = this;
return true;
}
bool IInteger.TryToInt32(out int result)
{
result = this;
return true;
}
bool IsInteger => true;
IInteger IntegerPart => this;
// Other fields and methods already existed.
} Usage ExamplesSo the example about can be like here. It's quite simple and let's say goodbye to those overloads. public static class A
{
public static bool IsPositiveNumber(IRealNumber num)
{
var i = num.IntegerPart;
i.TryToInt64(out var l) ? l > 0 : i.ToBigInteger() > 0;
}
}
|
IIRC the runtime team attempted to add an The C# language team is actively researching language (and runtime) changes that would enable describing generic constraints over numeric operations in a way that would enable these kinds of scenarios, plus arithmetic operations and a lot more, as zero-cost abstractions. Check out: dotnet/csharplang#164 and dotnet/csharplang#1711. |
(Also, especially relevant since it's slated for C# vNext: dotnet/csharplang#4436) |
Thanks for the suggestion. We won't add this proposal as currently outlined, but @tannergooding has been investigating a set of numerical interfaces as part of dotnet/csharplang#4436. |
Both issues only improves C#. What about other langauges in .NET? Using interfaces may not be the most performant solution but it will "just work" on all languages inside .NET, such as F# and VB.
What kind of overhead? If you use generics for passing those values (which you will of course do to avoid boxing the interface), the JITter already creates specialized versions of code for each For example look at: In this case the abstraction is zero-cost. Not sure if with more complex methods the JITter will also inline them, but for simple operations as math ones or the proposed by |
Any runtime work to support the language feature would theoretically benefit them all, although some of it may be tied to accompanying language features that each language would need to support. For example the runtime and language are both exploring virtual static methods now which would benefit operators.
I think part of the problem is that you end up having to walk a very fine line to avoid boxing, virtual calls and the like. I personally don't know the full backstory other than that the runtime team got close enough that it's still commented out in the reference source, but they pulled the plug on it. |
The features describe general improvements to the .NET ecosystem so that an interface such as the following can be defined: public interface IAddable<TSelf, TOther>
where TSelf : IAddable<TSelf, TOther>
{
static abstract TSelf Zero { get; }
static abstract TSelf operator +(TSelf lhs, TOther rhs);
} This would then allow types to implement it as the following: public struct Int32 : IAddable<int, int>
{
public static override int Zero => 0;
public static override int operator +(int lhs, int rhs)
{
return lhs + rhs;
}
} You could then have a method that uses this such as: public static class GenericMath
{
public T Sum<T>(T[] values)
where T : IAddable<T, T>
{
var sum = T.Zero;
for (int i = 0; i < values.Length; i++)
{
sum += values[i];
}
return sum;
}
} This new cross-cutting functionality would be available to any language that adds support for -- NOTE: This is a minimal sample to represent available functionality and is not necessarily representative of what the actual interface(s) will look like. |
|
Closing this. As mentioned above we are looking into the correct surface area to expose here as part of the 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. |
Background and Motivation
There are lots of type about number in .NET so it is hardly to introduce a way to pass these types in our codes except implement lots of overload methods separately for them.
I may code as following, for example, to implement a method to test a number is greater than 0.
In fact, these types are all about number:
Int16
,Int32
,Int64
,UInt16
,UInt32
,UInt64
,Decimal
,Single
,Double
,BigInteger
,Complex
.Proposed API
Add interfaces for integer and number.
So we can update the current types to implement these interfaces.
For example, following is the implementation of
Int32
.Usage Examples
So the example about can be like here. It's quite simple and let's say goodbye to those overloads.
The text was updated successfully, but these errors were encountered: