16

As both a C programmer and a C# programmer, one of the things I don't like about C# is how verbose math functions are. Every time you would have to use a Sin, cosine, or power function for example, you'd have to prepend the Math static class. This leads to very long code when the equation itself is pretty simple. The problem becomes even worse if you need to typecast data types. As a result, in my opinion, the readability suffers. For example:

double x =  -Math.Cos(X) * Math.Sin(Z) + Math.Sin(X) * Math.Sin(Y) * Math.Cos(Z);

As opposed to simply

double x = -cos(X) * sin(Z) + sin(X) * sin(Y) * cos(Z);

This is also the case in other langauges like Java.

I'm not sure if this question actually has a solution, but I would like to know if there are any tricks C# or Java programmers use to improve readability of Math code. I realize however that C#/Java/etc. are not math-oriented languages like MATLAB or similar, so it makes sense. But occasionally one would still need to write math code and it'll be great if one could make it more readable.

9a3eedi
  • 2,101
  • 3
  • 23
  • 29
  • I don't know any specifically, but you could probably find an algebra library that would allow you to define math functions with strings, though there would be some performance penalty. – raptortech97 Oct 24 '14 at 02:33
  • possible duplicate of [How would you know if you've written readable and easily maintainable code?](http://programmers.stackexchange.com/questions/141005/how-would-you-know-if-youve-written-readable-and-easily-maintainable-code) – gnat Oct 24 '14 at 04:36
  • 7
    You worry about a bit of extra verbosity yet happily hide a '+' in among '*' with unary operators - all without braces - I suspect you have priorities wrong. – mattnz Oct 24 '14 at 06:29
  • 1
    It was just an example, but good point – 9a3eedi Oct 24 '14 at 06:50
  • 6
    In C# 6.0, you will be able to write: `using System.Math; … double x = -Cos(X) * Sin(Z) + Sin(X) * Sin(Y) * Cos(Z);`. – svick Oct 24 '14 at 07:20
  • @mattnz: I suspect you have your worries wrong on this question. It's just an example. – phresnel Oct 24 '14 at 09:36
  • @phresnel - it's "just an example", that somehow makes it OK, as opposed to how bad and example it would be if it were "an example". – mattnz Oct 27 '14 at 05:13
  • @mattnz In any case where correctness matters I would expect the person to actually *look* at the expression and what it calculates instead of assuming the parentheses were placed in the correct place. Once it's under that level of scrutiny having braces won't make a difference for such a trivial example, unless the reader doesn't know basic algebra, in which case he's screwed either way. – Doval Jan 27 '15 at 15:18

7 Answers7

31

Within Java, there are many tools available to make certain things less verbose, you just have to be aware of them. One that is useful in this case is that of the static import (tutorial page, wikipedia).

In this case,

import static java.lang.Math.*;

class Demo {
    public static void main (String[] args) {
        double X = 42.0;
        double Y = 4.0;
        double Z = PI;

        double x =  -cos(X) * sin(Z) + sin(X) * sin(Y) * cos(Z);
        System.out.println(x);
    }
}

runs quite nicely (ideone). It's a bit heavy handed to do a static import of all of the Math class, but if you are doing a lot of math, then it might be called for.

The static import allows you to import a static field or method into the namespace of this class and invoke it without requiring the package name. You often find this in Junit test cases where import static org.junit.Assert.*; is found to get all the asserts available.

  • Excellent answer. I was not aware of this feature. What version of Java is this possible under? – 9a3eedi Oct 24 '14 at 01:52
  • @9a3eedi It was first made available in Java 1.5. –  Oct 24 '14 at 01:53
  • Nice technique. I like it. +1. – Randall Cook Oct 24 '14 at 02:03
  • 1
    @RandallCook In Java 1.4 days, people would do things like `public interface Constants { final static public double PI = 3.14; }` and then `public class Foo implements Constants` in all the classes to get access to the constants in the interface. This made a *big* mess. So, with 1.5, the static import was added to allow pulling in specific constants and static functions without having to implement an interface. –  Oct 24 '14 at 02:05
  • @RandallCook as an aside, this is one part of the classic "what are all the ways you can use `static` in Java?" interview question... one that many people miss (unless they are familiar with unit testing because you see it there). –  Oct 24 '14 at 02:08
  • 3
    You can selectively import certain functions `import static java.lang.Math.cos;` – ratchet freak Oct 24 '14 at 08:15
  • C# 6.0 will also support static Usings (imports). – TGnat Oct 24 '14 at 13:44
14

You could define local functions that call the global static functions. Hopefully the compiler will inline the wrappers, and then the JIT compiler will produce tight assembly code for the actual operations. For example:

class MathHeavy
{
    private double sin(double x) { return Math.sin(x); }
    private double cos(double x) { return Math.cos(x); }

    public double foo(double x, double y)
    {
        return sin(x) * cos(y) - cos(x) * sin(y);
    }
}

You could also create functions that bundle up common math operations into single operations. This would minimize the number of instances where functions like sin and cos appear in your code, thereby making the clunkiness of invoking the global static functions less noticeable. For example:

public Point2D rotate2D(double angle, Point2D p)
{
    double x = p.x * Math.cos(angle) - p.y * Math.sin(angle);
    double y = p.x * Math.sin(angle) + p.y * Math.cos(angle);

    return new Point2D(x, y)
}

You are working at the level of points and rotations, and the underlying trig functions are buried.

Randall Cook
  • 2,470
  • 19
  • 19
  • ... why did I not think of that :) – 9a3eedi Oct 24 '14 at 01:44
  • I marked this as the correct answer because it's a cross-platform solution that's simple enough. The other solutions are correct as well. I really can't believe I didn't think of this though :) it's just too obvious – 9a3eedi Oct 25 '14 at 16:46
