Skip to content

Extend System.Runtime.InteropServices.NFloat so it can replace the Xamarin in-box nfloat for .NET 7 #63801

@tannergooding

Description

@tannergooding

Rationale

In #13788 we exposed some new interchange types CLong, CULong, and NFloat to help ensure interop with underlying runtimes could be done in a stable and cross platform manner.

We initiallizy decided that these shoulld not expose more complex support such as operators, parsing, or formatting. However, this decision means that Xamarin must continue maintaining their own version of ObjCRuntime.nfloat.

As per dotnet/macios#13087 (comment), exposing this additional functionality on NFloat would allow them to remove their custom type and normalize on the "built-in" functionality instead.

Proposal

namespace System.Runtime.InteropServices;

public readonly struct NFloat
    : IComparable,
      IComparable<NFloat>,
      IConvertible,         // We've explicitly decided against exposing this in other cases
      IEquatable<NFloat>,   // Already exposed by 13788
      IFormattable
{
    // Xamarin exposes these as `static readonly` but that is "less optimizable" and not our normal convention
    public static NFloat Epsilon { get; }
    public static NFloat MaxValue { get; }
    public static NFloat MinValue { get; }
    public static NFloat NaN { get; }
    public static NFloat NegativeInfinity { get; }
    public static NFloat PositiveInfinity { get; }
    public static NFloat Size { get; }

    public static bool IsInfinity(NFloat value);
    public static bool IsNaN(NFloat value);
    public static bool IsNegativeInfinity(NFloat value);
    public static bool IsPositiveInfinity(NFloat value);

    // Parsing APIs, would match IParseable<NFloat> and IBinaryFloatingPoint<NFloat> if exposed
    public static NFloat Parse(string s);
    public static NFloat Parse(string s, NumberStyles style);
    public static NFloat Parse(string s, IFormatProvider? provider);
    public static NFloat Parse(string s, NumberStyles style, IFormatProvider? provider);
    public static bool TryParse([NotNullWhen(true)] string? s, out NFloat result);
    public static bool TryParse([NotNullWhen(true)] string? s, NumberStyles style, IFormatProvider? provider, out NFloat result);

    // Arithmetic Operators
    public static NFloat operator +(NFloat left, NFloat right);
    public static NFloat operator --(NFloat value);
    public static NFloat operator /(NFloat left, NFloat right);
    public static NFloat operator ++(NFloat value);
    public static NFloat operator %(NFloat left, NFloat right);
    public static NFloat operator *(NFloat left, NFloat right);
    public static NFloat operator -(NFloat left, NFloat right);
    public static NFloat operator +(NFloat value);
    public static NFloat operator -(NFloat value);

    // Comparison Operators
    public static bool operator ==(NFloat left, NFloat right);
    public static bool operator >(NFloat left, NFloat right);
    public static bool operator >=(NFloat left, NFloat right);
    public static bool operator !=(NFloat left, NFloat right);
    public static bool operator <(NFloat left, NFloat right);
    public static bool operator <=(NFloat left, NFloat right);

    // Explicit * to NFloat conversions
    public static explicit operator NFloat(decimal value);
    public static explicit operator NFloat(double value);
    public static explicit operator NFloat(nint value);     // Explicit is inconsistent with int and long
    public static explicit operator NFloat(nuint value);    // Explicit is inconsistent with uint and ulong, Xamarin is missing this one

    // Explicit NFloat to * conversions
    public static explicit operator byte(NFloat value);
    public static explicit operator char(NFloat value);
    public static explicit operator decimal(NFloat value);
    public static explicit operator short(NFloat value);
    public static explicit operator int(NFloat value);
    public static explicit operator long(NFloat value);
    public static explicit operator nint(NFloat value);
    public static explicit operator sbyte(NFloat value);
    public static explicit operator float(NFloat value);
    public static explicit operator ushort(NFloat value);
    public static explicit operator uint(NFloat value);
    public static explicit operator ulong(NFloat value);
    public static explicit operator nuint(NFloat value);   // Xamarin is missing this one

    // Implicit * to NFloat conversions
    public static implicit operator NFloat(byte value);
    public static implicit operator NFloat(char value);
    public static implicit operator NFloat(short value);
    public static implicit operator NFloat(int value);
    public static implicit operator NFloat(long value);
    public static implicit operator NFloat(sbyte value);
    public static implicit operator NFloat(float value);
    public static implicit operator NFloat(ushort value);
    public static implicit operator NFloat(uint value);
    public static implicit operator NFloat(ulong value);

    // Implicit NFloat to * conversions
    public static implicit operator double(NFloat value);

    // From IComparable and IComparable<NFloat>
    public bool CompareTo(NFloat other);
    public bool CompareTo(object other);

    // From IConvertible, if we decide to support
    public TypeCode GetTypeCode();
    bool IConvertible.ToBoolean(IFormatProvider? provider);
    byte IConvertible.ToByte(IFormatProvider? provider);
    char IConvertible.ToChar(IFormatProvider? provider);
    DateTime IConvertible.ToDateTime(IFormatProvider? provider);
    decimal IConvertible.ToDecimal(IFormatProvider? provider);
    double IConvertible.ToDouble(IFormatProvider? provider);
    short IConvertible.ToInt16(IFormatProvider? provider);
    int IConvertible.ToInt32(IFormatProvider? provider);
    long IConvertible.ToInt64(IFormatProvider? provider);
    sbyte IConvertible.ToSByte(IFormatProvider? provider);
    float IConvertible.ToSingle(IFormatProvider? provider);
    string IConvertible.ToString(IFormatProvider? provider);
    object IConvertible.ToType(Type conversionType, IFormatProvider? provider);
    ushort IConvertible.ToUInt16(IFormatProvider? provider);
    uint IConvertible.ToUInt32(IFormatProvider? provider);
    ulong IConvertible.ToUInt64(IFormatProvider? provider);

    // From object and IEquatable<NFloat>, already exposed
    public bool Equals(NFloat other);
    public bool Equals(object other);

    // From object and IFormattable
    public string ToString();
    public string ToString(IFormatProvider? provider);
    public string ToString(string? format);
    public string ToString(string? format, IFormatProvider? provider);
}

Additionally, it may be worth making this consistent with Half/Single/Double with regards to the generic math work that is being done. This would additionally implement IBinaryFloatingPoint<float> and IMinMaxValue<float>.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions