dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
15.38k stars 4.75k forks source link

INumber<T>.Create(out bool successful), [area-System.Numerics] #81016

Open Aniobodo opened 1 year ago

Aniobodo commented 1 year ago

Please add INumber.Create(out bool successful) method that outputs a flag -true/false-, indicating if the creation was successful or not. Instead of throwing an exception like INumber.CreateChecked(), just output the flag and return a default number value. Each program can then hadle the issue locally.

INumber.Create(out bool successful)

area-System.Numerics @jeffhandley

ghost commented 1 year ago

Tagging subscribers to this area: @dotnet/area-system-buffers See info in area-owners.md if you want to be subscribed.

Issue Details
Please add INumber.Create(out bool successful) method that outputs a flag -true/false-, indicating if the creation was successful or not. Instead of throwing an exception like INumber.CreateChecked(), just output the flag and return a default number value. Each program can then hadle the issue locally. INumber.Create(out bool successful)
Author: Aniobodo
Assignees: -
Labels: `area-System.Buffers`, `untriaged`
Milestone: -
Sergio0694 commented 1 year ago

"Instead of throwing an exception like INumber.CreateChecked()"

What's wrong with INumberBase<T>.TryConvertFromChecked (docs here)? 🤔

Aniobodo commented 1 year ago

@Sergio0694 INumberBase.TryConvertFromChecked is okay but not public.

ufcpp commented 1 year ago

https://learn.microsoft.com/ja-jp/dotnet/api/system.numerics.inumberbase-1.createchecked ?

Aniobodo commented 1 year ago

@ufcpp INumberBase.TryConvertFromChecked is protected abstract. I want the access or the implementation for primitive numeric types.

ufcpp commented 1 year ago

@Aniobodo TryConvertFromChecked is called from CreateChecked.

hamarb123 commented 1 year ago

Note also TryConvertFromChecked also can throw.

Aniobodo commented 1 year ago

@ufcpp @hamarb123 The exception from INumber.CreateChecked() is exactly the limitation. I would like to get notified about the overflow, without throwing an exception.

hamarb123 commented 1 year ago

Here's some code that can do what you want (based on a random implementation of CreateChecked I found):