5

With C# 6.0 you can use the feature of Static Import.

Your code could be:

using static System.Math;
using static System.Console;
namespace SomeTestApp
{
    class Program
    {
        static void Main(string[] args)
        {
            double X = 123;
            double Y = 5;
            double Z = 10;
            double x = -Cos(X) * Sin(Z) + Sin(X) * Sin(Y) * Cos(Z);
            WriteLine(x); //Without System, since it is imported 
        }
    }
}

See: Static Using Statements (A C# 6.0 Language Preview)

Another C# 6.0 “syntactic sugar” feature is the introduction of using static. With this feature, it’s possible to eliminate an explicit reference to the type when invoking a static method. Furthermore, using static lets you introduce only the extension methods on a specific class, rather than all extension methods within a namespace.

EDIT: Since Visual Studio 2015, CTP released in January 2015, static import requires explicit keyword static. like:

using static System.Console;
Habib
  • 151
  • 4
4

In addition to the other good answers here, I might also recommend a DSL for situations with substantial mathematical complexity (not average use cases, but perhaps some financial or academic projects).

With a DSL generation tool such as Xtext, you could define your own simplified mathematical grammar, which could in turn generate a class with containing the Java (or any other language) representation of your formulae.

DSL Expression:

domain GameMath {
    formula CalcLinearDistance(double): sqrt((x2 - x1)^2 + (y2 - y1)^2)
}

Generated Output:

public class GameMath {
    public static double CalcLinearDistance(int x1, int x2, int y1, int y2) {
        return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
    }
}

In such a simple example, the benefits of creating the grammar and Eclipse plugin wouldn't be worthwhile, but for more complicated projects, it could yield great benefits, especially if the DSL enabled business folks or academic researchers to maintain formal documents in a comfortable language, and be assured that their work was translated accurately into the project's implementation language.

Dan1701
  • 3,118
  • 1
  • 15
  • 24
  • 8
    Yes, in general, and by definition, a DSL may be useful when you work in a specific domain. However, if this DSL doesn't exist, or if it doesn't fit the needs, you have to *maintain* it, which may be problematic. Also, for the specific question ("How can I use the sin, cos, … methods/functions without writing the Math class every time"), a DSL is maybe an oversized solution. – mgoeminne Oct 24 '14 at 05:38
4

In C# you could use extension methods.

The below reads pretty nicely once you get used to the "postfix" notation:

public static class DoubleMathExtensions
{
    public static double Cos(this double n)
    {
        return Math.Cos(n);
    }

    public static double Sin(this double n)
    {
        return Math.Sin(n);
    }

    ...
}

var x =  -X.Cos() * Z.Sin() + X.Sin() * Y.Sin() * Z.Cos();

Unfortunately operator precedence makes things a bit uglier when dealing with negative numbers here. If you want to calculate Math.Cos(-X) instead of -Math.Cos(X) you will need to enclose the number in parenthesis:

var x = (-X).Cos() ...
rjnilsson
  • 399
  • 2
  • 4
  • 1
    Incidentally, this would make a nice use case for extension properties, and even a legitimate use case for abusing properties as methods! – Jörg W Mittag Oct 24 '14 at 08:31
  • This is what I thought. `x.Sin()` would take some adjustment, but I abuse extension methods and this would be, personally, my first inclination. – WernerCD Oct 24 '14 at 12:56
2

C#: A variation on Randall Cook's answer, which I like because it maintains the mathematical "look" of the code more than extension methods, is to use a wrapper but use function references for the calls rather than wrap them. I personally think it makes the code look cleaner, but it is basically doing the same thing.

I knocked up a little LINQPad test program including Randall's wrapped functions, my function references, and the direct calls.

The function referenced calls basically take the same time as the direct calls. The wrapped functions are consistently slower - although not by a huge amount.

Here's the code:

void Main()
{
    MyMathyClass mmc = new MyMathyClass();

    System.Diagnostics.Stopwatch sw = System.Diagnostics.Stopwatch.StartNew();

    for(int i = 0; i < 50000000; i++)
        mmc.DoStuff(1, 2, 3);

    "Function reference:".Dump();
    sw.Elapsed.Dump();      
    sw.Restart();

    for(int i = 0; i < 50000000; i++)
        mmc.DoStuffWrapped(1, 2, 3);

    "Wrapped function:".Dump();
    sw.Elapsed.Dump();      
    sw.Restart();

    "Direct call:".Dump();
    for(int i = 0; i < 50000000; i++)
        mmc.DoStuffControl(1, 2, 3);

    sw.Elapsed.Dump();
}

public class MyMathyClass
{
    // References
    public Func<double, double> sin;
    public Func<double, double> cos;
    public Func<double, double> tan;
    // ...

    public MyMathyClass()
    {
        sin = System.Math.Sin;
        cos = System.Math.Cos;
        tan = System.Math.Tan;
        // ...
    }

    // Wrapped functions
    public double wsin(double x) { return Math.Sin(x); }
    public double wcos(double x) { return Math.Cos(x); }
    public double wtan(double x) { return Math.Tan(x); }

    // Calculation functions
    public double DoStuff(double x, double y, double z)
    {
        return sin(x) + cos(y) + tan(z);
    }

    public double DoStuffWrapped(double x, double y, double z)
    {
        return wsin(x) + wcos(y) + wtan(z);
    }

    public double DoStuffControl(double x, double y, double z)
    {
        return Math.Sin(x) + Math.Cos(y) + Math.Tan(z);
    }
}

Results:

Function reference:
00:00:06.5952113

Wrapped function:
00:00:07.2570828

Direct call:
00:00:06.6396096
Whelkaholism
  • 129
  • 3
1

Use Scala! You can define symbolic operators, and you don't need parens for your methods. This makes math way easier to interpret.

For example, the same calculation in Scala and Java could be something like:

// Scala
def angle(u: Vec, v: Vec) = (u*v) / sqrt((u*u)*(v*v))

// Java
public double angle(u: Vec, v: Vec) {
  return u.dot(v) / sqrt(u.dot(u)*v.dot(v));
}

This adds up pretty quickly.

Rex Kerr
  • 1,234
  • 7
  • 9
  • 2
    Scala is not available on the CLR, only the JVM. Thus it is not really a viable alternative to C#. – ben rudgers Oct 24 '14 at 12:41
  • @benrudgers - C# doesn't run on the JVM, so it is not really a viable alternative to Java, which the question also asked about. The question doesn't specify that it has to be CLR! – Rex Kerr Oct 24 '14 at 19:55
  • Maybe I'm a Luddite, but two extra characters for "dot" instead of "*", with the benefit that the code is clearer, seems a small price to pay. Still, a good answer. – user949300 Jan 27 '15 at 17:06