Skip to content

Add missing math functions defined by IEEE 754:2019 #20137

Description

@dcwuser

These are very standard methods used in numerical computing.

namespace System
{
    public static partial class Math
    {
        // (1 + x)^n
        public static double Compound(double x, double n);

        // e^x - 1
        public static double ExpM1(double x);

        // 2^x
        public static double Exp2(double x);

        // 2^x - 1
        public static double Exp2M1(double x);

        // 10^x
        public static double Exp10(double x);

        // 10^x - 1
        public static double Exp10M1(double x);

        // sqrt(x^2 + y^2)
        public static double Hypot(double x, double y);

        // log(1 + x)
        public static double LogP1(double x);

        // log2(1 + x)
        public static double Log2P1(double x);

        // log10(1 + x)
        public static double Log10P1(double x);

        // x^(1 / n)
        public static double Root(double x, double n);

        // sin(pi * x)
        public static double SinPi(double x);

        // cos(pi * x)
        public static double CosPi(double x);

        // tan(pi * x)
        public static double TanPi(double x);

        // asin(x) / pi
        public static double AsinPi(double x);

        // acos(x) / pi
        public static double AcosPi(double x);

        // atan(x) / pi
        public static double AtanPi(double x);

        // special handling
        public static double Atan2Pi(double y, double x);
    }

    public static partial class MathF
    {
        // (1 + x)^n
        public static float Compound(float x, float n);

        // e^x - 1
        public static float ExpM1(float x);

        // 2^x
        public static float Exp2(float x);

        // 2^x - 1
        public static float Exp2M1(float x);

        // 10^x
        public static float Exp10(float x);

        // 10^x - 1
        public static float Exp10M1(float x);

        // sqrt(x^2 + y^2)
        public static float Hypot(float x, float y);

        // log(1 + x)
        public static float LogP1(float x);

        // log2(1 + x)
        public static float Log2P1(float x);

        // log10(1 + x)
        public static float Log10P1(float x);

        // x^(1 / n)
        public static float Root(float x, float n);

        // sin(pi * x)
        public static float SinPi(float x);

        // cos(pi * x)
        public static float CosPi(float x);

        // tan(pi * x)
        public static float TanPi(float x);

        // asin(x) / pi
        public static float AsinPi(float x);

        // acos(x) / pi
        public static float AcosPi(float x);

        // atan(x) / pi
        public static float AtanPi(float x);

        // special handling
        public static float Atan2Pi(float y, float x);
    }
}

They appear, with exactly these names, in the Java Math class and Boost and GSL libraries, in many other mathematical libraries, and in many articles in the numerical computing literature.

Naming: The names are somewhat cryptic. .NET coding guidelines would probably favor ExpMinusOne over Expm1, etc. On the other hand, the names are quite standardized. We would need to decide whether conforming to the expectations of advanced users or giving a better hint at meaning to less advanced users is more important.

Location: The obvious place is System.Math, but: (i) this class is already quite a jumble, (ii) it mostly follows math.h of the C standard library, which does not contain these functions, and (iii) the presence of these functions might baffle naive users. One possibility would be to add a MoreMath or AdvancedMath static class in the System.Numerics namespace, which would also be a natural place for future advanced functions such as erf, Gamma, etc.

Domain and Range: The only slightly non-trivial issue here is that Log1p is not defined for x < -1. We would need to decide whether to retutrn NaN or throw ArgumentOutOfRangeException.

Implementation and Performance: Implementations are straightforward and well-established. Each can be executed over the entire range with a handfull of flops, making them about as fast as most other standard math functions.

