Skip to content

Instantly share code, notes, and snippets.

@jcdickinson
Created February 28, 2012 16:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jcdickinson/1933489 to your computer and use it in GitHub Desktop.
Save jcdickinson/1933489 to your computer and use it in GitHub Desktop.
Sin Lookup Table
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Runtime.InteropServices;
namespace FastMath
{
public class FastTrig
{
// Care when going over 512:
// 512 (perfect NT page size) = 93ms.
// 16384 = 90ms.
// This contradicted what I just said - so I will leave it at 16384.
private const int MaxCircleAngle = 16384;
private const int HalfMaxCircleAngle = MaxCircleAngle / 2;
private const int QuarterMaxCircleAngle = MaxCircleAngle / 4;
private const int MaskMaxCircleAngle = MaxCircleAngle - 1;
private const double PiOverHalfCircleAngle = Math.PI / HalfMaxCircleAngle;
private const float HalfMaxCircleOverPiSingle = HalfMaxCircleAngle / (float)Math.PI;
private const double HalfMaxCircleOverPiDouble = HalfMaxCircleAngle / Math.PI;
private static readonly float[] _singleLookup = new float[MaxCircleAngle];
private static readonly double[] _doubleLookup = new double[MaxCircleAngle];
static FastTrig()
{
for (var i = 0; i < MaxCircleAngle; i++)
{
_singleLookup[i] = (float)
(_doubleLookup[i] = Math.Sin(i * PiOverHalfCircleAngle));
}
}
public static float Cos(float radians)
{
var i = (int)(radians * HalfMaxCircleOverPiSingle);
if (i < 0)
{
return _singleLookup[(QuarterMaxCircleAngle - i) & MaskMaxCircleAngle];
}
else
{
return _singleLookup[(QuarterMaxCircleAngle + i) & MaskMaxCircleAngle];
}
}
public static float Sin(float radians)
{
var i = (int)(radians * HalfMaxCircleOverPiSingle);
if (i < 0)
{
return _singleLookup[MaxCircleAngle - ((-i) & MaskMaxCircleAngle)];
}
else
{
return _singleLookup[i & MaskMaxCircleAngle];
}
}
public static double Cos(double radians)
{
var i = (int)(radians * HalfMaxCircleOverPiDouble);
if (i < 0)
{
return _doubleLookup[(QuarterMaxCircleAngle - i) & MaskMaxCircleAngle];
}
else
{
return _doubleLookup[(QuarterMaxCircleAngle + i) & MaskMaxCircleAngle];
}
}
public static double Sin(double radians)
{
var i = (int)(radians * HalfMaxCircleOverPiDouble);
if (i < 0)
{
return _doubleLookup[QuarterMaxCircleAngle - ((-i) & MaskMaxCircleAngle)];
}
else
{
return _doubleLookup[i & MaskMaxCircleAngle];
}
}
}
}
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace FastMath
{
class Program
{
private const int Iterations = 5000000;
static void Main(string[] args)
{
Bench("Math.Sin", () => Math.Sin(1));
Bench("Math.Cos", () => Math.Cos(1));
Bench("FastTrig.Sin", () => FastTrig.Sin(1));
Bench("FastTrig.Cos", () => FastTrig.Cos(1));
Console.Write("Press any key to graph Sin...");
Console.ReadKey(true);
Console.Clear();
DrawGraph(-1, 1, -(float)Math.PI, (float)Math.PI,
Tuple.Create<ConsoleColor, Func<float, float>>(ConsoleColor.Red, (x) => (float)Math.Sin(x)),
Tuple.Create<ConsoleColor, Func<float, float>>(ConsoleColor.Green, (x) => (float)FastTrig.Sin(x))
);
Console.Write("Press any key to graph Cos...");
Console.ReadKey(true);
Console.Clear();
DrawGraph(-1, 1, -(float)Math.PI, (float)Math.PI,
Tuple.Create<ConsoleColor, Func<float, float>>(ConsoleColor.Red, (x) => (float)Math.Cos(x)),
Tuple.Create<ConsoleColor, Func<float, float>>(ConsoleColor.Green, (x) => (float)FastTrig.Cos(x))
);
Console.Write("Press any key to quit...");
Console.ReadKey(true);
}
// Console graphs too accurate.
static void DrawGraph(
float vmin,
float vmax,
float hmin,
float hmax,
params Tuple<ConsoleColor, Func<float, float>>[] functions
)
{
var vrange = vmax - vmin;
var hrange = hmax - hmin;
var vscale = (Console.WindowHeight - 1) / vrange;
var hscale = hrange / (Console.WindowWidth - 1);
for (var i = 0; i < Console.WindowWidth - 1; i++)
{
var hval = (hscale * i) + hmin;
foreach (var item in functions)
{
var vval = (item.Item2(hval) - vmin) * vscale;
Console.SetCursorPosition(i, (int)vval);
Console.ForegroundColor = item.Item1;
Console.Write(".");
}
}
Console.ResetColor();
Console.SetCursorPosition(0, Console.WindowHeight - 1);
}
static void Bench(string description, Action act)
{
// Warm up.
for (var i = 0; i < 10; i++)
act();
var sw = new Stopwatch();
sw.Start();
sw.Stop();
sw.Reset();
Console.WriteLine("Benching {0}", description);
sw.Start();
for (var i = 0; i < Iterations; i++)
act();
sw.Stop();
Console.WriteLine("Took {0:0.0} ms for {1} iterations", sw.Elapsed.TotalMilliseconds, Iterations);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment