'Equivalent C# Function For Excels Norm.S.Inv Function

I plan on finding the benchmark Z's of some data in C#. For this I need the Norm.S.Inv function from Excel. I am not able to find any sort of implementation for this in C#. Can anyone point me out to a library or to some source code that can help me? Thank you



Solution 1:[1]

You can use Math.NET.

The method you are looking for is MathNet.Numerics.Distribution.Normal.InverseCumulativeDistribution

Solution 2:[2]

In Excel 2010 onwards the implementation of NORM.S.INV looks to be taken from the paper:

Wichura, M.J. (1988). "Algorithm AS241: The Percentage Points of the Normal Distribution". Applied Statistics (Blackwell Publishing) 37 (3): 477–484. (Ref: http://blogs.office.com/b/microsoft-excel/archive/2009/10/08/going-back-to-the-topic-of-functions-for-a-moment.aspx attachment P34 Sections 2.3.32-33)

For precision brenchmarking, maybe try: http://www.alglib.net/specialfunctions/distributions/normal.php

Solution 3:[3]

Since none of the other answers have provided one directly, below is an implementation of Algorithm AS 241: The Percentage Points of the Normal Distribution.

  • AS241(mean: m, probability: p, standardDeviation: s) is equivalent to NORM.INV.
  • AS241(mean: 0, probability: p, standardDeviation: 1) is equivalent to NORM.S.INV.

Code:

public static double As241_Unsafe(double probability) {
    var q = (probability - 0.5d);

    double v;

    if (Math.Abs(value: q) <= 0.425d) {
        var r = (0.180625d - (q * q));

        var t0 = Math.FusedMultiplyAdd(x: 2.5090809287301226727e+03d, y: r, z: 3.3430575583588128105e+04d);
        var t1 = Math.FusedMultiplyAdd(x: t0, y: r, z: 6.7265770927008700853e+04d);
        var t2 = Math.FusedMultiplyAdd(x: t1, y: r, z: 4.5921953931549871457e+04d);
        var t3 = Math.FusedMultiplyAdd(x: t2, y: r, z: 1.3731693765509461125e+04d);
        var t4 = Math.FusedMultiplyAdd(x: t3, y: r, z: 1.9715909503065514427e+03d);
        var t5 = Math.FusedMultiplyAdd(x: t4, y: r, z: 1.3314166789178437745e+02d);
        var t6 = Math.FusedMultiplyAdd(x: t5, y: r, z: 3.3871328727963666080e+00d);
        var u0 = Math.FusedMultiplyAdd(x: 5.2264952788528545610e+03d, y: r, z: 2.8729085735721942674e+04d);
        var u1 = Math.FusedMultiplyAdd(x: u0, y: r, z: 3.9307895800092710610e+04d);
        var u2 = Math.FusedMultiplyAdd(x: u1, y: r, z: 2.1213794301586595867e+04d);
        var u3 = Math.FusedMultiplyAdd(x: u2, y: r, z: 5.3941960214247511077e+03d);
        var u4 = Math.FusedMultiplyAdd(x: u3, y: r, z: 6.8718700749205790830e+02d);
        var u5 = Math.FusedMultiplyAdd(x: u4, y: r, z: 4.2313330701600911252e+01d);
        var u6 = Math.FusedMultiplyAdd(x: u5, y: r, z: 1.0d);

        v = ((q * t6) / u6);
    }
    else {
        var r = Math.Sqrt(d: -Math.Log(d: (q < 0.0d) ? probability : (1.0d - probability)));

        if (r <= 5.0d) {
            r -= 1.6d;

            var t0 = Math.FusedMultiplyAdd(x: 7.74545014278341407640e-04d, y: r, z: 2.27238449892691845833e-02d);
            var t1 = Math.FusedMultiplyAdd(x: t0, y: r, z: 2.41780725177450611770e-01d);
            var t2 = Math.FusedMultiplyAdd(x: t1, y: r, z: 1.27045825245236838258e+00d);
            var t3 = Math.FusedMultiplyAdd(x: t2, y: r, z: 3.64784832476320460504e+00d);
            var t4 = Math.FusedMultiplyAdd(x: t3, y: r, z: 5.76949722146069140550e+00d);
            var t5 = Math.FusedMultiplyAdd(x: t4, y: r, z: 4.63033784615654529590e+00d);
            var t6 = Math.FusedMultiplyAdd(x: t5, y: r, z: 1.42343711074968357734e+00d);
            var u0 = Math.FusedMultiplyAdd(x: 1.05075007164441684324e-09d, y: r, z: 5.47593808499534494600e-04d);
            var u1 = Math.FusedMultiplyAdd(x: u0, y: r, z: 1.51986665636164571966e-02d);
            var u2 = Math.FusedMultiplyAdd(x: u1, y: r, z: 1.48103976427480074590e-01d);
            var u3 = Math.FusedMultiplyAdd(x: u2, y: r, z: 6.89767334985100004550e-01d);
            var u4 = Math.FusedMultiplyAdd(x: u3, y: r, z: 1.67638483018380384940e+00d);
            var u5 = Math.FusedMultiplyAdd(x: u4, y: r, z: 2.05319162663775882187e+00d);
            var u6 = Math.FusedMultiplyAdd(x: u5, y: r, z: 1.0d);

            v = (t6 / u6);
        }
        else {
            r -= 5.0d;

            var t0 = Math.FusedMultiplyAdd(x: 2.01033439929228813265e-07d, y: r, z: 2.71155556874348757815e-05d);
            var t1 = Math.FusedMultiplyAdd(x: t0, y: r, z: 1.24266094738807843860e-03d);
            var t2 = Math.FusedMultiplyAdd(x: t1, y: r, z: 2.65321895265761230930e-02d);
            var t3 = Math.FusedMultiplyAdd(x: t2, y: r, z: 2.96560571828504891230e-01d);
            var t4 = Math.FusedMultiplyAdd(x: t3, y: r, z: 1.78482653991729133580e+00d);
            var t5 = Math.FusedMultiplyAdd(x: t4, y: r, z: 5.46378491116411436990e+00d);
            var t6 = Math.FusedMultiplyAdd(x: t5, y: r, z: 6.65790464350110377720e+00d);
            var u0 = Math.FusedMultiplyAdd(x: 2.04426310338993978564e-15d, y: r, z: 1.42151175831644588870e-07d);
            var u1 = Math.FusedMultiplyAdd(x: u0, y: r, z: 1.84631831751005468180e-05d);
            var u2 = Math.FusedMultiplyAdd(x: u1, y: r, z: 7.86869131145613259100e-04d);
            var u3 = Math.FusedMultiplyAdd(x: u2, y: r, z: 1.48753612908506148525e-02d);
            var u4 = Math.FusedMultiplyAdd(x: u3, y: r, z: 1.36929880922735805310e-01d);
            var u5 = Math.FusedMultiplyAdd(x: u4, y: r, z: 5.99832206555887937690e-01d);
            var u6 = Math.FusedMultiplyAdd(x: u5, y: r, z: 1.0d);

            v = (t6 / u6);
        }

        if (q < 0.0d) {
            v = -v;
        }
    }

    return v;
}
public static double As241(double probability) {
    if ((0.0d > probability) || (1.0d < probability)) {
        throw new ArgumentOutOfRangeException(
            message: "probability must be between the inclusive range [0, 1]",
            paramName: nameof(probability)
        );
    }

    if (0.0d == probability) {
        return double.NegativeInfinity;
    }

    if (1.0d == probability) {
        return double.PositiveInfinity;
    }

    return As241_Unsafe(probability: probability);
}
public static double As241(double mean, double probability, double standardDeviation) {
    if (0.0d > standardDeviation) {
        throw new ArgumentOutOfRangeException(
            message: "standardDeviation must be greater than or equal to 0",
            paramName: nameof(standardDeviation)
        );
    }

    return (mean + (standardDeviation * As241(probability: probability)));
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Matthieu Durut
Solution 2 lori_m
Solution 3