Activity

  1. transferred this issue fromdotnet/corefxon Jan 31, 2020
  2. added this to the 5.0 milestone on Jan 31, 2020
  3. tannergooding commented on Mar 2, 2020

    @tannergooding
    Member

    @dcwuser, if you could update the proposal to include the float overloads in MathF and to rename Log1P to be LogP1, I'd be happy to take this forward.

    These functions are also defined in section 9. Recommended Operations of the IEEE 754:2019 spec (and equivalent section in the 2008 spec) so these would need to return NaN when the value is outside the allowed domain.
    I imagine we may want to take Exp2, Exp2M1, Exp10, Exp10M1, Log2P1, and Log10P1 for consideration at the same time (or possibly as part of an alternative proposal).

    It is likely worth calling out in the rationale that these functions can account for floating-point inaccuracies when performing these common operations and is one reason why they are exposed by other languages/frameworks and recommended by the IEEE 754 specification.

  4. removed
    untriagedNew issue has not been triaged by the area owner
    on Mar 2, 2020
  5. modified the milestones: 5.0.0, Future on Jun 23, 2020
  6. added
    api-ready-for-reviewAPI is ready for review, it is NOT ready for implementation
    and removed
    api-needs-workAPI needs work before it is approved, it is NOT ready for implementation
    on Jan 15, 2021
  7. bartonjs commented on Jan 15, 2021

    @bartonjs
    Member

    We'd like to finish off all of the part 9 methods in one pass (the list seems to be expm1, exp2, exp2m1, exp10, exp10m1, logp1, log2p1, log10p1, hypot, compound, rootn, sinpi, cospi, tanpi, asinpi, acospi, atanpi, atan2pi).

    @tannergooding: Please update the proposal to include all of these operations (PascalCased as appropriate) to show the signatures (including parameter names). Should be an easy re-review.

  8. added
    api-needs-workAPI needs work before it is approved, it is NOT ready for implementation
    and removed
    api-ready-for-reviewAPI is ready for review, it is NOT ready for implementation
    on Jan 15, 2021
  9. changed the title [-]Add hypot, expm1, log1p[/-] [+]Add missing math functions defined by IEEE 754:2019[/+] on Jan 15, 2021
  10. added
    api-ready-for-reviewAPI is ready for review, it is NOT ready for implementation
    and removed
    api-needs-workAPI needs work before it is approved, it is NOT ready for implementation
    on Jan 15, 2021
  11. 1 remaining item

  12. added
    api-approvedAPI was approved in API review, it can be implemented
    and removed
    api-ready-for-reviewAPI is ready for review, it is NOT ready for implementation
    on Jan 21, 2021
  13. terrajobst commented on Jan 21, 2021

    @terrajobst
    Contributor

    Video

    • Looks good as proposed, but let's also add the MaxNumber variants that we previously rejected so that we get entire spec implemented
    namespace System
    {
        public static partial class Math
        {    
            public static double AcosPi(double x);
            public static double AsinPi(double x);
            public static double Atan2Pi(double y, double x);
            public static double AtanPi(double x);
            public static double Compound(double x, double n);
            public static double CosPi(double x);
            public static double Exp10(double x);
            public static double Exp10M1(double x);
            public static double Exp2(double x);
            public static double Exp2M1(double x);
            public static double ExpM1(double x);
            public static double Hypot(double x, double y);
            public static double Log10P1(double x);
            public static double Log2P1(double x);
            public static double LogP1(double x);
            public static double MaxMagnitudeNumber(double x, double y);
            public static double MaxNumber(double x, double y);
            public static double MinMagnitudeNumber(double x, double y);
            public static double MinNumber(double x, double y);
            public static double Root(double x, double n);
            public static double SinPi(double x);
            public static double TanPi(double x);
        }
        public static partial class MathF
        {
            public static float AcosPi(float x);
            public static float AsinPi(float x);
            public static float Atan2Pi(float y, float x);
            public static float AtanPi(float x);
            public static float Compound(float x, float n);
            public static float CosPi(float x);
            public static float Exp10(float x);
            public static float Exp10M1(float x);
            public static float Exp2(float x);
            public static float Exp2M1(float x);
            public static float ExpM1(float x);
            public static float Hypot(float x, float y);
            public static float Log10P1(float x);
            public static float Log2P1(float x);
            public static float LogP1(float x);
            public static float MaxMagnitudeNumber(float x, float y);
            public static float MaxNumber(float x, float y);
            public static float MinMagnitudeNumber(float x, float y);
            public static float MinNumber(float x, float y);
            public static float Root(float x, float n);
            public static float SinPi(float x);
            public static float TanPi(float x);
        }
    }
  14. added
    help wanted[up-for-grabs] Good issue for external contributors
    on Mar 8, 2022
  15. danmoseley commented on Mar 8, 2022

    @danmoseley
    Contributor

    marking 'up for grabs' as I assume it is?

  16. tannergooding commented on Mar 8, 2022

    @tannergooding
    Member

    This one requires JIT work as well and is blocked on the generic math work.

  17. deeprobin commented on Mar 16, 2022

    @deeprobin
    Contributor

    @tannergooding
    In principle, I would have no problem with implementing the mathematical functions in C#.

    Why is work on the JIT needed here? Should AggresiveInlining not optimize these functions sufficiently?
    Is the implementation in the JIT easy?

  18. tannergooding commented on Mar 16, 2022

    @tannergooding
    Member

    In principle, I would have no problem with implementing the mathematical functions in C#.
    Why is work on the JIT needed here? Should AggresiveInlining not optimize these functions sufficiently?
    Is the implementation in the JIT easy?

    Implementing these functions correctly is non-trivial and requires quite a bit of work. We are currently relying on the C Runtime to provide these functions where possible to help simplify the implementation.

    The JIT work can be quite verbose and requires touching multiple layers, from PAL to JIT to managed, as well as adding tests and knowing the relevant edge case scenarios that need to be tested/validated for correctness.

  19. stephentoub commented on Jun 22, 2022

    @stephentoub
    Member

    @tannergooding, with all of the functions being exposed via the generic math interfaces, is this issue still relevant / we still want to add all these functions to Math{F}?

  20. tannergooding commented on Jun 22, 2022

    @tannergooding
    Member

    This can be closed now. We are indeed exposing all functions via the generic math interfaces and there aren't currently any plans to also expose them in Math/MathF.

  21. dcwuser commented on Jun 24, 2022

    @dcwuser
    ContributorAuthor

    Sorry, but how do the generic math interfaces make e.g. Log1P available? The use of generic math interfaces might make it possible to write a generic math implementation, but they don't make that implementation magically appear. The key issue here isn't whether these function are exposed via the Math/MathF types; it's whether they are available in anywhere. And unless I misunderstand, the generic math interfaces do not make them available.

  22. tannergooding commented on Jun 24, 2022

    @tannergooding
    Member

    LogP1, ExpM1, and friends have been in for a couple previews already. They are available as static members on float, double, and Half. AcosPi and friends were merged a couple days ago and will be in .NET 7 Preview 7 in the same manner (exposed as static members on the floating-point types).

    API review decided as part of generic math that Math and MathF would effectively not be versioned moving forward. Instead, Math APIs are exposed on the respective types as static functions, which is inline with how other types throughout the ecosystem work. It also solves the versioning issue and avoids needing to introduce yet another Math* class to handle System.Half or any future IEEE 754 floating-point types.

    The System.Numerics.IExponenentialFunctions<TSelf> interface covers Exp, ExpM1, Exp2, Exp2M1, Exp10, and Exp10M1.

    The System.Numerics.ILogarithmicFunctions<TSelf> interface covers Log, LogP1, Log2, Log2P1, Log10, and Log10P1

    The System.Numerics.ITrigonometricFunctions<TSelf> interfaces covers Acos, AcosPi, Asin, AsinPi, Atan, Atan2, Atan2Pi, AtanPi, Cos, CosPi, Sin, SinCos, SinPi, Tan, and TanPi.

    These interfaces are implemented by System.Numerics.IFloatingPointIeee754<TSelf> which is then implemented by System.Double, System.Half, and System.Single making it available both directly on the concrete types (e.g. float.Cos(x)) and indirectly via a constrained generic (e.g. T.Cos(x) given where T : IFloatingPointIeee754<T>). Future IEEE 754 types, such as a Binary128, Decimal32, Decimal64, Decimal128, BigFloat, or BigDecimal would implement the interfaces as well and follow the same pattern.

    APIs like Max, MaxNumber, Min, and MinNumber are available on System.Numerics.INumber<T>. APIs like MaxMagnitude, MaxMagnitudeNumber, MinMagnitude, and MinMagnitudeNumber are available on System.Numerics.INumberBase<T>, etc.

    The over-arching design doc for the generic math feature is covered in https://github.com/dotnet/designs/tree/main/accepted/2021/statics-in-interfaces

  23. tannergooding commented on Jun 24, 2022

    @tannergooding
    Member

    I believe Compound is the only API I still have to implement at this point for .NET 7. Hypot, CosPi, SinPi, and TanPi are implemented as compliant IEEE 754 implementations. LogP1 and friends are currently implemented "naively" and while I'll try to ensure a compliant implementation for them in .NET 7, that may get pushed out to .NET 8 due to timing. The important bit is making it available in the first place however as that greatly simplifies the generic math feature and versioning story.

  24. ghost locked as resolved and limited conversation to collaborators on Jul 24, 2022
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

    api-approvedAPI was approved in API review, it can be implementedarea-System.Numericshelp wanted[up-for-grabs] Good issue for external contributors

    Type

    No type

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions