Skip to content

Commit 20b7021

Browse files
authored
New Interval.Contains method. Documentation. (#62)
1 parent b4778ae commit 20b7021

40 files changed

+470
-217
lines changed

Examples/Program.cs

Lines changed: 1 addition & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -20,59 +20,8 @@ private static MethodInfo[] GetExampleMethods () {
2020
return(methods);
2121
}
2222

23-
private static double EllipticPi(double n, double k) {
24-
25-
if (n > 1.0) throw new ArgumentOutOfRangeException(nameof(n));
26-
if (k < 1.0 || k > 1.0) throw new ArgumentOutOfRangeException(nameof(k));
27-
28-
if (n == 1.0 || k == 1.0) return Double.PositiveInfinity;
29-
30-
// DLMF 19.8 describes how to compute \Pi(n, k) via AGM plus some auxiluary
31-
// calculations. Here a and g, along with p, converge to AGM(1,k') in the
32-
// usual way; the sum of auxiluary variables q computed along the way gives \Pi(n, k).
33-
// This method appears to have been derived by Carlson in "Three Improvements in
34-
// Reduction and Computation of Elliptic Integrals", Journal of Research of the
35-
// National Institute of Standards and Technology, 2002 Sep-Oct 107(5): 413-418
36-
// (https://www.ncbi.nlm.nih.gov/pmc/articles/PMC4861378/)
37-
// as a specialization of his method to calcuate R_J.
38-
39-
double a = 1.0;
40-
double g = Math.Sqrt((1.0 - k) * (1.0 + k));
41-
double n1 = 1.0 - n;
42-
double pSquared = n1;
43-
double p = Math.Sqrt(pSquared);
44-
double q = 1.0;
45-
double s = q;
46-
47-
for (int i = 1; i < 100; i++) {
48-
49-
double s_old = s;
50-
double ag = a * g;
51-
double e = (pSquared - ag) / (pSquared + ag);
52-
q = 0.5 * q * e;
53-
s += q;
54-
55-
double p_old = p;
56-
p = (pSquared + ag ) / (2.0 * p);
57-
58-
if (p == p_old && s == s_old) {
59-
return Math.PI / 4.0 / p * (2.0 + n / n1 * s);
60-
}
61-
62-
pSquared = p * p;
63-
a = 0.5 * (a + g);
64-
g = Math.Sqrt(ag);
65-
66-
}
67-
68-
throw new Exception();
69-
70-
}
71-
72-
7323
static void Main(string[] args)
7424
{
75-
double e3 = EllipticPi(-0.25,0.99);
7625

7726
MethodInfo[] methods = GetExampleMethods();
7827
Dictionary<string, MethodInfo> index = new Dictionary<string, MethodInfo>();
@@ -81,6 +30,7 @@ static void Main(string[] args)
8130
}
8231

8332
if (args.Length == 0) {
33+
Console.WriteLine("The following examples are defined:");
8434
foreach(string key in index.Keys) {
8535
Console.WriteLine(key);
8636
}

Numerics/Core/Interval.cs

Lines changed: 57 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,46 @@ public double RightEndpoint {
3939
}
4040
}
4141

42+
/// <summary>
43+
/// Determines whether a given point is contained in the closed interval.
44+
/// </summary>
45+
/// <param name="x">The point.</param>
46+
/// <returns><see langword="true"/> if <paramref name="x"/> is contained in the closed interval [a,b],
47+
/// otherwise <see langword="false"/>.</returns>
48+
public bool Contains (double x) {
49+
return (x >= a) && (x <= b);
50+
}
51+
52+
/// <summary>
53+
/// Determines whether a given point is contained in the closed or open interval.
54+
/// </summary>
55+
/// <param name="x">The point.</param>
56+
/// <param name="type">Specifier of whether the interval should be considered closed or open.</param>
57+
/// <returns><see langword="true"/> if <paramref name="x"/> is contained in the interval as specified,
58+
/// otherwise <see langword="false"/></returns>
59+
public bool Contains (double x, IntervalType type) {
60+
if (type == IntervalType.Closed) {
61+
return (x >= a) && (x <= b);
62+
} else {
63+
return (x > a) && (x < b);
64+
}
65+
}
66+
67+
/// <summary>
68+
/// Determines whether a given point is contained in the interval, with the left and right endpoints
69+
/// separately specififed as closed or open.
70+
/// </summary>
71+
/// <param name="x">The point.</param>
72+
/// <param name="leftEndpointType">Specifier of whether the interval should be considered as including its left endpoint.</param>
73+
/// <param name="rightEndpointType">Specifier of whether the interval should be considered as including its right endpoint.</param>
74+
/// <returns><see langword="true"/> if <paramref name="x"/> is contained in the interval as specified,
75+
/// otherwise <see langword="false"/></returns>
76+
public bool Contains (double x, IntervalType leftEndpointType, IntervalType rightEndpointType) {
77+
bool leftSatisfied = leftEndpointType == IntervalType.Closed ? x >= a : x > a;
78+
bool rightSatisfied = rightEndpointType == IntervalType.Closed ? x <= b : x < b;
79+
return leftSatisfied && rightSatisfied;
80+
}
81+
4282
/// <summary>
4383
/// Determines whether the argument lies in the open interval.
4484
/// </summary>
@@ -70,7 +110,7 @@ public double Width {
70110
/// </summary>
71111
public double Midpoint {
72112
get {
73-
return ((a + b) / 2.0);
113+
return 0.5 * (a + b);
74114
}
75115
}
76116

@@ -214,4 +254,20 @@ internal IEnumerable<int> GetContainedIntegers () {
214254

215255
}
216256

257+
/// <summary>
258+
/// Indicates whether an interval should be considered closed or open.
259+
/// </summary>
260+
public enum IntervalType {
261+
262+
/// <summary>
263+
/// Endpoints should be considered within the interval.
264+
/// </summary>
265+
Closed,
266+
267+
/// <summary>
268+
/// Endpoints should be considered outside the interval.
269+
/// </summary>
270+
Open
271+
}
272+
217273
}

Numerics/Core/MoreMath.cs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ public static double Hypot (double x, double y) {
140140
// Beebe, "Computation of expm1(x) = exp(x) - 1", 2002 (http://www.math.utah.edu/~beebe/reports/expm1.pdf)
141141
// makes some good points about e^x - 1.
142142
// * He shows that the point e^x = 1/2 and e^x = 3/2 are the relevent limits where Math.Exp(x) - 1.0
143-
// looses a one bit of accuracy.
143+
// looses one bit of accuracy.
144144
// * He measures that the maximum number of terms in the Taylor series required in this region is 17.
145145
// * He measures that the RMS error of the Taylor series in this region is ~0.8 bits and it's maximum
146146
// relative error is ~ 2.7 bits.
@@ -177,9 +177,9 @@ private static double ReducedExpm1Series (double x) {
177177
/// <returns>The value of e<sup>x</sup>-1.</returns>
178178
/// <remarks>
179179
/// <para>If x is close to 0, then e<sup>x</sup> is close to 1, and computing e<sup>x</sup>-1 by
180-
/// <tt>Math.Exp(x) - 1.0</tt> will be subject to severe loss of significance due to cancelation.
181-
/// This method maintains full precision for all values of x by switching to a series expansion for values of
182-
/// x near zero.</para>
180+
/// <c>Math.Exp(x) - 1.0</c> will be subject to severe loss of significance due to cancelation.
181+
/// This method maintains full precision for all values of x by switching to a series expansion
182+
/// when x is near zero.</para>
183183
/// </remarks>
184184
public static double ExpMinusOne (double x) {
185185
if ((expm1SeriesLowerLimit < x) && (x < expm1SeriesUpperLimit)) {

Numerics/Extended/AdvancedDoubleDoubleMath.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,20 +10,21 @@ namespace Meta.Numerics.Extended {
1010
/// <see cref="Double"/>-based functions, you must ensure that the arguments you provide are also more
1111
/// accurate than <see cref="Double"/>. Because decimal numbers expressed in code are automatically
1212
/// intrepreted by the compiler as <see cref="Double"/>, it's easier than you might think do this wrong.
13-
/// For example, invoking Gamma(0.2) will <i>not</i> compute &#x393;(1/5) to <see cref="DoubleDouble"/>
13+
/// For example, invoking <c>Gamma(0.2)</c> will <i>not</i> compute &#x393;(1/5) to <see cref="DoubleDouble"/>
1414
/// precision. The reason is that 0.2 is intrepreted by the compiler as a <see cref="Double"/>, and there
1515
/// is no <see cref="Double"/> value that is precisely 1/5. Instead, 0.2 parsed as a <see cref="Double"/>,
1616
/// is stored as 3602879701896397 X 2<sup>-54</sup> = 0.20000000000000001110223024625157... This equals
17-
/// 0.2 within the 16 decimal-place accuracy of <see cref="Double"/>, but clearly not within the 32
17+
/// 0.2 to within the 16 decimal-place accuracy of <see cref="Double"/>, but clearly not to within the 32
1818
/// decimal-place accuracy of <see cref="DoubleDouble"/>.</para>
1919
/// <para>There are a number of ways to ensure that you are providing an argument to <see cref="DoubleDouble"/> accuracy.
20-
/// One possibility is to use the <see cref="DoubleDouble"/> text parser, for example by invoking new DoubleDouble("0.2").
21-
/// Another is to produce tha argument as the result of calculation from exact integers, e.g. DoubleDouble.One / 5 or
22-
/// ((DoubleDouble) 1) / 5.
20+
/// One possibility is to use the <see cref="DoubleDouble"/> text parser, for example by invoking <c>new DoubleDouble("0.2")</c>.
21+
/// Another is to produce tha argument as the result of calculation from exact integers, <c>(DoubleDouble) 1 / 5</c>, which works
22+
/// because 1 and 5 (like all integers within range) are represented exactly and the because of the cast the division operation
23+
/// is <see cref="DoubleDouble.op_Division"/>.
2324
/// (The stored value in these cases is again not precisely 1/5, but is equal within the accuracy of <see cref="DoubleDouble"/>.)
2425
/// Finally, if you know that the argument you want is precisely represetable as a <see cref="Double"/>, you can safely
2526
/// use the compiler's parser. For example, invoking Gamma(0.25) does compute &#x393;(1/4) to <see cref="DoubleDouble"/>
26-
/// to <see cref="DoubleDouble"/> accuracy, because 1/4 is exactly representable via <see cref="Double"/>.</para>
27+
/// precision, because 1/4 is exactly representable by <see cref="Double"/>.</para>
2728
/// </remarks>
2829
public static partial class AdvancedDoubleDoubleMath {
2930

Numerics/Extended/AdvancedDoubleDoubleMath_Gamma.cs

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,12 @@ namespace Meta.Numerics.Extended {
1717
public static partial class AdvancedDoubleDoubleMath {
1818

1919
/// <summary>
20-
/// Computes the logarithm of the Gamma function double double precision.
20+
/// Computes the logarithm of the Gamma function with double double precision.
2121
/// </summary>
2222
/// <param name="x">The argument, which must be non-negative.</param>
23-
/// <returns>The value of Gamma(x).</returns>
23+
/// <returns>The value of ln(&#x393;(x)).</returns>
24+
/// <exception cref="ArgumentOutOfRangeException"><paramref name="x"/> is less than zero.</exception>
25+
/// <seealso cref="Meta.Numerics.Functions.AdvancedMath.LogGamma(double)"/>
2426
public static DoubleDouble LogGamma (DoubleDouble x) {
2527

2628
if (x < DoubleDouble.Zero) {

Numerics/Extended/DoubleDouble.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,38 @@ namespace Meta.Numerics.Extended {
99
/// Represents a floating point number with quadruple precision.
1010
/// </summary>
1111
/// <remarks>
12-
/// <para>The double double format uses two <see cref="Double"/> values to effectively
13-
/// double the precision with which a number can be stored and manipulated as compared to
14-
/// to the <see cref="Double"/> structure, i.e. to approximately 31 decimal digits of accuracy.</para>
12+
/// <para>The <see cref="DoubleDouble"/> structure uses two <see cref="Double"/> values to achieve
13+
/// twice the precision with which a floating point number can be stored and manipulated as compared to
14+
/// to the <see cref="Double"/> structure, approximately 31 decimal digits.</para>
1515
/// <para>Of all the extended precision floating point systems, double double is the
1616
/// fastest when implemented in software. A typical floating point operation using
17-
/// <see cref="DoubleDouble"/>s is just 3-4 times slower than on <see cref="Double"/>s.</para>
17+
/// <see cref="DoubleDouble"/>s is just 3-4 times slower than using <see cref="Double"/>s.</para>
1818
/// <para>To instantiate a <see cref="DoubleDouble"/>, you can use <see cref="DoubleDouble.TryParse(string, out DoubleDouble)"/>,
1919
/// or <see cref="DoubleDouble.Parse(string)"/>, or the constructor <see cref="DoubleDouble.DoubleDouble(string)"/>
2020
/// to parse the text representation of the decimal value you want. If the value you want can be represented as a <see cref="Double"/>
21-
/// or <see cref="Int32"/> or other built in type, you can cast that value to a <see cref="DoubleDouble"/>.</para>
21+
/// or <see cref="Int32"/> or other built in numeric type, you can cast that value to a <see cref="DoubleDouble"/>.</para>
22+
/// <para>When casting a <see cref="Double"/> to a <see cref="DoubleDouble"/>, there is a gotcha that you must be careful
23+
/// to avoid. Suppose you write <c>DoubleDouble x = 0.2</c> or <c>DoubleDouble x = 1.0 / 5.0</c>. You might think that this produces the
24+
/// <see cref="DoubleDouble"/> representation of 1/5, but you would be wrong. The problem is that the compiler intreprets 0.2 or 1.0/5.0
25+
/// as <see cref="Double"/>s, and 1/5th is not exactly representable as a double, since it is not a rational number with a power-of-two denominator.
26+
/// Double's best attempt at 1/5th is 3602879701896397 X 2^<sup>-54</sup> = 0.20000000000000001110223024625157..., which
27+
/// is accurate to 16 decimal digits, but not to 32. Therefore when it is cast to a <see cref="DoubleDouble"/> it is much
28+
/// farther away from 1/5th than <see cref="DoubleDouble"/> can achieve. To obtain 1/5th to the accuracy of a <see cref="DoubleDouble"/>,
29+
/// you must write <c>DoubleDouble x = new DoubleDouble("0.2")</c> or <c>DoubleDouble x = (DoubleDouble) 1 / 5</c>. (The latter works
30+
/// because 1 and 5 are exactly representable and the division is performed as <see cref="DoubleDouble"/> division. All integers in range,
31+
/// indeed all rational numbers with in-range numerators and power-of-two denominators, are exactly representable. So, for example,
32+
/// <c>DoubleDouble x = 0.25</c> <i>does</i> work as expected, because 1/4 is exactly representable. But to avoid the gotcha
33+
/// it's best to simply train yourself to avoid assigning <see cref="DoubleDouble"/> variables from factional <see cref="Double"/> values.)</para>
34+
/// <para>Many of the mathematical functions which are implemented for <see cref="Double"/> arguments by static methods of the <see cref="Math"/> class
35+
/// are implemented for <see cref="DoubleDouble"/> arguments by static methods of the <see cref="DoubleDouble"/> type itself,
36+
/// for example <see cref="DoubleDouble.Sqrt(DoubleDouble)"/> and <see cref="DoubleDouble.Log(DoubleDouble)"/>.
37+
/// Some of the advanced functions which are implemented for <see cref="Double"/> arguments by static methods of the <see cref="Meta.Numerics.Functions.AdvancedMath"/> class
38+
/// are implemented for <see cref="DoubleDouble"/> arguments by static methods of the <see cref="AdvancedDoubleDoubleMath"/> class.
39+
/// </para>
40+
/// <para>You may wonder why <see cref="DoubleDouble"/> is not simply named "Quad". The reason is that "Quad" would propertly refer to an
41+
/// implementation of the <see href="https://en.wikipedia.org/wiki/Quadruple-precision_floating-point_format">IEEE 754 quadruple-precision binary floating
42+
/// point format</see>, which would have not only the extended presion of <see cref="DoubleDouble"/>, but also an extended range (up to 10<sup>4932</sup>).
43+
/// </para>
2244
/// </remarks>
2345
public struct DoubleDouble : IEquatable<DoubleDouble>, IComparable<DoubleDouble> {
2446

0 commit comments

Comments
 (0)