chiark / gitweb /
windows: C# wrapper for Fixed128
authorRichard Kettlewell <rjk@terraraq.org.uk>
Sat, 22 Dec 2012 16:12:21 +0000 (16:12 +0000)
committerRichard Kettlewell <rjk@terraraq.org.uk>
Sat, 22 Dec 2012 16:12:21 +0000 (16:12 +0000)
Lightly tested.

lib/Fixed128-str2.c
lib/Fixed128.h
mandy.sln
windows/mandycs/Fixed128.cs [new file with mode: 0644]
windows/mandycs/mandycs.csproj
windows/tests/Fixed128Test.cs [new file with mode: 0644]
windows/tests/tests.csproj

index 0338940..5709c3f 100644 (file)
@@ -291,6 +291,18 @@ done:
   return error;
 }
 
+int Fixed128_str2_cs(struct Fixed128 *r, const char *s) {
+  char *endptr;
+  int rc = Fixed128_str2(r, s, &endptr);
+  if(rc == 0) {
+    if(endptr == s || *endptr)
+      return FIXED128_STR_FORMAT;
+    else
+      return FIXED128_STR_OK;
+  } else
+    return FIXED128_STR_RANGE;
+}
+
 /*
 Local Variables:
 mode:c
index de3059d..565bb21 100644 (file)
@@ -87,6 +87,11 @@ extern "C" {
                                    int base);
   LIBMANDY_API int Fixed128_str2(struct Fixed128 *r, const char *s, char **endptr);
 
+  LIBMANDY_API int Fixed128_str2_cs(struct Fixed128 *r, const char *s);
+#define FIXED128_STR_OK 0
+#define FIXED128_STR_RANGE 1
+#define FIXED128_STR_FORMAT 2
+
   LIBMANDY_API void Fixed128_double2(struct Fixed128 *r, double n);
   LIBMANDY_API double Fixed128_2double(const struct Fixed128 *a);
   LIBMANDY_API long double Fixed128_2longdouble(const struct Fixed128 *a);
index ab8eedb..52da45b 100644 (file)
--- a/mandy.sln
+++ b/mandy.sln
@@ -37,9 +37,10 @@ Global
                Release|x64 = Release|x64
        EndGlobalSection
        GlobalSection(ProjectConfigurationPlatforms) = postSolution
-               {E5DDECA6-1F6C-401B-BBB6-1101B1F5B61D}.Debug|Any CPU.ActiveCfg = Debug|Win32
-               {E5DDECA6-1F6C-401B-BBB6-1101B1F5B61D}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
-               {E5DDECA6-1F6C-401B-BBB6-1101B1F5B61D}.Debug|Mixed Platforms.Build.0 = Debug|Win32
+               {E5DDECA6-1F6C-401B-BBB6-1101B1F5B61D}.Debug|Any CPU.ActiveCfg = Debug|x64
+               {E5DDECA6-1F6C-401B-BBB6-1101B1F5B61D}.Debug|Any CPU.Build.0 = Debug|x64
+               {E5DDECA6-1F6C-401B-BBB6-1101B1F5B61D}.Debug|Mixed Platforms.ActiveCfg = Debug|x64
+               {E5DDECA6-1F6C-401B-BBB6-1101B1F5B61D}.Debug|Mixed Platforms.Build.0 = Debug|x64
                {E5DDECA6-1F6C-401B-BBB6-1101B1F5B61D}.Debug|Win32.ActiveCfg = Debug|Win32
                {E5DDECA6-1F6C-401B-BBB6-1101B1F5B61D}.Debug|Win32.Build.0 = Debug|Win32
                {E5DDECA6-1F6C-401B-BBB6-1101B1F5B61D}.Debug|x64.ActiveCfg = Debug|x64
@@ -51,9 +52,10 @@ Global
                {E5DDECA6-1F6C-401B-BBB6-1101B1F5B61D}.Release|Win32.Build.0 = Release|Win32
                {E5DDECA6-1F6C-401B-BBB6-1101B1F5B61D}.Release|x64.ActiveCfg = Release|x64
                {E5DDECA6-1F6C-401B-BBB6-1101B1F5B61D}.Release|x64.Build.0 = Release|x64
-               {15F60354-A2F4-4C39-91E1-656DA501CE79}.Debug|Any CPU.ActiveCfg = Debug|Win32
-               {15F60354-A2F4-4C39-91E1-656DA501CE79}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
-               {15F60354-A2F4-4C39-91E1-656DA501CE79}.Debug|Mixed Platforms.Build.0 = Debug|Win32
+               {15F60354-A2F4-4C39-91E1-656DA501CE79}.Debug|Any CPU.ActiveCfg = Debug|x64
+               {15F60354-A2F4-4C39-91E1-656DA501CE79}.Debug|Any CPU.Build.0 = Debug|x64
+               {15F60354-A2F4-4C39-91E1-656DA501CE79}.Debug|Mixed Platforms.ActiveCfg = Debug|x64
+               {15F60354-A2F4-4C39-91E1-656DA501CE79}.Debug|Mixed Platforms.Build.0 = Debug|x64
                {15F60354-A2F4-4C39-91E1-656DA501CE79}.Debug|Win32.ActiveCfg = Debug|Win32
                {15F60354-A2F4-4C39-91E1-656DA501CE79}.Debug|Win32.Build.0 = Debug|Win32
                {15F60354-A2F4-4C39-91E1-656DA501CE79}.Debug|x64.ActiveCfg = Debug|x64
@@ -65,9 +67,10 @@ Global
                {15F60354-A2F4-4C39-91E1-656DA501CE79}.Release|Win32.Build.0 = Release|Win32
                {15F60354-A2F4-4C39-91E1-656DA501CE79}.Release|x64.ActiveCfg = Release|x64
                {15F60354-A2F4-4C39-91E1-656DA501CE79}.Release|x64.Build.0 = Release|x64
-               {8A51A96E-3178-4E07-8ADC-31613CFFC2BD}.Debug|Any CPU.ActiveCfg = Debug|Win32
-               {8A51A96E-3178-4E07-8ADC-31613CFFC2BD}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
-               {8A51A96E-3178-4E07-8ADC-31613CFFC2BD}.Debug|Mixed Platforms.Build.0 = Debug|Win32
+               {8A51A96E-3178-4E07-8ADC-31613CFFC2BD}.Debug|Any CPU.ActiveCfg = Debug|x64
+               {8A51A96E-3178-4E07-8ADC-31613CFFC2BD}.Debug|Any CPU.Build.0 = Debug|x64
+               {8A51A96E-3178-4E07-8ADC-31613CFFC2BD}.Debug|Mixed Platforms.ActiveCfg = Debug|x64
+               {8A51A96E-3178-4E07-8ADC-31613CFFC2BD}.Debug|Mixed Platforms.Build.0 = Debug|x64
                {8A51A96E-3178-4E07-8ADC-31613CFFC2BD}.Debug|Win32.ActiveCfg = Debug|Win32
                {8A51A96E-3178-4E07-8ADC-31613CFFC2BD}.Debug|Win32.Build.0 = Debug|Win32
                {8A51A96E-3178-4E07-8ADC-31613CFFC2BD}.Debug|x64.ActiveCfg = Debug|x64
@@ -79,9 +82,10 @@ Global
                {8A51A96E-3178-4E07-8ADC-31613CFFC2BD}.Release|Win32.Build.0 = Release|Win32
                {8A51A96E-3178-4E07-8ADC-31613CFFC2BD}.Release|x64.ActiveCfg = Release|x64
                {8A51A96E-3178-4E07-8ADC-31613CFFC2BD}.Release|x64.Build.0 = Release|x64
-               {51AC3A69-21F2-4E24-B5AD-289F1EE8FCFE}.Debug|Any CPU.ActiveCfg = Debug|Win32
-               {51AC3A69-21F2-4E24-B5AD-289F1EE8FCFE}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32
-               {51AC3A69-21F2-4E24-B5AD-289F1EE8FCFE}.Debug|Mixed Platforms.Build.0 = Debug|Win32
+               {51AC3A69-21F2-4E24-B5AD-289F1EE8FCFE}.Debug|Any CPU.ActiveCfg = Debug|x64
+               {51AC3A69-21F2-4E24-B5AD-289F1EE8FCFE}.Debug|Any CPU.Build.0 = Debug|x64
+               {51AC3A69-21F2-4E24-B5AD-289F1EE8FCFE}.Debug|Mixed Platforms.ActiveCfg = Debug|x64
+               {51AC3A69-21F2-4E24-B5AD-289F1EE8FCFE}.Debug|Mixed Platforms.Build.0 = Debug|x64
                {51AC3A69-21F2-4E24-B5AD-289F1EE8FCFE}.Debug|Win32.ActiveCfg = Debug|Win32
                {51AC3A69-21F2-4E24-B5AD-289F1EE8FCFE}.Debug|Win32.Build.0 = Debug|Win32
                {51AC3A69-21F2-4E24-B5AD-289F1EE8FCFE}.Debug|x64.ActiveCfg = Debug|x64
diff --git a/windows/mandycs/Fixed128.cs b/windows/mandycs/Fixed128.cs
new file mode 100644 (file)
index 0000000..341265e
--- /dev/null
@@ -0,0 +1,218 @@
+\feffusing System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using System.Runtime.InteropServices;
+
+namespace uk.org.greenend.mandy
+{
+  /// <summary>
+  /// 128-bit fixed point arithmetic
+  /// </summary>
+  /// <remarks><para>You get 32 bits of integer part and 96 bit of fractional part.</para></remarks>
+  [StructLayout(LayoutKind.Sequential)]
+  public struct Fixed128
+  {
+    /// <summary>
+    /// Three fractional words and the integer part
+    /// </summary>
+    /// <remarks><para>Negative values are indicate using two's complement.</para></remarks>
+    public uint f3, f2, f1, i;
+
+    /// <summary>
+    /// Constructor
+    /// </summary>
+    /// <param name="i">Integer part</param>
+    /// <param name="f1">Top 32 bits of fractional part</param>
+    /// <param name="f2">Middle 32 bits of fractional part</param>
+    /// <param name="f3">Bottom 32 bits of fractional part</param>
+    public Fixed128(int i, uint f1, uint f2, uint f3)
+    {
+      this.i = (uint)i;
+      this.f1 = f1;
+      this.f2 = f2;
+      this.f3 = f3;
+    }
+
+    #region Conversions
+
+    /// <summary>
+    /// Cast from an int.
+    /// </summary>
+    /// <param name="n">Integer value</param>
+    /// <returns>Converted value</returns>
+    public static implicit operator Fixed128(int n)
+    {
+      return new Fixed128(n, 0, 0, 0);
+    }
+
+    public static implicit operator Fixed128(string s)
+    {
+      Fixed128 r = 0;
+      int error = Fixed128_str2_cs(ref r, s);
+      switch(error)
+      {
+        case 0: // FIXED128_STR_OK
+          break;
+        case 1: // FIXED128_STR_RANGE
+          throw new FormatException("number out of range"); // TODO better choice?
+        case 2: // FIXED128_STR_FORMAT
+          throw new FormatException("invalid numeric format"); // TODO better choice?
+      }
+      return r;
+    }
+
+    public unsafe static implicit operator string(Fixed128 n)
+    {
+      const int BUFSIZE = 256;
+      byte* buffer = stackalloc byte[BUFSIZE];
+      Fixed128_2str(buffer, (IntPtr)BUFSIZE, ref n, 10);
+      return Marshal.PtrToStringAnsi((IntPtr)buffer);
+    }
+
+    public static implicit operator double(Fixed128 n)
+    {
+      return Fixed128_2double(ref n);
+    }
+
+    public static implicit operator Fixed128(double n)
+    {
+      Fixed128 r = 0;
+      Fixed128_double2(ref r, n);
+      return r;
+    }
+
+    #endregion
+
+    #region Operations
+
+    public static Fixed128 operator +(Fixed128 a, Fixed128 b)
+    {
+      Fixed128 r = 0;
+      Fixed128_add(ref r, ref a, ref b);
+      return r;
+    }
+
+    public static Fixed128 operator -(Fixed128 a, Fixed128 b)
+    {
+      Fixed128 r = 0;
+      Fixed128_sub(ref r, ref a, ref b);
+      return r;
+    }
+
+    public static Fixed128 operator *(Fixed128 a, Fixed128 b)
+    {
+      Fixed128 r = 0;
+      Fixed128_mul(ref r, ref a, ref b);
+      return r;
+    }
+
+    public static Fixed128 operator /(Fixed128 a, Fixed128 b)
+    {
+      Fixed128 r = 0;
+      Fixed128_div(ref r, ref a, ref b);
+      return r;
+    }
+
+    public static Fixed128 operator-(Fixed128 a)
+    {
+      Fixed128 r = 0;
+      Fixed128_neg(ref r, ref a);
+      return r;
+    }
+
+    public Fixed128 sqrt()
+    {
+      Fixed128 r = 0;
+      Fixed128_sqrt(ref r, ref this);
+      return r;
+    }
+
+    public static bool operator ==(Fixed128 a, Fixed128 b)
+    {
+      return Fixed128_eq(ref a, ref b) != 0;
+    }
+
+    public static bool operator !=(Fixed128 a, Fixed128 b)
+    {
+      return Fixed128_eq(ref a, ref b) == 0;
+    }
+
+    public static bool operator <(Fixed128 a, Fixed128 b)
+    {
+      return Fixed128_lt(ref a, ref b) != 0;
+    }
+
+    public static bool operator >=(Fixed128 a, Fixed128 b)
+    {
+      return Fixed128_lt(ref a, ref b) == 0;
+    }
+
+    public static bool operator >(Fixed128 a, Fixed128 b)
+    {
+      return Fixed128_lt(ref b, ref a) != 0;
+    }
+
+    public static bool operator <=(Fixed128 a, Fixed128 b)
+    {
+      return Fixed128_lt(ref b, ref a) == 0;
+    }
+
+    #endregion
+
+    #region Unmanaged code
+
+    [DllImport("libmandy.dll")]
+    private static extern void Fixed128_add(ref Fixed128 r,
+                                            ref Fixed128 a,
+                                            ref Fixed128 b);
+
+    [DllImport("libmandy.dll")]
+    private static extern void Fixed128_sub(ref Fixed128 r,
+                                            ref Fixed128 a,
+                                            ref Fixed128 b);
+
+    [DllImport("libmandy.dll")]
+    private static extern void Fixed128_mul(ref Fixed128 r,
+                                            ref Fixed128 a,
+                                            ref Fixed128 b);
+
+    [DllImport("libmandy.dll")]
+    private static extern void Fixed128_div(ref Fixed128 r,
+                                            ref Fixed128 a,
+                                            ref Fixed128 b);
+
+    [DllImport("libmandy.dll")]
+    private static extern void Fixed128_neg(ref Fixed128 r,
+                                            ref Fixed128 a);
+
+    [DllImport("libmandy.dll")]
+    private static extern void Fixed128_sqrt(ref Fixed128 r,
+                                             ref Fixed128 a);
+
+    [DllImport("libmandy.dll")]
+    private static extern int Fixed128_eq(ref Fixed128 r,
+                                          ref Fixed128 a);
+
+    [DllImport("libmandy.dll")]
+    private static extern int Fixed128_lt(ref Fixed128 r,
+                                          ref Fixed128 a);
+
+    [DllImport("libmandy.dll")]
+    private static extern int Fixed128_str2_cs(ref Fixed128 r, string s);
+
+    [DllImport("libmandy.dll")]
+    private unsafe static extern IntPtr Fixed128_2str(byte *buffer, IntPtr bufsize,
+                                                      ref Fixed128 a, int radix);
+
+    [DllImport("libmandy.dll")]
+    private static extern void Fixed128_double2(ref Fixed128 r, double n);
+
+    [DllImport("libmandy.dll")]
+    private static extern double Fixed128_2double(ref Fixed128 a);
+
+    #endregion
+
+  }
+}
index 9132a80..08988ee 100644 (file)
@@ -20,6 +20,8 @@
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <PlatformTarget>x64</PlatformTarget>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
@@ -28,6 +30,8 @@
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <PlatformTarget>x64</PlatformTarget>
+    <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
@@ -39,6 +43,7 @@
     <Reference Include="System.Xml" />
   </ItemGroup>
   <ItemGroup>
+    <Compile Include="Fixed128.cs" />
     <Compile Include="JobQueue.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
   </ItemGroup>
diff --git a/windows/tests/Fixed128Test.cs b/windows/tests/Fixed128Test.cs
new file mode 100644 (file)
index 0000000..e70eb04
--- /dev/null
@@ -0,0 +1,164 @@
+\feffusing System;
+using Microsoft.VisualStudio.TestTools.UnitTesting;
+using uk.org.greenend.mandy;
+
+namespace tests
+{
+  /// <summary>
+  /// Tests for Fix128
+  /// </summary>
+  /// <remarks><para>These tests aren't particularly exhaustive;
+  /// use fixed128-test for more exhaustive (and portable) testing.</para>
+  /// <para>Only the 64-bit configuration is testable.</para></remarks>
+  [TestClass]
+  [DeploymentItem(@"..\..\..\..\x64\Debug\libmandy.dll")]
+  public class Fixed128Test
+  {
+    [TestMethod]
+    public void AddTest()
+    {
+      Fixed128 a = 1;
+      Fixed128 b = 2;
+      Fixed128 r = a + b;
+      Assert.AreEqual(3u, r.i);
+      Assert.AreEqual(0u, r.f1);
+      Assert.AreEqual(0u, r.f2);
+      Assert.AreEqual(0u, r.f3);
+    }
+
+    [TestMethod]
+    public void SubTest()
+    {
+      Fixed128 a = 1;
+      Fixed128 b = 2;
+      Fixed128 r = a - b;
+      Assert.AreEqual(0xFFFFFFFFu, r.i);
+      Assert.AreEqual(0u, r.f1);
+      Assert.AreEqual(0u, r.f2);
+      Assert.AreEqual(0u, r.f3);
+    }
+
+    [TestMethod]
+    public void MulTest()
+    {
+      Fixed128 a = 3;
+      Fixed128 b = 5;
+      Fixed128 r = a * b;
+      Assert.AreEqual(15u, r.i);
+      Assert.AreEqual(0u, r.f1);
+      Assert.AreEqual(0u, r.f2);
+      Assert.AreEqual(0u, r.f3);
+      a = -3;
+      b = -5;
+      r = a * b;
+      Assert.AreEqual(15u, r.i);
+      Assert.AreEqual(0u, r.f1);
+      Assert.AreEqual(0u, r.f2);
+      Assert.AreEqual(0u, r.f3);
+      a = 3;
+      b = -5;
+      r = a * b;
+      Assert.AreEqual(0xFFFFFFF1u, r.i);
+      Assert.AreEqual(0u, r.f1);
+      Assert.AreEqual(0u, r.f2);
+      Assert.AreEqual(0u, r.f3);
+    }
+
+    [TestMethod]
+    public void DivTest()
+    {
+      Fixed128 a = 3;
+      Fixed128 b = 2;
+      Fixed128 r = a / b;
+      Assert.AreEqual(1u, r.i);
+      Assert.AreEqual(0x80000000u, r.f1);
+      Assert.AreEqual(0u, r.f2);
+      Assert.AreEqual(0u, r.f3);
+    }
+
+    [TestMethod]
+    public void NegTest()
+    {
+      Fixed128 a = 100;
+      Fixed128 r = -a;
+      Assert.AreEqual(0xFFFFFF9Cu, r.i);
+      Assert.AreEqual(0u, r.f1);
+      Assert.AreEqual(0u, r.f2);
+      Assert.AreEqual(0u, r.f3);
+      r = -r;
+      Assert.AreEqual(100u, r.i);
+      Assert.AreEqual(0u, r.f1);
+      Assert.AreEqual(0u, r.f2);
+      Assert.AreEqual(0u, r.f3);
+    }
+
+    [TestMethod]
+    public void SqrtTest()
+    {
+      Fixed128 a = 100;
+      Fixed128 r = a.sqrt();
+      Assert.AreEqual(10u, r.i);
+      Assert.AreEqual(0u, r.f1);
+      Assert.AreEqual(0u, r.f2);
+      Assert.AreEqual(0u, r.f3);
+      a = 2;
+      r = a.sqrt();
+      Assert.AreEqual(1u, r.i);
+      Assert.AreEqual(0x6a09e667u, r.f1);
+      Assert.AreEqual(0xf3bcc908u, r.f2);
+      Assert.AreEqual(0xb2fb1367u, r.f3);
+    }
+
+    [TestMethod]
+    public void FromDecimalTest()
+    {
+      Fixed128 a = "1";
+      Assert.AreEqual(1u, a.i);
+      Assert.AreEqual(0u, a.f1);
+      Assert.AreEqual(0u, a.f2);
+      Assert.AreEqual(0u, a.f3);
+      a = "400.125";
+      Assert.AreEqual(400u, a.i);
+      Assert.AreEqual(0x20000000u, a.f1);
+      Assert.AreEqual(0u, a.f2);
+      Assert.AreEqual(0u, a.f3);
+    }
+
+    [TestMethod]
+    public void ToDecimalTest()
+    {
+      Fixed128 a = 3;
+      string s = (string)a;
+      Assert.AreEqual("3", s);
+      a /= (Fixed128)2;
+      s = (string)a;
+      Assert.AreEqual("1.5", s);
+    }
+
+    [TestMethod]
+    public void FromDoubleTest()
+    {
+      Fixed128 a = 1.5;
+      Assert.AreEqual(1u, a.i);
+      Assert.AreEqual(0x80000000u, a.f1);
+      Assert.AreEqual(0u, a.f2);
+      Assert.AreEqual(0u, a.f3);
+      a = -100.125;
+      Assert.AreEqual(0xFFFFFF9Bu, a.i);
+      Assert.AreEqual(0xE0000000u, a.f1);
+      Assert.AreEqual(0u, a.f2);
+      Assert.AreEqual(0u, a.f3);
+    }
+
+    [TestMethod]
+    public void ToDoubleTest()
+    {
+      Fixed128 a = new Fixed128(1, 0x80000000u, 0u, 0u);
+      double n = a;
+      Assert.AreEqual(1.5, n);
+      a = new Fixed128(-101, 0xE0000000u, 0u, 0u);
+      n = a;
+      Assert.AreEqual(-100.125, n);
+    }
+  }
+}
index 3d6b848..2109d12 100644 (file)
@@ -25,6 +25,7 @@
     <DefineConstants>DEBUG;TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <PlatformTarget>x64</PlatformTarget>
   </PropertyGroup>
   <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
     <DebugType>pdbonly</DebugType>
@@ -33,6 +34,7 @@
     <DefineConstants>TRACE</DefineConstants>
     <ErrorReport>prompt</ErrorReport>
     <WarningLevel>4</WarningLevel>
+    <PlatformTarget>x64</PlatformTarget>
   </PropertyGroup>
   <ItemGroup>
     <Reference Include="System" />
@@ -52,6 +54,7 @@
   <ItemGroup>
     <Compile Include="JobQueueTest.cs" />
     <Compile Include="Properties\AssemblyInfo.cs" />
+    <Compile Include="Fixed128Test.cs" />
   </ItemGroup>
   <ItemGroup>
     <ProjectReference Include="..\mandycs\mandycs.csproj">