// Computer numbers: Integers and IEEE floating point in C# // Peter Sestoft * sestoft@itu.dk * 2009-02-26, 2015-11-13, 2019-06-11 // Should add "unit tests" of all System.Math functions, following the // LaTeX tables. Challenge: Cannot check for NaNs using ==, so need // to test as // result==expected || Double.IsNaN(result)&&Double.IsNaN(expected) // Compile with // csc Numbers.cs using System; using System.Text; class Numbers { public static void Main(String[] args) { SystemInfo(); Console.WriteLine(7.0%2.0); Console.WriteLine(Math.IEEERemainder(7.0,2.0)); byteExamples(); intExamples(); floatExamples(); doubleExamples(); ExpFunction(); // Works only because IEEE handles infinities correctly: Console.WriteLine("R(1) = " + R(1)); Console.WriteLine("R(2) = " + R(2)); Console.WriteLine("R(3) = " + R(3)); Console.WriteLine("R(4) = " + R(4)); // Zeroes double zp = 0.0, zn = -0.0; Console.WriteLine("0.0 = {0} and -0.0 = {1}", zp, zn); Console.WriteLine(zp == zn && 1/zp != 1/zn); // true Console.WriteLine(zp.Equals(zn)); Console.WriteLine(zp.GetHashCode() + " " + zn.GetHashCode()); Console.WriteLine(-0.0 < 0.0); // Loss of significant digits double v = 9876543210.2, w = 9876543210.1; Console.WriteLine(v+w-w != v); // true AllDoubleOperations(); Point[] ps1 = new Point[] { new Point(2.1, 5.2), new Point(2.2, 5.4), new Point(2.4, 5.8) }; linear1(ps1); linear2(ps1); Point[] ps2 = Point.moveAll(ps1, 1E7, 1E7); linear1(ps2); linear2(ps2); Point[] ps3 = Point.moveAll(ps1, 5E7, 5E7); linear1(ps3); linear2(ps3); // Testing sum int N = 10000; double[] xs = new double[2*N]; for (int i=0; i f, String name, int i) { bool ok; if (!(ok = ErrorCode(f(MakeNaN(i))) == i)) Console.WriteLine("Wrong NaN payload in {0}(NaN) at errorbits={1}", name, i); return ok; } private static bool NanPayload(Func f, String name, int i) { double d = MakeNaN(i); bool ok1, ok2; if (!(ok1 = ErrorCode(f(d,2)) == i)) Console.WriteLine("Wrong NaN payload in {0}(NaN,2) at errorbits={1}", name, i); if (!(ok2 = ErrorCode(f(2,d)) == i)) Console.WriteLine("Wrong NaN payload in {0}(2,NaN) at errorbits={1}", name, i); return ok1 && ok2; } private static void NanPayload(Func f, String name) { for (int i=0; i<1025; i++) if (!NanPayload(f, name, i) || !NanPayload(f, name, -i)) return; } private static void NanPayload(Func f, String name) { for (int i=0; i<1025; i++) if (!NanPayload(f, name, i) || !NanPayload(f, name, -i)) return; } private static void TestNanPayload() { NanPayload(delegate(double x) { return x; }, "identity"); NanPayload(Math.Abs, "Abs"); NanPayload(Math.Acos, "Acos"); NanPayload(Math.Asin, "Asin"); NanPayload(Math.Atan, "Atan"); NanPayload(Math.Ceiling, "Ceiling"); NanPayload(Math.Cos, "Cos"); NanPayload(Math.Exp, "Exp"); NanPayload(Math.Floor, "Floor"); NanPayload(delegate(double x) { return Math.Log(x); }, "Log"); NanPayload(Math.Log10, "Log10"); NanPayload(Math.Round, "Round"); NanPayload(Math.Sin, "Sin"); NanPayload(Math.Sqrt, "Sqrt"); NanPayload(Math.Tan, "Tan"); // Arithmetic operations NanPayload(delegate(double x, double y) { return x+y; }, "+"); NanPayload(delegate(double x, double y) { return x-y; }, "-"); NanPayload(delegate(double x, double y) { return x*y; }, "*"); NanPayload(delegate(double x, double y) { return x/y; }, "/"); NanPayload(delegate(double x, double y) { return x%y; }, "%"); // Two-argument Math functions NanPayload(Math.Atan2, "Atan2"); NanPayload(Math.IEEERemainder, "IEEERemainder"); NanPayload(Math.Max, "Max"); NanPayload(Math.Min, "Min"); NanPayload(Math.Pow, "Pow"); NanPayload(delegate(double x, double y) { return Math.Log(x,y); }, "Log"); } // ------------------------------------------------------------ // Manipulating NaNs public static int ErrorCode(double d) { return (int)System.BitConverter.DoubleToInt64Bits(d); } public static double MakeNaN(int errorNumber) { long nanbits = System.BitConverter.DoubleToInt64Bits(Double.NaN); return System.BitConverter.Int64BitsToDouble(nanbits | errorNumber); } // ------------------------------------------------------------ // Linear regression computed two ways public class Point { public double x, y; public Point(double x, double y) { this.x = x; this.y = y; } public Point move(double dx, double dy) { return new Point(x + dx, y + dy); } public static Point[] moveAll(Point[] ps, double dx, double dy) { Point[] res = new Point[ps.Length]; for (int i=0; i fix(x+y)); displayAll2("-", (x, y) => fix(x-y)); displayAll2("*", (x, y) => fix(x*y)); displayAll2("/", (x, y) => fix(x/y)); displayAll2("\\%", (x, y) => fix(x%y)); Console.WriteLine("\\subsection{Comparison operators}"); displayAll2("==", (x, y) => (x==y) + ""); displayAll2("!=", (x, y) => (x!=y) + ""); displayAll2("<", (x, y) => (x", (x, y) => (x>y) + ""); displayAll2(">=", (x, y) => (x>=y) + ""); Console.WriteLine("\\subsection{Two-argument mathematical functions}"); Console.WriteLine(@"\noindent\textrm{These agree with the corresponding functions in Java, except for \texttt{Atan2(+/-Inf,+/-Inf)} and \texttt{Pow(NaN, +/-0.0)}.}\\[1ex]"); displayAll2("Math.Atan2", (x, y) => fix("{0:F3}", Math.Atan2(x,y))); displayAll2("Math.IEEERemainder", (x, y) => fix(Math.IEEERemainder(x,y))); displayAll2("Math.Max", (x, y) => fix(Math.Max(x,y))); displayAll2("Math.Min", (x, y) => fix(Math.Min(x,y))); displayAll2("Math.Pow", (x, y) => fix(Math.Pow(x,y))); Console.WriteLine("\\subsection{One-argument mathematical functions}"); Console.WriteLine(@"\noindent\textrm{These all agree with the corresponding functions in Java.}\\[1ex]"); displayAll1( new Function1("Math.Abs", Math.Abs), new Function1("Math.Acos", Math.Acos), new Function1("Math.Asin", Math.Asin), new Function1("Math.Atan", Math.Atan), new Function1("Math.Ceiling", Math.Ceiling), new Function1("Math.Cos", Math.Cos), new Function1("Math.Exp", Math.Exp), new Function1("Math.Floor", Math.Floor), new Function1("Math.Log", Math.Log), new Function1("Math.Log10", Math.Log10), new Function1("Math.Round", Math.Round), new Function1("Math.Sin", Math.Sin), new Function1("Math.Sqrt", Math.Sqrt), new Function1("Math.Tan", Math.Tan) ); } class Function1 { public readonly String name; public readonly Func fun; public Function1(String name, Func fun) { this.name = name; this.fun = fun; } public String invoke(double x) { return fix("{0:F3}", fun(x)); } } private readonly static double[] values = { Double.NegativeInfinity, -2.0, -0.0, 0.0, +2.0, Double.PositiveInfinity, Double.NaN }; private static void displayAll2(String name, Func function) { values[2] = -0.0; // Necessary because of bug in gmcs 2.2 Console.WriteLine("\\noindent"); Console.WriteLine("\\begin{tabular}{r|rrrrrrr}"); Console.Write("\\multicolumn{1}{c|}{" + name + "}"); for (int i=0; i>= 1; } return new String(cs); } }