For some reason this is legal (it's because NumberBaseExtensions.Helper implements INumberBase`1 meaning it can access protected members on it for some reason, despite the generic parameter being wrong):

https://sharplab.io/#v2:EYLgxg9gTgpgtADwGwBYA0AXEUCuA7AHwAEAmARgFgAoIgBgAIiyA6AJXwwEsBbGZgYQjcADpwA2MKAGVJAN05gYAZwDc1OoxYA5HLygLV1dWQCcACjMsAKlACe/WAEMMMfgAsYYANYwAJgB5OPAw0ejEIPABzAD4zCBwMemAICDF6JRwwRT8/MgBKUIysmBzffLy1GlMLMgAZZht7Jxd3Tx8A8KjQ308eRzFY+MTk1PTM7J7fEgKx4tLpiuNzS1pVhrsHGGdXD28/QODQ4FsXQYSklLSiib8AZhnrksn7xap1W80kRhJ6HW5gSQAIUcShgAFEEC48EpOBElNQAN7Uego+jCfSybb0IIuKAAM0cinoAAkYGJhJJ/FYZGI8dF6CB6ABJP4AqDA0FUml05GovkAdw8sHo1LJeIZzNZQJBMC5YuivJRSKofL5RA+TC+VlYyhwYkSjU221aewCVgA8hghaFtbr9bELVbJPRMWIcDBQkMLqNHqU8orVQKhTARZahRKWbo2RzZY6hQqVYHVYLJCHbRl9RGpeyZVSdRmMAmk/RlcW+em9YlYAXKmXUb7JvQALz0DC4GCVAPFtu2LtJ0t11WccVmDC2CkQPFmOOSPLNltjidTiv6vL+xOD1EDzeB6uV5v0af5yt5OLAABWngweVd7trO5RAF8+3WyaDsSOAIQrjDrewRWRJAwAAxKAhBNdozFvD16C9PdV3oAAyRD6G/MNJD/QQ8EAqAMCsCAIL8KD+ndT1zng691wfJUXx3Bs/APAkxFBe9qNRABVaFHDxPgpC8ThhCZPBOAwOJyLta9WJ3Z8NzLGTNzAZwwDcWi+W3HcAHoNMUjBlPoFM8BFDYAKA0DwN2dpYKgIz/2woD8MI3xWzcMD+XoRxDJgBBFGELgIlUpN6KclsmJYgLA04pRuN4/jBOE0S4Ik15Nxk8LGAAdnoCipL5eTUTylEA3VT4RWPTNDWaHY2n2GcoBtMrC1HNxOCUUMnWs6CyOGS5ZhuXwqKTFNhVqrMo2lTlaqLQbg1KiTRv+cbYwaqaaNktVMtJclKRxaJMMqxyqXQurZoLWJOtg84guS/LqBkoA===

Note: the code above is probably not great due to the try/catch.

The exception from INumber.CreateChecked() is exactly the limitation. I would like to get notified about the overflow, without throwing an exception.

I was just pointing out that TryConvertFromChecked is not a solution since it can also throw. You also should try TryConvertToChecked.

I think this could be a good feature (as far as I can tell there is no way to do this currently, and it would clearly be faster if you didn't have to catch an exception), but it probably won't be very fast for any types that don't directly implement relevant new members and just rely on whatever default interface implementation we would have to add.

ghost commented 1 year ago

Tagging subscribers to this area: @dotnet/area-system-numerics See info in area-owners.md if you want to be subscribed.

Issue Details
Please add INumber.Create(out bool successful) method that outputs a flag -true/false-, indicating if the creation was successful or not. Instead of throwing an exception like INumber.CreateChecked(), just output the flag and return a default number value. Each program can then hadle the issue locally. INumber.Create(out bool successful) [area-System.Numerics](https://github.com/dotnet/runtime/labels/area-System.Numerics) @jeffhandley
Author: Aniobodo
Assignees: -
Labels: `area-System.Numerics`, `untriaged`
Milestone: -
tannergooding commented 1 year ago

Can you elaborate on what you're trying to do and what you're going to do when the conversion fails?

We provide 3 Create APIs because there are roughly 3 types of conversions:

The internal TryConvert* APIs are protected only because they are "private impl" details of how Create* work. They are expected to have very specific contracts and be used in vary specific ways to account for any T to U conversion. This allows some conversion to be defined almost no matter how the relationship exists between the concrete T and U (same assembly, different assemblies, T depends onU,Udepends onT, etc). It also ensures some "deterministic" behavior in the case that bothTandU` define the same conversion.

Aniobodo commented 1 year ago

@tannergooding What am trying to do is: simple numeric comparision of INumbers and INumbers. The output of the comparision would be -true/false-. Because there is no (in)equality operator for INumbers and INumbers, one has to be converted to the other, to enable the use of (in)equality operators: say T1 valueOldT2 = T1.CreateFrom(T2 valueT2).

Now let's look at the available APIs

  1. T1.CreateChecked(T2 valueT2). The problem here: throwing exception is not the target here. If overflow is the reason for the exception, returning a flag -false- would be appropriate.

  2. T1.CreateSaturating(T2 valueT2) The problem here: the saturating has already changed the numeric value of T2 and no flag is returned. For example: let T1 valueT1 = int.Max and T2 valueT2 = int.Max + 0.5. typeof(T1) = Int32. Now T1 valueOldT2 = T1.CreateSaturating(T2 valueT2) will return int.Max. Then the comparision valueT1 == valueOldT2 becomes true, which is INCORRECT.

  3. T1.CreateTruncating(T2 valueT2) Same scenario with T1.CreateSaturating(T2 valueT2).

tannergooding commented 1 year ago

It would likely be better/more efficient to handle this more generically rather than trying to convert between unknown types which may allocate, perform other expensive operations, or not have the behavior you expect. -- For example, the proposed approach will also not work for say floating-point where the concept of truncation/overflow do not exist. For Single, Half, and Double there is only saturation and rounding to nearest representable results and therefore some T1: double cast to a T2: float may silently lose precision and report "equal" when they actually aren't.

IBinaryInteger<TSelf> and IFloatingPoint<TSelf> expose some instance methods you can use for this. For example, you can given two binary integers you can value1.GetShortestBitLength() op value2.GetShortestBitLength() as an early filter and only do the more expensive comparison in the case the two lengths are equal. In the case they are equal you can get the underlying bytes using [Try]WriteBigEndian or [Try]WriteLittleEndian to do the more exact comparison.

tannergooding commented 1 year ago

There are also APIs that exist on INumberBase<TSelf> that can likewise help do efficient filtering/early exit checks, such as IsNegative (noting that for INumber<TSelf> these are well defined, but for INumberBase<TSelf> which may include Complex numbers, you can get other interesting behaviors and considerations to take into account).