├── .gitignore ├── CHANGELOG.md ├── Cpp ├── CppTest.cpp ├── CppTest.sln ├── CppTest.vcxproj ├── CppTest.vcxproj.filters ├── Fixed32.h ├── Fixed64.h ├── FixedUtil.h ├── UnitTest.cpp └── UnitTest.h ├── Examples ├── FixMath │ ├── App.config │ ├── F32.cs │ ├── F32Vec2.cs │ ├── F32Vec3.cs │ ├── F32Vec4.cs │ ├── F64.cs │ ├── F64Quat.cs │ ├── F64Vec2.cs │ ├── F64Vec3.cs │ ├── F64Vec4.cs │ └── FixMath.csproj └── FixedTracer │ ├── App.config │ ├── DoubleTracer.cs │ ├── FixedTracer.cs │ ├── FixedTracer.csproj │ └── Program.cs ├── FixPointCS.sln ├── FixPointCS ├── FixPointCS.csproj ├── Fixed32.cs ├── Fixed64.cs └── FixedUtil.cs ├── FixPointCSTest ├── App.config ├── FixPointCSTest.csproj └── Program.cs ├── Java ├── FixPointTest.java ├── Fixed32.java ├── Fixed64.java ├── FixedUtil.java ├── UnitTest.java └── test.bat ├── LICENSE.txt ├── Polyfit ├── polyfit.py └── remez.py ├── README.md ├── Transpiler ├── App.config ├── GenerateCpp.cs ├── GenerateJava.cs ├── Program.cs ├── Transpiler.csproj └── Util.cs └── functions.md /.gitignore: -------------------------------------------------------------------------------- 1 | .vs 2 | *.user 3 | *.class 4 | Cpp/x64 5 | obj 6 | bin 7 | UpgradeLog.htm 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | 2 | # Changelog 3 | 4 | ## 0.3 (2022-03-26) 5 | 6 | ### Changes 7 | 8 | - Upgrade all C# projects to .NET 6. 9 | - FixMath: Replace F32 and F64 constructors with explicit FromInt(), FromFloat(), FromDouble() to avoid accidental implicit casts. 10 | - FixMath: F32 and F64 now implement IComparable to make them usable in unit test assertion macros. 11 | - FixMath: Optimize Lerp() operations with a simpler formula. 12 | - FixMath: Introduce Clamp01() method for F32 and F64 which clamps the input to [0, 1] range. 13 | - FixMath: Introduce some extra inlining to speed up code using the library. 14 | - FixMath: F32 family of Length*() and Normalize*() methods now use 64-bit intermediate results to avoid overflows. 15 | - FixMath: F32Vec*.LengthSqr() now returns F64 to avoid overflows. 16 | - Fixed32.Nlz(): Disabled the System.Numerics.BitOperations.LeadingZeroCount() because it's slower in benchmarks. 17 | - Fixed32,64.ToString() now uses CultureInfo.InvariantCulture. 18 | 19 | ## 0.2 (2021-11-14) 20 | 21 | ### Changes 22 | 23 | - Converted C# projects to .NET 5. The sources are still compatible with .NET Framework and Unity as well. 24 | - Fix C++ transpiled code to use INT64_MIN and INT64_MAX. 25 | - Generate only LF in transpiler. 26 | - Removed extra logic from F32.GetHashCode(), still outputs the same value. 27 | - Use System.Numerics.BitOperations.LeadingZeroCount() with .NET 5 or higher. Boosts performance by about 10% for methods relying on it. 28 | 29 | ### Breaking (these can cause math function outputs to change and thus break determinism) 30 | 31 | - Changed F64.GetHashCode() to use System.Int64.GetHashCode(). 32 | - Fixed32 and Fixed64.Mod() checks for div-by-zero. Simplify the code by using the modulo operator (%). 33 | - Fixed all Pow*(X, 0) to always return precisely 1.0, including Pow*(0, 0) which previously returned 0. 34 | 35 | ## 0.1 (2019-06-22) 36 | 37 | - First released version (tagged after-the-fact) 38 | -------------------------------------------------------------------------------- /Cpp/CppTest.cpp: -------------------------------------------------------------------------------- 1 | // 2 | // FixPointCS 3 | // 4 | // Copyright(c) Jere Sanisalo, Petri Kero 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | #include 25 | 26 | #include "FixedUtil.h" 27 | #include "Fixed32.h" 28 | #include "Fixed64.h" 29 | 30 | #include "UnitTest.h" 31 | 32 | void Test32() 33 | { 34 | double v = 0.0001f; 35 | 36 | while (v < 150.f) 37 | { 38 | Fixed32::FP_INT fv = Fixed32::FromDouble(v); 39 | Fixed32::FP_INT fv_div = Fixed32::DivPrecise(fv, Fixed32::FromDouble(-2.34)); 40 | Fixed32::FP_INT fv_sqrt = Fixed32::Sqrt(fv); 41 | Fixed32::FP_INT fv_sin = Fixed32::Sin(fv); 42 | Fixed32::FP_INT fv_rcp = Fixed32::RcpFast(fv); 43 | 44 | std::cout << v 45 | << ": div_by_-2.34: " << Fixed32::ToDouble(fv_div) 46 | << ", sqrt: " << Fixed32::ToDouble(fv_sqrt) 47 | << ", sin: " << Fixed32::ToDouble(fv_sin) 48 | << ", rcp: " << Fixed32::ToDouble(fv_rcp) 49 | << std::endl; 50 | 51 | // Next number 52 | v *= 1.5f; 53 | } 54 | } 55 | 56 | void Test64() 57 | { 58 | double v = 0.0001f; 59 | 60 | while (v < 150.f) 61 | { 62 | Fixed64::FP_LONG fv = Fixed64::FromDouble(v); 63 | Fixed64::FP_LONG fv_div = Fixed64::DivPrecise(fv, Fixed64::FromDouble(-2.34)); 64 | Fixed64::FP_LONG fv_sqrt = Fixed64::Sqrt(fv); 65 | Fixed64::FP_LONG fv_sin = Fixed64::Sin(fv); 66 | Fixed64::FP_LONG fv_rcp = Fixed64::RcpFast(fv); 67 | 68 | std::cout << v 69 | << ": div_by_-2.34: " << Fixed64::ToDouble(fv_div) 70 | << ", sqrt: " << Fixed64::ToDouble(fv_sqrt) 71 | << ", sin: " << Fixed64::ToDouble(fv_sin) 72 | << ", rcp: " << Fixed64::ToDouble(fv_rcp) 73 | << std::endl; 74 | 75 | // Next number 76 | v *= 1.5f; 77 | } 78 | } 79 | 80 | int main() 81 | { 82 | std::cout << "Testing 16.16 fixed point numbers.." << std::endl; 83 | Test32(); 84 | 85 | std::cout << std::endl; 86 | std::cout << "Testing 32.32 fixed point numbers.." << std::endl; 87 | Test64(); 88 | 89 | std::cout << std::endl; 90 | std::cout << "Executing all unit tests.." << std::endl; 91 | UnitTest_TestAll(); 92 | std::cout << "Unit tests finished!" << std::endl; 93 | 94 | return 0; 95 | } 96 | -------------------------------------------------------------------------------- /Cpp/CppTest.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.27130.2027 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "CppTest", "CppTest.vcxproj", "{C8358827-2A34-4AC0-8E43-F6C3D9C0EB9E}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|x64 = Debug|x64 11 | Release|x64 = Release|x64 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {C8358827-2A34-4AC0-8E43-F6C3D9C0EB9E}.Debug|x64.ActiveCfg = Debug|x64 15 | {C8358827-2A34-4AC0-8E43-F6C3D9C0EB9E}.Debug|x64.Build.0 = Debug|x64 16 | {C8358827-2A34-4AC0-8E43-F6C3D9C0EB9E}.Release|x64.ActiveCfg = Release|x64 17 | {C8358827-2A34-4AC0-8E43-F6C3D9C0EB9E}.Release|x64.Build.0 = Release|x64 18 | EndGlobalSection 19 | GlobalSection(SolutionProperties) = preSolution 20 | HideSolutionNode = FALSE 21 | EndGlobalSection 22 | GlobalSection(ExtensibilityGlobals) = postSolution 23 | SolutionGuid = {7D05C426-C690-4393-BC4B-719898A90413} 24 | EndGlobalSection 25 | EndGlobal 26 | -------------------------------------------------------------------------------- /Cpp/CppTest.vcxproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Debug 6 | Win32 7 | 8 | 9 | Release 10 | Win32 11 | 12 | 13 | Debug 14 | x64 15 | 16 | 17 | Release 18 | x64 19 | 20 | 21 | 22 | 15.0 23 | {C8358827-2A34-4AC0-8E43-F6C3D9C0EB9E} 24 | Win32Proj 25 | CppTest 26 | 10.0 27 | 28 | 29 | 30 | Application 31 | true 32 | v142 33 | Unicode 34 | 35 | 36 | Application 37 | false 38 | v142 39 | true 40 | Unicode 41 | 42 | 43 | Application 44 | true 45 | v142 46 | Unicode 47 | 48 | 49 | Application 50 | false 51 | v142 52 | true 53 | Unicode 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | true 75 | 76 | 77 | true 78 | 79 | 80 | false 81 | 82 | 83 | false 84 | 85 | 86 | 87 | NotUsing 88 | Level3 89 | Disabled 90 | true 91 | WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) 92 | true 93 | 94 | 95 | 96 | Console 97 | true 98 | 99 | 100 | 101 | 102 | NotUsing 103 | Level3 104 | Disabled 105 | true 106 | _DEBUG;_CONSOLE;%(PreprocessorDefinitions) 107 | true 108 | 109 | 110 | 111 | Console 112 | true 113 | 114 | 115 | 116 | 117 | NotUsing 118 | Level3 119 | MaxSpeed 120 | true 121 | true 122 | true 123 | WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 124 | true 125 | 126 | 127 | 128 | Console 129 | true 130 | true 131 | true 132 | 133 | 134 | 135 | 136 | NotUsing 137 | Level3 138 | MaxSpeed 139 | true 140 | true 141 | true 142 | NDEBUG;_CONSOLE;%(PreprocessorDefinitions) 143 | true 144 | 145 | 146 | 147 | Console 148 | true 149 | true 150 | true 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | -------------------------------------------------------------------------------- /Cpp/CppTest.vcxproj.filters: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | {4FC737F1-C7A5-4376-A066-2A32D752A2FF} 6 | cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx 7 | 8 | 9 | {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} 10 | rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms 11 | 12 | 13 | 14 | 15 | Source Files 16 | 17 | 18 | Source Files 19 | 20 | 21 | 22 | 23 | Source Files 24 | 25 | 26 | Source Files 27 | 28 | 29 | Source Files 30 | 31 | 32 | Source Files 33 | 34 | 35 | -------------------------------------------------------------------------------- /Cpp/UnitTest.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #ifndef __UNITTEST_H 3 | #define __UNITTEST_H 4 | 5 | #include 6 | #include 7 | 8 | #include "Fixed32.h" 9 | #include "Fixed64.h" 10 | 11 | class Util 12 | { 13 | public: 14 | static void Check(const char* opName, int64_t output, int64_t expected, int64_t input0) 15 | { 16 | if (output != expected) 17 | printf("MISMATCH in %s: got %" PRId64 ", expected %" PRId64 ", inputs %" PRId64 "\n", opName, output, expected, input0); 18 | } 19 | 20 | static void Check(const char* opName, int64_t output, int64_t expected, int64_t input0, int64_t input1) 21 | { 22 | if (output != expected) 23 | printf("MISMATCH in %s: got %" PRId64 ", expected %" PRId64 ", inputs %" PRId64 " %" PRId64 "\n", opName, output, expected, input0, input1); 24 | } 25 | 26 | // static void Check(const char* opName, int32_t output, int32_t expected, int32_t input0) 27 | // { 28 | // if (output != expected) 29 | // printf("MISMATCH in %s: got %d, expected %d, inputs %d\n", opName, output, expected, input0); 30 | // } 31 | // 32 | // static void Check(const char* opName, int32_t output, int32_t expected, int32_t input0, int32_t input1) 33 | // { 34 | // if (output != expected) 35 | // printf("MISMATCH in %s: got %d, expected %d, inputs %d %d\n", opName, output, expected, input0, input1); 36 | // } 37 | }; 38 | 39 | void UnitTest_TestAll(); 40 | 41 | #endif 42 | -------------------------------------------------------------------------------- /Examples/FixMath/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Examples/FixMath/F32.cs: -------------------------------------------------------------------------------- 1 | // 2 | // FixPointCS 3 | // 4 | // Copyright(c) Jere Sanisalo, Petri Kero 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | using FixPointCS; 25 | using System; 26 | using System.Runtime.CompilerServices; 27 | 28 | namespace FixMath 29 | { 30 | /// 31 | /// Signed 16.16 fixed point value struct. 32 | /// 33 | [Serializable] 34 | public struct F32 : IComparable, IEquatable, IComparable 35 | { 36 | // Constants 37 | public static F32 Neg1 { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.Neg1); } } 38 | public static F32 Zero { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.Zero); } } 39 | public static F32 Half { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.Half); } } 40 | public static F32 One { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.One); } } 41 | public static F32 Two { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.Two); } } 42 | public static F32 Pi { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.Pi); } } 43 | public static F32 Pi2 { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.Pi2); } } 44 | public static F32 PiHalf { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.PiHalf); } } 45 | public static F32 E { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.E); } } 46 | 47 | public static F32 MinValue { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.MinValue); } } 48 | public static F32 MaxValue { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed32.MaxValue); } } 49 | 50 | // Raw fixed point value 51 | public int Raw; 52 | 53 | // Construction 54 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F32 FromRaw(int raw) { F32 v; v.Raw = raw; return v; } 55 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F32 FromInt(int v) { return FromRaw(Fixed32.FromInt(v)); } 56 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F32 FromFloat(float v) { return FromRaw(Fixed32.FromFloat(v)); } 57 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F32 FromDouble(double v) { return FromRaw(Fixed32.FromDouble(v)); } 58 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F32 FromF64(F64 v) { return FromRaw((int)(v.Raw >> 16)); } 59 | 60 | // Conversions 61 | public static int FloorToInt(F32 a) { return Fixed32.FloorToInt(a.Raw); } 62 | public static int CeilToInt(F32 a) { return Fixed32.CeilToInt(a.Raw); } 63 | public static int RoundToInt(F32 a) { return Fixed32.RoundToInt(a.Raw); } 64 | public float Float { get { return Fixed32.ToFloat(Raw); } } 65 | public double Double { get { return Fixed32.ToDouble(Raw); } } 66 | 67 | // Creates the fixed point number that's a divided by b. 68 | public static F32 Ratio(int a, int b) { return F32.FromRaw((int)(((long)a << 16) / b)); } 69 | // Creates the fixed point number that's a divided by 10. 70 | public static F32 Ratio10(int a) { return F32.FromRaw((int)(((long)a << 16) / 10)); } 71 | // Creates the fixed point number that's a divided by 100. 72 | public static F32 Ratio100(int a) { return F32.FromRaw((int)(((long)a << 16) / 100)); } 73 | // Creates the fixed point number that's a divided by 1000. 74 | public static F32 Ratio1000(int a) { return F32.FromRaw((int)(((long)a << 16) / 1000)); } 75 | 76 | // Operators 77 | public static F32 operator -(F32 v1) { return FromRaw(-v1.Raw); } 78 | 79 | //public static F32 operator +(F32 v1, F32 v2) { F32 r; r.raw = v1.raw + v2.raw; return r; } 80 | public static F32 operator +(F32 v1, F32 v2) { return FromRaw(v1.Raw + v2.Raw); } 81 | public static F32 operator -(F32 v1, F32 v2) { return FromRaw(v1.Raw - v2.Raw); } 82 | public static F32 operator *(F32 v1, F32 v2) { return FromRaw(Fixed32.Mul(v1.Raw, v2.Raw)); } 83 | public static F32 operator /(F32 v1, F32 v2) { return FromRaw(Fixed32.DivPrecise(v1.Raw, v2.Raw)); } 84 | public static F32 operator %(F32 v1, F32 v2) { return FromRaw(Fixed32.Mod(v1.Raw, v2.Raw)); } 85 | 86 | public static F32 operator +(F32 v1, int v2) { return FromRaw(v1.Raw + Fixed32.FromInt(v2)); } 87 | public static F32 operator +(int v1, F32 v2) { return FromRaw(Fixed32.FromInt(v1) + v2.Raw); } 88 | public static F32 operator -(F32 v1, int v2) { return FromRaw(v1.Raw - Fixed32.FromInt(v2)); } 89 | public static F32 operator -(int v1, F32 v2) { return FromRaw(Fixed32.FromInt(v1) - v2.Raw); } 90 | public static F32 operator *(F32 v1, int v2) { return FromRaw(v1.Raw * (int)v2); } 91 | public static F32 operator *(int v1, F32 v2) { return FromRaw((int)v1 * v2.Raw); } 92 | public static F32 operator /(F32 v1, int v2) { return FromRaw(v1.Raw / (int)v2); } 93 | public static F32 operator /(int v1, F32 v2) { return FromRaw(Fixed32.DivPrecise(Fixed32.FromInt(v1), v2.Raw)); } 94 | public static F32 operator %(F32 v1, int v2) { return FromRaw(Fixed32.Mod(v1.Raw, Fixed32.FromInt(v2))); } 95 | public static F32 operator %(int v1, F32 v2) { return FromRaw(Fixed32.Mod(Fixed32.FromInt(v1), v2.Raw)); } 96 | 97 | public static F32 operator ++(F32 v1) { return FromRaw(v1.Raw + Fixed32.One); } 98 | public static F32 operator --(F32 v1) { return FromRaw(v1.Raw - Fixed32.One); } 99 | 100 | public static bool operator ==(F32 v1, F32 v2) { return v1.Raw == v2.Raw; } 101 | public static bool operator !=(F32 v1, F32 v2) { return v1.Raw != v2.Raw; } 102 | public static bool operator <(F32 v1, F32 v2) { return v1.Raw < v2.Raw; } 103 | public static bool operator <=(F32 v1, F32 v2) { return v1.Raw <= v2.Raw; } 104 | public static bool operator >(F32 v1, F32 v2) { return v1.Raw > v2.Raw; } 105 | public static bool operator >=(F32 v1, F32 v2) { return v1.Raw >= v2.Raw; } 106 | 107 | public static bool operator ==(int v1, F32 v2) { return Fixed32.FromInt(v1) == v2.Raw; } 108 | public static bool operator ==(F32 v1, int v2) { return v1.Raw == Fixed32.FromInt(v2); } 109 | public static bool operator !=(int v1, F32 v2) { return Fixed32.FromInt(v1) != v2.Raw; } 110 | public static bool operator !=(F32 v1, int v2) { return v1.Raw != Fixed32.FromInt(v2); } 111 | public static bool operator <(int v1, F32 v2) { return Fixed32.FromInt(v1) < v2.Raw; } 112 | public static bool operator <(F32 v1, int v2) { return v1.Raw < Fixed32.FromInt(v2); } 113 | public static bool operator <=(int v1, F32 v2) { return Fixed32.FromInt(v1) <= v2.Raw; } 114 | public static bool operator <=(F32 v1, int v2) { return v1.Raw <= Fixed32.FromInt(v2); } 115 | public static bool operator >(int v1, F32 v2) { return Fixed32.FromInt(v1) > v2.Raw; } 116 | public static bool operator >(F32 v1, int v2) { return v1.Raw > Fixed32.FromInt(v2); } 117 | public static bool operator >=(int v1, F32 v2) { return Fixed32.FromInt(v1) >= v2.Raw; } 118 | public static bool operator >=(F32 v1, int v2) { return v1.Raw >= Fixed32.FromInt(v2); } 119 | 120 | public static F32 RadToDeg(F32 a) { return FromRaw(Fixed32.Mul(a.Raw, 3754943)); } // 180 / F32.Pi 121 | public static F32 DegToRad(F32 a) { return FromRaw(Fixed32.Mul(a.Raw, 1143)); } // F32.Pi / 180 122 | 123 | public static F32 Div2(F32 a) { return FromRaw(a.Raw >> 1); } 124 | public static F32 Abs(F32 a) { return FromRaw(Fixed32.Abs(a.Raw)); } 125 | public static F32 Nabs(F32 a) { return FromRaw(Fixed32.Nabs(a.Raw)); } 126 | public static int Sign(F32 a) { return Fixed32.Sign(a.Raw); } 127 | public static F32 Ceil(F32 a) { return FromRaw(Fixed32.Ceil(a.Raw)); } 128 | public static F32 Floor(F32 a) { return FromRaw(Fixed32.Floor(a.Raw)); } 129 | public static F32 Round(F32 a) { return FromRaw(Fixed32.Round(a.Raw)); } 130 | public static F32 Fract(F32 a) { return FromRaw(Fixed32.Fract(a.Raw)); } 131 | public static F32 Div(F32 a, F32 b) { return FromRaw(Fixed32.Div(a.Raw, b.Raw)); } 132 | public static F32 DivFast(F32 a, F32 b) { return FromRaw(Fixed32.DivFast(a.Raw, b.Raw)); } 133 | public static F32 DivFastest(F32 a, F32 b) { return FromRaw(Fixed32.DivFastest(a.Raw, b.Raw)); } 134 | public static F32 SqrtPrecise(F32 a) { return FromRaw(Fixed32.SqrtPrecise(a.Raw)); } 135 | public static F32 Sqrt(F32 a) { return FromRaw(Fixed32.Sqrt(a.Raw)); } 136 | public static F32 SqrtFast(F32 a) { return FromRaw(Fixed32.SqrtFast(a.Raw)); } 137 | public static F32 SqrtFastest(F32 a) { return FromRaw(Fixed32.SqrtFastest(a.Raw)); } 138 | public static F32 RSqrt(F32 a) { return FromRaw(Fixed32.RSqrt(a.Raw)); } 139 | public static F32 RSqrtFast(F32 a) { return FromRaw(Fixed32.RSqrtFast(a.Raw)); } 140 | public static F32 RSqrtFastest(F32 a) { return FromRaw(Fixed32.RSqrtFastest(a.Raw)); } 141 | public static F32 Rcp(F32 a) { return FromRaw(Fixed32.Rcp(a.Raw)); } 142 | public static F32 RcpFast(F32 a) { return FromRaw(Fixed32.RcpFast(a.Raw)); } 143 | public static F32 RcpFastest(F32 a) { return FromRaw(Fixed32.RcpFastest(a.Raw)); } 144 | public static F32 Exp(F32 a) { return FromRaw(Fixed32.Exp(a.Raw)); } 145 | public static F32 ExpFast(F32 a) { return FromRaw(Fixed32.ExpFast(a.Raw)); } 146 | public static F32 ExpFastest(F32 a) { return FromRaw(Fixed32.ExpFastest(a.Raw)); } 147 | public static F32 Exp2(F32 a) { return FromRaw(Fixed32.Exp2(a.Raw)); } 148 | public static F32 Exp2Fast(F32 a) { return FromRaw(Fixed32.Exp2Fast(a.Raw)); } 149 | public static F32 Exp2Fastest(F32 a) { return FromRaw(Fixed32.Exp2Fastest(a.Raw)); } 150 | public static F32 Log(F32 a) { return FromRaw(Fixed32.Log(a.Raw)); } 151 | public static F32 LogFast(F32 a) { return FromRaw(Fixed32.LogFast(a.Raw)); } 152 | public static F32 LogFastest(F32 a) { return FromRaw(Fixed32.LogFastest(a.Raw)); } 153 | public static F32 Log2(F32 a) { return FromRaw(Fixed32.Log2(a.Raw)); } 154 | public static F32 Log2Fast(F32 a) { return FromRaw(Fixed32.Log2Fast(a.Raw)); } 155 | public static F32 Log2Fastest(F32 a) { return FromRaw(Fixed32.Log2Fastest(a.Raw)); } 156 | 157 | public static F32 Sin(F32 a) { return FromRaw(Fixed32.Sin(a.Raw)); } 158 | public static F32 SinFast(F32 a) { return FromRaw(Fixed32.SinFast(a.Raw)); } 159 | public static F32 SinFastest(F32 a) { return FromRaw(Fixed32.SinFastest(a.Raw)); } 160 | public static F32 Cos(F32 a) { return FromRaw(Fixed32.Cos(a.Raw)); } 161 | public static F32 CosFast(F32 a) { return FromRaw(Fixed32.CosFast(a.Raw)); } 162 | public static F32 CosFastest(F32 a) { return FromRaw(Fixed32.CosFastest(a.Raw)); } 163 | public static F32 Tan(F32 a) { return FromRaw(Fixed32.Tan(a.Raw)); } 164 | public static F32 TanFast(F32 a) { return FromRaw(Fixed32.TanFast(a.Raw)); } 165 | public static F32 TanFastest(F32 a) { return FromRaw(Fixed32.TanFastest(a.Raw)); } 166 | public static F32 Asin(F32 a) { return FromRaw(Fixed32.Asin(a.Raw)); } 167 | public static F32 AsinFast(F32 a) { return FromRaw(Fixed32.AsinFast(a.Raw)); } 168 | public static F32 AsinFastest(F32 a) { return FromRaw(Fixed32.AsinFastest(a.Raw)); } 169 | public static F32 Acos(F32 a) { return FromRaw(Fixed32.Acos(a.Raw)); } 170 | public static F32 AcosFast(F32 a) { return FromRaw(Fixed32.AcosFast(a.Raw)); } 171 | public static F32 AcosFastest(F32 a) { return FromRaw(Fixed32.AcosFastest(a.Raw)); } 172 | public static F32 Atan(F32 a) { return FromRaw(Fixed32.Atan(a.Raw)); } 173 | public static F32 AtanFast(F32 a) { return FromRaw(Fixed32.AtanFast(a.Raw)); } 174 | public static F32 AtanFastest(F32 a) { return FromRaw(Fixed32.AtanFastest(a.Raw)); } 175 | public static F32 Atan2(F32 y, F32 x) { return FromRaw(Fixed32.Atan2(y.Raw, x.Raw)); } 176 | public static F32 Atan2Fast(F32 y, F32 x) { return FromRaw(Fixed32.Atan2Fast(y.Raw, x.Raw)); } 177 | public static F32 Atan2Fastest(F32 y, F32 x) { return FromRaw(Fixed32.Atan2Fastest(y.Raw, x.Raw)); } 178 | public static F32 Pow(F32 a, F32 b) { return FromRaw(Fixed32.Pow(a.Raw, b.Raw)); } 179 | public static F32 PowFast(F32 a, F32 b) { return FromRaw(Fixed32.PowFast(a.Raw, b.Raw)); } 180 | public static F32 PowFastest(F32 a, F32 b) { return FromRaw(Fixed32.PowFastest(a.Raw, b.Raw)); } 181 | 182 | public static F32 Min(F32 a, F32 b) { return FromRaw(Fixed32.Min(a.Raw, b.Raw)); } 183 | public static F32 Max(F32 a, F32 b) { return FromRaw(Fixed32.Max(a.Raw, b.Raw)); } 184 | public static F32 Clamp(F32 a, F32 min, F32 max) { return FromRaw(Fixed32.Clamp(a.Raw, min.Raw, max.Raw)); } 185 | public static F32 Clamp01(F32 a) { return FromRaw(Fixed32.Clamp(a.Raw, Fixed32.Zero, Fixed32.One)); } 186 | 187 | public static F32 Lerp(F32 a, F32 b, F32 t) 188 | { 189 | int tb = t.Raw; 190 | int ta = Fixed32.One - tb; 191 | return FromRaw(Fixed32.Mul(a.Raw, ta) + Fixed32.Mul(b.Raw, tb)); 192 | } 193 | 194 | public bool Equals(F32 other) 195 | { 196 | return (Raw == other.Raw); 197 | } 198 | 199 | public override bool Equals(object obj) 200 | { 201 | if (!(obj is F32)) 202 | return false; 203 | return ((F32)obj).Raw == Raw; 204 | } 205 | 206 | public int CompareTo(F32 other) 207 | { 208 | if (Raw < other.Raw) return -1; 209 | if (Raw > other.Raw) return +1; 210 | return 0; 211 | } 212 | 213 | public override string ToString() 214 | { 215 | return Fixed32.ToString(Raw); 216 | } 217 | 218 | public override int GetHashCode() 219 | { 220 | return Raw; 221 | } 222 | 223 | int IComparable.CompareTo(object obj) 224 | { 225 | if (obj is F32 other) 226 | return CompareTo(other); 227 | else if (obj is null) 228 | return 1; 229 | // don't allow comparisons with other numeric or non-numeric types. 230 | throw new ArgumentException("F32 can only be compared against another F32."); 231 | } 232 | } 233 | } 234 | -------------------------------------------------------------------------------- /Examples/FixMath/F32Vec2.cs: -------------------------------------------------------------------------------- 1 | // 2 | // FixPointCS 3 | // 4 | // Copyright(c) Jere Sanisalo, Petri Kero 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | using FixPointCS; 25 | using System; 26 | using System.Runtime.CompilerServices; 27 | 28 | namespace FixMath 29 | { 30 | /// 31 | /// Vector2 struct with signed 16.16 fixed point components. 32 | /// 33 | [Serializable] 34 | public struct F32Vec2 : IEquatable 35 | { 36 | // Constants 37 | public static F32Vec2 Zero { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.Zero, Fixed32.Zero); } } 38 | public static F32Vec2 One { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.One, Fixed32.One); } } 39 | public static F32Vec2 Half { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.Half, Fixed32.Half); } } 40 | public static F32Vec2 Down { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.Zero, Fixed32.Neg1); } } 41 | public static F32Vec2 Up { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.Zero, Fixed32.One); } } 42 | public static F32Vec2 Left { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.Neg1, Fixed32.Zero); } } 43 | public static F32Vec2 Right { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.One, Fixed32.Zero); } } 44 | public static F32Vec2 AxisX { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.One, Fixed32.Zero); } } 45 | public static F32Vec2 AxisY { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec2(Fixed32.Zero, Fixed32.One); } } 46 | 47 | // Raw components 48 | public int RawX; 49 | public int RawY; 50 | 51 | // F32 accessors 52 | public F32 X { get { return F32.FromRaw(RawX); } set { RawX = value.Raw; } } 53 | public F32 Y { get { return F32.FromRaw(RawY); } set { RawY = value.Raw; } } 54 | 55 | public F32Vec2(F32 x, F32 y) 56 | { 57 | RawX = x.Raw; 58 | RawY = y.Raw; 59 | } 60 | 61 | // raw ctor only for internal usage 62 | private F32Vec2(int x, int y) 63 | { 64 | RawX = x; 65 | RawY = y; 66 | } 67 | 68 | public static F32Vec2 FromRaw(int rawX, int rawY) { return new F32Vec2(rawX, rawY); } 69 | public static F32Vec2 FromInt(int x, int y) { return new F32Vec2(Fixed32.FromInt(x), Fixed32.FromInt(y)); } 70 | public static F32Vec2 FromFloat(float x, float y) { return new F32Vec2(Fixed32.FromFloat(x), Fixed32.FromFloat(y)); } 71 | public static F32Vec2 FromDouble(double x, double y) { return new F32Vec2(Fixed32.FromDouble(x), Fixed32.FromDouble(y)); } 72 | 73 | public static F32Vec2 operator -(F32Vec2 a) { return new F32Vec2(-a.RawX, -a.RawY); } 74 | public static F32Vec2 operator +(F32Vec2 a, F32Vec2 b) { return new F32Vec2(a.RawX + b.RawX, a.RawY + b.RawY); } 75 | public static F32Vec2 operator -(F32Vec2 a, F32Vec2 b) { return new F32Vec2(a.RawX - b.RawX, a.RawY - b.RawY); } 76 | public static F32Vec2 operator *(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.Mul(a.RawX, b.RawX), Fixed32.Mul(a.RawY, b.RawY)); } 77 | public static F32Vec2 operator /(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.DivPrecise(a.RawX, b.RawX), Fixed32.DivPrecise(a.RawY, b.RawY)); } 78 | public static F32Vec2 operator %(F32Vec2 a, F32Vec2 b) { return new F32Vec2(a.RawX % b.RawX, a.RawY % b.RawY); } 79 | 80 | public static F32Vec2 operator +(F32 a, F32Vec2 b) { return new F32Vec2(a.Raw + b.RawX, a.Raw + b.RawY); } 81 | public static F32Vec2 operator +(F32Vec2 a, F32 b) { return new F32Vec2(a.RawX + b.Raw, a.RawY + b.Raw); } 82 | public static F32Vec2 operator -(F32 a, F32Vec2 b) { return new F32Vec2(a.Raw - b.RawX, a.Raw - b.RawY); } 83 | public static F32Vec2 operator -(F32Vec2 a, F32 b) { return new F32Vec2(a.RawX - b.Raw, a.RawY - b.Raw); } 84 | public static F32Vec2 operator *(F32 a, F32Vec2 b) { return new F32Vec2(Fixed32.Mul(a.Raw, b.RawX), Fixed32.Mul(a.Raw, b.RawY)); } 85 | public static F32Vec2 operator *(F32Vec2 a, F32 b) { return new F32Vec2(Fixed32.Mul(a.RawX, b.Raw), Fixed32.Mul(a.RawY, b.Raw)); } 86 | public static F32Vec2 operator /(F32 a, F32Vec2 b) { return new F32Vec2(Fixed32.DivPrecise(a.Raw, b.RawX), Fixed32.DivPrecise(a.Raw, b.RawY)); } 87 | public static F32Vec2 operator /(F32Vec2 a, F32 b) { return new F32Vec2(Fixed32.DivPrecise(a.RawX, b.Raw), Fixed32.DivPrecise(a.RawY, b.Raw)); } 88 | public static F32Vec2 operator %(F32 a, F32Vec2 b) { return new F32Vec2(a.Raw % b.RawX, a.Raw % b.RawY); } 89 | public static F32Vec2 operator %(F32Vec2 a, F32 b) { return new F32Vec2(a.RawX % b.Raw, a.RawY % b.Raw); } 90 | 91 | public static bool operator ==(F32Vec2 a, F32Vec2 b) { return a.RawX == b.RawX && a.RawY == b.RawY; } 92 | public static bool operator !=(F32Vec2 a, F32Vec2 b) { return a.RawX != b.RawX || a.RawY != b.RawY; } 93 | 94 | public static F32Vec2 Div(F32Vec2 a, F32 b) { int oob = Fixed32.Rcp(b.Raw); return new F32Vec2(Fixed32.Mul(a.RawX, oob), Fixed32.Mul(a.RawY, oob)); } 95 | public static F32Vec2 DivFast(F32Vec2 a, F32 b) { int oob = Fixed32.RcpFast(b.Raw); return new F32Vec2(Fixed32.Mul(a.RawX, oob), Fixed32.Mul(a.RawY, oob)); } 96 | public static F32Vec2 DivFastest(F32Vec2 a, F32 b) { int oob = Fixed32.RcpFastest(b.Raw); return new F32Vec2(Fixed32.Mul(a.RawX, oob), Fixed32.Mul(a.RawY, oob)); } 97 | public static F32Vec2 Div(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.Div(a.RawX, b.RawX), Fixed32.Div(a.RawY, b.RawY)); } 98 | public static F32Vec2 DivFast(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.DivFast(a.RawX, b.RawX), Fixed32.DivFast(a.RawY, b.RawY)); } 99 | public static F32Vec2 DivFastest(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.DivFastest(a.RawX, b.RawX), Fixed32.DivFastest(a.RawY, b.RawY)); } 100 | public static F32Vec2 SqrtPrecise(F32Vec2 a) { return new F32Vec2(Fixed32.SqrtPrecise(a.RawX), Fixed32.SqrtPrecise(a.RawY)); } 101 | public static F32Vec2 Sqrt(F32Vec2 a) { return new F32Vec2(Fixed32.Sqrt(a.RawX), Fixed32.Sqrt(a.RawY)); } 102 | public static F32Vec2 SqrtFast(F32Vec2 a) { return new F32Vec2(Fixed32.SqrtFast(a.RawX), Fixed32.SqrtFast(a.RawY)); } 103 | public static F32Vec2 SqrtFastest(F32Vec2 a) { return new F32Vec2(Fixed32.SqrtFastest(a.RawX), Fixed32.SqrtFastest(a.RawY)); } 104 | public static F32Vec2 RSqrt(F32Vec2 a) { return new F32Vec2(Fixed32.RSqrt(a.RawX), Fixed32.RSqrt(a.RawY)); } 105 | public static F32Vec2 RSqrtFast(F32Vec2 a) { return new F32Vec2(Fixed32.RSqrtFast(a.RawX), Fixed32.RSqrtFast(a.RawY)); } 106 | public static F32Vec2 RSqrtFastest(F32Vec2 a) { return new F32Vec2(Fixed32.RSqrtFastest(a.RawX), Fixed32.RSqrtFastest(a.RawY)); } 107 | public static F32Vec2 Rcp(F32Vec2 a) { return new F32Vec2(Fixed32.Rcp(a.RawX), Fixed32.Rcp(a.RawY)); } 108 | public static F32Vec2 RcpFast(F32Vec2 a) { return new F32Vec2(Fixed32.RcpFast(a.RawX), Fixed32.RcpFast(a.RawY)); } 109 | public static F32Vec2 RcpFastest(F32Vec2 a) { return new F32Vec2(Fixed32.RcpFastest(a.RawX), Fixed32.RcpFastest(a.RawY)); } 110 | public static F32Vec2 Exp(F32Vec2 a) { return new F32Vec2(Fixed32.Exp(a.RawX), Fixed32.Exp(a.RawY)); } 111 | public static F32Vec2 ExpFast(F32Vec2 a) { return new F32Vec2(Fixed32.ExpFast(a.RawX), Fixed32.ExpFast(a.RawY)); } 112 | public static F32Vec2 ExpFastest(F32Vec2 a) { return new F32Vec2(Fixed32.ExpFastest(a.RawX), Fixed32.ExpFastest(a.RawY)); } 113 | public static F32Vec2 Exp2(F32Vec2 a) { return new F32Vec2(Fixed32.Exp2(a.RawX), Fixed32.Exp2(a.RawY)); } 114 | public static F32Vec2 Exp2Fast(F32Vec2 a) { return new F32Vec2(Fixed32.Exp2Fast(a.RawX), Fixed32.Exp2Fast(a.RawY)); } 115 | public static F32Vec2 Exp2Fastest(F32Vec2 a) { return new F32Vec2(Fixed32.Exp2Fastest(a.RawX), Fixed32.Exp2Fastest(a.RawY)); } 116 | public static F32Vec2 Log(F32Vec2 a) { return new F32Vec2(Fixed32.Log(a.RawX), Fixed32.Log(a.RawY)); } 117 | public static F32Vec2 LogFast(F32Vec2 a) { return new F32Vec2(Fixed32.LogFast(a.RawX), Fixed32.LogFast(a.RawY)); } 118 | public static F32Vec2 LogFastest(F32Vec2 a) { return new F32Vec2(Fixed32.LogFastest(a.RawX), Fixed32.LogFastest(a.RawY)); } 119 | public static F32Vec2 Log2(F32Vec2 a) { return new F32Vec2(Fixed32.Log2(a.RawX), Fixed32.Log2(a.RawY)); } 120 | public static F32Vec2 Log2Fast(F32Vec2 a) { return new F32Vec2(Fixed32.Log2Fast(a.RawX), Fixed32.Log2Fast(a.RawY)); } 121 | public static F32Vec2 Log2Fastest(F32Vec2 a) { return new F32Vec2(Fixed32.Log2Fastest(a.RawX), Fixed32.Log2Fastest(a.RawY)); } 122 | public static F32Vec2 Sin(F32Vec2 a) { return new F32Vec2(Fixed32.Sin(a.RawX), Fixed32.Sin(a.RawY)); } 123 | public static F32Vec2 SinFast(F32Vec2 a) { return new F32Vec2(Fixed32.SinFast(a.RawX), Fixed32.SinFast(a.RawY)); } 124 | public static F32Vec2 SinFastest(F32Vec2 a) { return new F32Vec2(Fixed32.SinFastest(a.RawX), Fixed32.SinFastest(a.RawY)); } 125 | public static F32Vec2 Cos(F32Vec2 a) { return new F32Vec2(Fixed32.Cos(a.RawX), Fixed32.Cos(a.RawY)); } 126 | public static F32Vec2 CosFast(F32Vec2 a) { return new F32Vec2(Fixed32.CosFast(a.RawX), Fixed32.CosFast(a.RawY)); } 127 | public static F32Vec2 CosFastest(F32Vec2 a) { return new F32Vec2(Fixed32.CosFastest(a.RawX), Fixed32.CosFastest(a.RawY)); } 128 | 129 | public static F32Vec2 Pow(F32Vec2 a, F32 b) { return new F32Vec2(Fixed32.Pow(a.RawX, b.Raw), Fixed32.Pow(a.RawY, b.Raw)); } 130 | public static F32Vec2 PowFast(F32Vec2 a, F32 b) { return new F32Vec2(Fixed32.PowFast(a.RawX, b.Raw), Fixed32.PowFast(a.RawY, b.Raw)); } 131 | public static F32Vec2 PowFastest(F32Vec2 a, F32 b) { return new F32Vec2(Fixed32.PowFastest(a.RawX, b.Raw), Fixed32.PowFastest(a.RawY, b.Raw)); } 132 | public static F32Vec2 Pow(F32 a, F32Vec2 b) { return new F32Vec2(Fixed32.Pow(a.Raw, b.RawX), Fixed32.Pow(a.Raw, b.RawY)); } 133 | public static F32Vec2 PowFast(F32 a, F32Vec2 b) { return new F32Vec2(Fixed32.PowFast(a.Raw, b.RawX), Fixed32.PowFast(a.Raw, b.RawY)); } 134 | public static F32Vec2 PowFastest(F32 a, F32Vec2 b) { return new F32Vec2(Fixed32.PowFastest(a.Raw, b.RawX), Fixed32.PowFastest(a.Raw, b.RawY)); } 135 | public static F32Vec2 Pow(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.Pow(a.RawX, b.RawX), Fixed32.Pow(a.RawY, b.RawY)); } 136 | public static F32Vec2 PowFast(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.PowFast(a.RawX, b.RawX), Fixed32.PowFast(a.RawY, b.RawY)); } 137 | public static F32Vec2 PowFastest(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.PowFastest(a.RawX, b.RawX), Fixed32.PowFastest(a.RawY, b.RawY)); } 138 | 139 | public static F32 Length(F32Vec2 a) { return F32.FromRaw((int)(Fixed64.Sqrt((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY) >> 16)); } 140 | public static F32 LengthFast(F32Vec2 a) { return F32.FromRaw((int)(Fixed64.SqrtFast((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY) >> 16)); } 141 | public static F32 LengthFastest(F32Vec2 a) { return F32.FromRaw((int)(Fixed64.SqrtFastest((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY) >> 16)); } 142 | public static F64 LengthSqr(F32Vec2 a) { return F64.FromRaw((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY); } 143 | public static F32Vec2 Normalize(F32Vec2 a) { F32 ooLen = F32.FromRaw((int)(Fixed64.RSqrt((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY) >> 16)); return ooLen * a; } 144 | public static F32Vec2 NormalizeFast(F32Vec2 a) { F32 ooLen = F32.FromRaw((int)(Fixed64.RSqrtFast((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY) >> 16)); return ooLen * a; } 145 | public static F32Vec2 NormalizeFastest(F32Vec2 a) { F32 ooLen = F32.FromRaw((int)(Fixed64.RSqrtFastest((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY) >> 16)); return ooLen * a; } 146 | 147 | public static F32 Dot(F32Vec2 a, F32Vec2 b) { return F32.FromRaw(Fixed32.Mul(a.RawX, b.RawX) + Fixed32.Mul(a.RawY, b.RawY)); } 148 | public static F32 Distance(F32Vec2 a, F32Vec2 b) { return Length(a - b); } 149 | public static F32 DistanceFast(F32Vec2 a, F32Vec2 b) { return LengthFast(a - b); } 150 | public static F32 DistanceFastest(F32Vec2 a, F32Vec2 b) { return LengthFastest(a - b); } 151 | 152 | public static F32Vec2 Min(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.Min(a.RawX, b.RawX), Fixed32.Min(a.RawY, b.RawY)); } 153 | public static F32Vec2 Max(F32Vec2 a, F32Vec2 b) { return new F32Vec2(Fixed32.Max(a.RawX, b.RawX), Fixed32.Max(a.RawY, b.RawY)); } 154 | 155 | public static F32Vec2 Clamp(F32Vec2 a, F32 min, F32 max) 156 | { 157 | return new F32Vec2( 158 | Fixed32.Clamp(a.RawX, min.Raw, max.Raw), 159 | Fixed32.Clamp(a.RawY, min.Raw, max.Raw)); 160 | } 161 | 162 | public static F32Vec2 Clamp(F32Vec2 a, F32Vec2 min, F32Vec2 max) 163 | { 164 | return new F32Vec2( 165 | Fixed32.Clamp(a.RawX, min.RawX, max.RawX), 166 | Fixed32.Clamp(a.RawY, min.RawY, max.RawY)); 167 | } 168 | 169 | public static F32Vec2 Lerp(F32Vec2 a, F32Vec2 b, F32 t) 170 | { 171 | int tb = t.Raw; 172 | int ta = Fixed32.One - tb; 173 | return new F32Vec2( 174 | Fixed32.Mul(a.RawX, ta) + Fixed32.Mul(b.RawX, tb), 175 | Fixed32.Mul(a.RawY, ta) + Fixed32.Mul(b.RawY, tb)); 176 | } 177 | 178 | public bool Equals(F32Vec2 other) 179 | { 180 | return (this == other); 181 | } 182 | 183 | public override bool Equals(object obj) 184 | { 185 | if (!(obj is F32Vec2)) 186 | return false; 187 | return ((F32Vec2)obj) == this; 188 | } 189 | 190 | public override string ToString() 191 | { 192 | return "(" + Fixed32.ToString(RawX) + ", " + Fixed32.ToString(RawY) + ")"; 193 | } 194 | 195 | public override int GetHashCode() 196 | { 197 | return RawX.GetHashCode() ^ RawY.GetHashCode() * 7919; 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /Examples/FixMath/F32Vec3.cs: -------------------------------------------------------------------------------- 1 | // 2 | // FixPointCS 3 | // 4 | // Copyright(c) Jere Sanisalo, Petri Kero 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | using FixPointCS; 25 | using System; 26 | using System.Runtime.CompilerServices; 27 | 28 | namespace FixMath 29 | { 30 | /// 31 | /// Vector3 struct with signed 16.16 fixed point components. 32 | /// 33 | [Serializable] 34 | public struct F32Vec3 : IEquatable 35 | { 36 | // Constants 37 | public static F32Vec3 Zero { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.Zero, Fixed32.Zero); } } 38 | public static F32Vec3 One { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.One, Fixed32.One, Fixed32.One); } } 39 | public static F32Vec3 Down { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.Neg1, Fixed32.Zero); } } 40 | public static F32Vec3 Up { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.One, Fixed32.Zero); } } 41 | public static F32Vec3 Left { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Neg1, Fixed32.Zero, Fixed32.Zero); } } 42 | public static F32Vec3 Right { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.One, Fixed32.Zero, Fixed32.Zero); } } 43 | public static F32Vec3 Forward { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.Zero, Fixed32.One); } } 44 | public static F32Vec3 Back { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.Zero, Fixed32.Neg1); } } 45 | public static F32Vec3 AxisX { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.One, Fixed32.Zero, Fixed32.Zero); } } 46 | public static F32Vec3 AxisY { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.One, Fixed32.Zero); } } 47 | public static F32Vec3 AxisZ { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F32Vec3(Fixed32.Zero, Fixed32.Zero, Fixed32.One); } } 48 | 49 | // Raw components 50 | public int RawX; 51 | public int RawY; 52 | public int RawZ; 53 | 54 | // F32 accessors 55 | public F32 X { get { return F32.FromRaw(RawX); } set { RawX = value.Raw; } } 56 | public F32 Y { get { return F32.FromRaw(RawY); } set { RawY = value.Raw; } } 57 | public F32 Z { get { return F32.FromRaw(RawZ); } set { RawZ = value.Raw; } } 58 | 59 | public F32Vec3(F32 x, F32 y, F32 z) 60 | { 61 | RawX = x.Raw; 62 | RawY = y.Raw; 63 | RawZ = z.Raw; 64 | } 65 | 66 | // raw ctor only for internal usage 67 | private F32Vec3(int x, int y, int z) 68 | { 69 | RawX = x; 70 | RawY = y; 71 | RawZ = z; 72 | } 73 | 74 | public static F32Vec3 FromRaw(int rawX, int rawY, int rawZ) { return new F32Vec3(rawX, rawY, rawZ); } 75 | public static F32Vec3 FromInt(int x, int y, int z) { return new F32Vec3(F32.FromInt(x), F32.FromInt(y), F32.FromInt(z)); } 76 | public static F32Vec3 FromFloat(float x, float y, float z) { return new F32Vec3(F32.FromFloat(x), F32.FromFloat(y), F32.FromFloat(z)); } 77 | public static F32Vec3 FromDouble(double x, double y, double z) { return new F32Vec3(F32.FromDouble(x), F32.FromDouble(y), F32.FromDouble(z)); } 78 | 79 | public static F32Vec3 operator -(F32Vec3 a) { return new F32Vec3(-a.RawX, -a.RawY, -a.RawZ); } 80 | public static F32Vec3 operator +(F32Vec3 a, F32Vec3 b) { return new F32Vec3(a.RawX + b.RawX, a.RawY + b.RawY, a.RawZ + b.RawZ); } 81 | public static F32Vec3 operator -(F32Vec3 a, F32Vec3 b) { return new F32Vec3(a.RawX - b.RawX, a.RawY - b.RawY, a.RawZ - b.RawZ); } 82 | public static F32Vec3 operator *(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.Mul(a.RawX, b.RawX), Fixed32.Mul(a.RawY, b.RawY), Fixed32.Mul(a.RawZ, b.RawZ)); } 83 | public static F32Vec3 operator /(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.DivPrecise(a.RawX, b.RawX), Fixed32.DivPrecise(a.RawY, b.RawY), Fixed32.DivPrecise(a.RawZ, b.RawZ)); } 84 | public static F32Vec3 operator %(F32Vec3 a, F32Vec3 b) { return new F32Vec3(a.RawX % b.RawX, a.RawY % b.RawY, a.RawZ % b.RawZ); } 85 | 86 | public static F32Vec3 operator +(F32 a, F32Vec3 b) { return new F32Vec3(a.Raw + b.RawX, a.Raw + b.RawY, a.Raw + b.RawZ); } 87 | public static F32Vec3 operator +(F32Vec3 a, F32 b) { return new F32Vec3(a.RawX + b.Raw, a.RawY + b.Raw, a.RawZ + b.Raw); } 88 | public static F32Vec3 operator -(F32 a, F32Vec3 b) { return new F32Vec3(a.Raw - b.RawX, a.Raw - b.RawY, a.Raw - b.RawZ); } 89 | public static F32Vec3 operator -(F32Vec3 a, F32 b) { return new F32Vec3(a.RawX - b.Raw, a.RawY - b.Raw, a.RawZ - b.Raw); } 90 | public static F32Vec3 operator *(F32 a, F32Vec3 b) { return new F32Vec3(Fixed32.Mul(a.Raw, b.RawX), Fixed32.Mul(a.Raw, b.RawY), Fixed32.Mul(a.Raw, b.RawZ)); } 91 | public static F32Vec3 operator *(F32Vec3 a, F32 b) { return new F32Vec3(Fixed32.Mul(a.RawX, b.Raw), Fixed32.Mul(a.RawY, b.Raw), Fixed32.Mul(a.RawZ, b.Raw)); } 92 | public static F32Vec3 operator /(F32 a, F32Vec3 b) { return new F32Vec3(Fixed32.DivPrecise(a.Raw, b.RawX), Fixed32.DivPrecise(a.Raw, b.RawY), Fixed32.DivPrecise(a.Raw, b.RawZ)); } 93 | public static F32Vec3 operator /(F32Vec3 a, F32 b) { return new F32Vec3(Fixed32.DivPrecise(a.RawX, b.Raw), Fixed32.DivPrecise(a.RawY, b.Raw), Fixed32.DivPrecise(a.RawZ, b.Raw)); } 94 | public static F32Vec3 operator %(F32 a, F32Vec3 b) { return new F32Vec3(a.Raw % b.RawX, a.Raw % b.RawY, a.Raw % b.RawZ); } 95 | public static F32Vec3 operator %(F32Vec3 a, F32 b) { return new F32Vec3(a.RawX % b.Raw, a.RawY % b.Raw, a.RawZ % b.Raw); } 96 | 97 | public static bool operator ==(F32Vec3 a, F32Vec3 b) { return a.RawX == b.RawX && a.RawY == b.RawY && a.RawZ == b.RawZ; } 98 | public static bool operator !=(F32Vec3 a, F32Vec3 b) { return a.RawX != b.RawX || a.RawY != b.RawY || a.RawZ != b.RawZ; } 99 | 100 | public static F32Vec3 Div(F32Vec3 a, F32 b) { int oob = Fixed32.Rcp(b.Raw); return new F32Vec3(Fixed32.Mul(a.RawX, oob), Fixed32.Mul(a.RawY, oob), Fixed32.Mul(a.RawZ, oob)); } 101 | public static F32Vec3 DivFast(F32Vec3 a, F32 b) { int oob = Fixed32.RcpFast(b.Raw); return new F32Vec3(Fixed32.Mul(a.RawX, oob), Fixed32.Mul(a.RawY, oob), Fixed32.Mul(a.RawZ, oob)); } 102 | public static F32Vec3 DivFastest(F32Vec3 a, F32 b) { int oob = Fixed32.RcpFastest(b.Raw); return new F32Vec3(Fixed32.Mul(a.RawX, oob), Fixed32.Mul(a.RawY, oob), Fixed32.Mul(a.RawZ, oob)); } 103 | public static F32Vec3 Div(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.Div(a.RawX, b.RawX), Fixed32.Div(a.RawY, b.RawY), Fixed32.Div(a.RawZ, b.RawZ)); } 104 | public static F32Vec3 DivFast(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.DivFast(a.RawX, b.RawX), Fixed32.DivFast(a.RawY, b.RawY), Fixed32.DivFast(a.RawZ, b.RawZ)); } 105 | public static F32Vec3 DivFastest(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.DivFastest(a.RawX, b.RawX), Fixed32.DivFastest(a.RawY, b.RawY), Fixed32.DivFastest(a.RawZ, b.RawZ)); } 106 | public static F32Vec3 SqrtPrecise(F32Vec3 a) { return new F32Vec3(Fixed32.SqrtPrecise(a.RawX), Fixed32.SqrtPrecise(a.RawY), Fixed32.SqrtPrecise(a.RawZ)); } 107 | public static F32Vec3 Sqrt(F32Vec3 a) { return new F32Vec3(Fixed32.Sqrt(a.RawX), Fixed32.Sqrt(a.RawY), Fixed32.Sqrt(a.RawZ)); } 108 | public static F32Vec3 SqrtFast(F32Vec3 a) { return new F32Vec3(Fixed32.SqrtFast(a.RawX), Fixed32.SqrtFast(a.RawY), Fixed32.SqrtFast(a.RawZ)); } 109 | public static F32Vec3 SqrtFastest(F32Vec3 a) { return new F32Vec3(Fixed32.SqrtFastest(a.RawX), Fixed32.SqrtFastest(a.RawY), Fixed32.SqrtFastest(a.RawZ)); } 110 | public static F32Vec3 RSqrt(F32Vec3 a) { return new F32Vec3(Fixed32.RSqrt(a.RawX), Fixed32.RSqrt(a.RawY), Fixed32.RSqrt(a.RawZ)); } 111 | public static F32Vec3 RSqrtFast(F32Vec3 a) { return new F32Vec3(Fixed32.RSqrtFast(a.RawX), Fixed32.RSqrtFast(a.RawY), Fixed32.RSqrtFast(a.RawZ)); } 112 | public static F32Vec3 RSqrtFastest(F32Vec3 a) { return new F32Vec3(Fixed32.RSqrtFastest(a.RawX), Fixed32.RSqrtFastest(a.RawY), Fixed32.RSqrtFastest(a.RawZ)); } 113 | public static F32Vec3 Rcp(F32Vec3 a) { return new F32Vec3(Fixed32.Rcp(a.RawX), Fixed32.Rcp(a.RawY), Fixed32.Rcp(a.RawZ)); } 114 | public static F32Vec3 RcpFast(F32Vec3 a) { return new F32Vec3(Fixed32.RcpFast(a.RawX), Fixed32.RcpFast(a.RawY), Fixed32.RcpFast(a.RawZ)); } 115 | public static F32Vec3 RcpFastest(F32Vec3 a) { return new F32Vec3(Fixed32.RcpFastest(a.RawX), Fixed32.RcpFastest(a.RawY), Fixed32.RcpFastest(a.RawZ)); } 116 | public static F32Vec3 Exp(F32Vec3 a) { return new F32Vec3(Fixed32.Exp(a.RawX), Fixed32.Exp(a.RawY), Fixed32.Exp(a.RawZ)); } 117 | public static F32Vec3 ExpFast(F32Vec3 a) { return new F32Vec3(Fixed32.ExpFast(a.RawX), Fixed32.ExpFast(a.RawY), Fixed32.ExpFast(a.RawZ)); } 118 | public static F32Vec3 ExpFastest(F32Vec3 a) { return new F32Vec3(Fixed32.ExpFastest(a.RawX), Fixed32.ExpFastest(a.RawY), Fixed32.ExpFastest(a.RawZ)); } 119 | public static F32Vec3 Exp2(F32Vec3 a) { return new F32Vec3(Fixed32.Exp2(a.RawX), Fixed32.Exp2(a.RawY), Fixed32.Exp2(a.RawZ)); } 120 | public static F32Vec3 Exp2Fast(F32Vec3 a) { return new F32Vec3(Fixed32.Exp2Fast(a.RawX), Fixed32.Exp2Fast(a.RawY), Fixed32.Exp2Fast(a.RawZ)); } 121 | public static F32Vec3 Exp2Fastest(F32Vec3 a) { return new F32Vec3(Fixed32.Exp2Fastest(a.RawX), Fixed32.Exp2Fastest(a.RawY), Fixed32.Exp2Fastest(a.RawZ)); } 122 | public static F32Vec3 Log(F32Vec3 a) { return new F32Vec3(Fixed32.Log(a.RawX), Fixed32.Log(a.RawY), Fixed32.Log(a.RawZ)); } 123 | public static F32Vec3 LogFast(F32Vec3 a) { return new F32Vec3(Fixed32.LogFast(a.RawX), Fixed32.LogFast(a.RawY), Fixed32.LogFast(a.RawZ)); } 124 | public static F32Vec3 LogFastest(F32Vec3 a) { return new F32Vec3(Fixed32.LogFastest(a.RawX), Fixed32.LogFastest(a.RawY), Fixed32.LogFastest(a.RawZ)); } 125 | public static F32Vec3 Log2(F32Vec3 a) { return new F32Vec3(Fixed32.Log2(a.RawX), Fixed32.Log2(a.RawY), Fixed32.Log2(a.RawZ)); } 126 | public static F32Vec3 Log2Fast(F32Vec3 a) { return new F32Vec3(Fixed32.Log2Fast(a.RawX), Fixed32.Log2Fast(a.RawY), Fixed32.Log2Fast(a.RawZ)); } 127 | public static F32Vec3 Log2Fastest(F32Vec3 a) { return new F32Vec3(Fixed32.Log2Fastest(a.RawX), Fixed32.Log2Fastest(a.RawY), Fixed32.Log2Fastest(a.RawZ)); } 128 | public static F32Vec3 Sin(F32Vec3 a) { return new F32Vec3(Fixed32.Sin(a.RawX), Fixed32.Sin(a.RawY), Fixed32.Sin(a.RawZ)); } 129 | public static F32Vec3 SinFast(F32Vec3 a) { return new F32Vec3(Fixed32.SinFast(a.RawX), Fixed32.SinFast(a.RawY), Fixed32.SinFast(a.RawZ)); } 130 | public static F32Vec3 SinFastest(F32Vec3 a) { return new F32Vec3(Fixed32.SinFastest(a.RawX), Fixed32.SinFastest(a.RawY), Fixed32.SinFastest(a.RawZ)); } 131 | public static F32Vec3 Cos(F32Vec3 a) { return new F32Vec3(Fixed32.Cos(a.RawX), Fixed32.Cos(a.RawY), Fixed32.Cos(a.RawZ)); } 132 | public static F32Vec3 CosFast(F32Vec3 a) { return new F32Vec3(Fixed32.CosFast(a.RawX), Fixed32.CosFast(a.RawY), Fixed32.CosFast(a.RawZ)); } 133 | public static F32Vec3 CosFastest(F32Vec3 a) { return new F32Vec3(Fixed32.CosFastest(a.RawX), Fixed32.CosFastest(a.RawY), Fixed32.CosFastest(a.RawZ)); } 134 | 135 | public static F32Vec3 Pow(F32Vec3 a, F32 b) { return new F32Vec3(Fixed32.Pow(a.RawX, b.Raw), Fixed32.Pow(a.RawY, b.Raw), Fixed32.Pow(a.RawZ, b.Raw)); } 136 | public static F32Vec3 PowFast(F32Vec3 a, F32 b) { return new F32Vec3(Fixed32.PowFast(a.RawX, b.Raw), Fixed32.PowFast(a.RawY, b.Raw), Fixed32.PowFast(a.RawZ, b.Raw)); } 137 | public static F32Vec3 PowFastest(F32Vec3 a, F32 b) { return new F32Vec3(Fixed32.PowFastest(a.RawX, b.Raw), Fixed32.PowFastest(a.RawY, b.Raw), Fixed32.PowFastest(a.RawZ, b.Raw)); } 138 | public static F32Vec3 Pow(F32 a, F32Vec3 b) { return new F32Vec3(Fixed32.Pow(a.Raw, b.RawX), Fixed32.Pow(a.Raw, b.RawY), Fixed32.Pow(a.Raw, b.RawZ)); } 139 | public static F32Vec3 PowFast(F32 a, F32Vec3 b) { return new F32Vec3(Fixed32.PowFast(a.Raw, b.RawX), Fixed32.PowFast(a.Raw, b.RawY), Fixed32.PowFast(a.Raw, b.RawZ)); } 140 | public static F32Vec3 PowFastest(F32 a, F32Vec3 b) { return new F32Vec3(Fixed32.PowFastest(a.Raw, b.RawX), Fixed32.PowFastest(a.Raw, b.RawY), Fixed32.PowFastest(a.Raw, b.RawZ)); } 141 | public static F32Vec3 Pow(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.Pow(a.RawX, b.RawX), Fixed32.Pow(a.RawY, b.RawY), Fixed32.Pow(a.RawZ, b.RawZ)); } 142 | public static F32Vec3 PowFast(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.PowFast(a.RawX, b.RawX), Fixed32.PowFast(a.RawY, b.RawY), Fixed32.PowFast(a.RawZ, b.RawZ)); } 143 | public static F32Vec3 PowFastest(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.PowFastest(a.RawX, b.RawX), Fixed32.PowFastest(a.RawY, b.RawY), Fixed32.PowFastest(a.RawZ, b.RawZ)); } 144 | 145 | public static F32 Length(F32Vec3 a) { return F32.FromRaw((int)(Fixed64.Sqrt((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ) >> 16)); } 146 | public static F32 LengthFast(F32Vec3 a) { return F32.FromRaw((int)(Fixed64.SqrtFast((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ) >> 16)); } 147 | public static F32 LengthFastest(F32Vec3 a) { return F32.FromRaw((int)(Fixed64.SqrtFastest((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ) >> 16)); } 148 | public static F64 LengthSqr(F32Vec3 a) { return F64.FromRaw((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ); } 149 | public static F32Vec3 Normalize(F32Vec3 a) { F32 ooLen = F32.FromRaw((int)(Fixed64.RSqrt((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ) >> 16)); return ooLen * a; } 150 | public static F32Vec3 NormalizeFast(F32Vec3 a) { F32 ooLen = F32.FromRaw((int)(Fixed64.RSqrtFast((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ) >> 16)); return ooLen * a; } 151 | public static F32Vec3 NormalizeFastest(F32Vec3 a) { F32 ooLen = F32.FromRaw((int)(Fixed64.RSqrtFastest((long)a.RawX * (long)a.RawX + (long)a.RawY * (long)a.RawY + (long)a.RawZ * (long)a.RawZ) >> 16)); return ooLen * a; } 152 | 153 | public static F32 Dot(F32Vec3 a, F32Vec3 b) { return F32.FromRaw(Fixed32.Mul(a.RawX, b.RawX) + Fixed32.Mul(a.RawY, b.RawY) + Fixed32.Mul(a.RawZ, b.RawZ)); } 154 | public static F32 Distance(F32Vec3 a, F32Vec3 b) { return Length(a - b); } 155 | public static F32 DistanceFast(F32Vec3 a, F32Vec3 b) { return LengthFast(a - b); } 156 | public static F32 DistanceFastest(F32Vec3 a, F32Vec3 b) { return LengthFastest(a - b); } 157 | 158 | public static F32Vec3 Min(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.Min(a.RawX, b.RawX), Fixed32.Min(a.RawY, b.RawY), Fixed32.Min(a.RawZ, b.RawZ)); } 159 | public static F32Vec3 Max(F32Vec3 a, F32Vec3 b) { return new F32Vec3(Fixed32.Max(a.RawX, b.RawX), Fixed32.Max(a.RawY, b.RawY), Fixed32.Max(a.RawZ, b.RawZ)); } 160 | 161 | public static F32Vec3 Clamp(F32Vec3 a, F32 min, F32 max) 162 | { 163 | return new F32Vec3( 164 | Fixed32.Clamp(a.RawX, min.Raw, max.Raw), 165 | Fixed32.Clamp(a.RawY, min.Raw, max.Raw), 166 | Fixed32.Clamp(a.RawZ, min.Raw, max.Raw)); 167 | } 168 | 169 | public static F32Vec3 Clamp(F32Vec3 a, F32Vec3 min, F32Vec3 max) 170 | { 171 | return new F32Vec3( 172 | Fixed32.Clamp(a.RawX, min.RawX, max.RawX), 173 | Fixed32.Clamp(a.RawY, min.RawY, max.RawY), 174 | Fixed32.Clamp(a.RawZ, min.RawZ, max.RawZ)); 175 | } 176 | 177 | public static F32Vec3 Lerp(F32Vec3 a, F32Vec3 b, F32 t) 178 | { 179 | int tb = t.Raw; 180 | int ta = Fixed32.One - tb; 181 | return new F32Vec3( 182 | Fixed32.Mul(a.RawX, ta) + Fixed32.Mul(b.RawX, tb), 183 | Fixed32.Mul(a.RawY, ta) + Fixed32.Mul(b.RawY, tb), 184 | Fixed32.Mul(a.RawZ, ta) + Fixed32.Mul(b.RawZ, tb)); 185 | } 186 | 187 | public static F32Vec3 Cross(F32Vec3 a, F32Vec3 b) 188 | { 189 | return new F32Vec3( 190 | Fixed32.Mul(a.RawY, b.RawZ) - Fixed32.Mul(a.RawZ, b.RawY), 191 | Fixed32.Mul(a.RawZ, b.RawX) - Fixed32.Mul(a.RawX, b.RawZ), 192 | Fixed32.Mul(a.RawX, b.RawY) - Fixed32.Mul(a.RawY, b.RawX)); 193 | } 194 | 195 | public bool Equals(F32Vec3 other) 196 | { 197 | return (this == other); 198 | } 199 | 200 | public override bool Equals(object obj) 201 | { 202 | if (!(obj is F32Vec3)) 203 | return false; 204 | return ((F32Vec3)obj) == this; 205 | } 206 | 207 | public override string ToString() 208 | { 209 | return "(" + Fixed32.ToString(RawX) + ", " + Fixed32.ToString(RawY) + ", " + Fixed32.ToString(RawZ) + ")"; 210 | } 211 | 212 | public override int GetHashCode() 213 | { 214 | return RawX.GetHashCode() ^ RawY.GetHashCode() * 7919 ^ RawZ.GetHashCode() * 4513; 215 | } 216 | } 217 | } 218 | -------------------------------------------------------------------------------- /Examples/FixMath/F64.cs: -------------------------------------------------------------------------------- 1 | // 2 | // FixPointCS 3 | // 4 | // Copyright(c) Jere Sanisalo, Petri Kero 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | using FixPointCS; 25 | using System; 26 | using System.Runtime.CompilerServices; 27 | 28 | namespace FixMath 29 | { 30 | /// 31 | /// Signed 32.32 fixed point value struct. 32 | /// 33 | [Serializable] 34 | public struct F64 : IComparable, IEquatable, IComparable 35 | { 36 | // Constants 37 | public static F64 Neg1 { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.Neg1); } } 38 | public static F64 Zero { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.Zero); } } 39 | public static F64 Half { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.Half); } } 40 | public static F64 One { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.One); } } 41 | public static F64 Two { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.Two); } } 42 | public static F64 Pi { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.Pi); } } 43 | public static F64 Pi2 { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.Pi2); } } 44 | public static F64 PiHalf { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.PiHalf); } } 45 | public static F64 E { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.E); } } 46 | 47 | public static F64 MinValue { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.MinValue); } } 48 | public static F64 MaxValue { [MethodImpl(FixedUtil.AggressiveInlining)] get { return FromRaw(Fixed64.MaxValue); } } 49 | 50 | // Raw fixed point value 51 | public long Raw; 52 | 53 | // Construction 54 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F64 FromRaw(long raw) { F64 v; v.Raw = raw; return v; } 55 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F64 FromInt(int v) { return FromRaw(Fixed64.FromInt(v)); } 56 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F64 FromFloat(float v) { return FromRaw(Fixed64.FromFloat(v)); } 57 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F64 FromDouble(double v) { return FromRaw(Fixed64.FromDouble(v)); } 58 | [MethodImpl(FixedUtil.AggressiveInlining)] public static F64 FromF32(F32 v) { return FromRaw((long)v.Raw << 16); } 59 | 60 | // Conversions 61 | public static int FloorToInt(F64 a) { return Fixed64.FloorToInt(a.Raw); } 62 | public static int CeilToInt(F64 a) { return Fixed64.CeilToInt(a.Raw); } 63 | public static int RoundToInt(F64 a) { return Fixed64.RoundToInt(a.Raw); } 64 | public float Float { get { return Fixed64.ToFloat(Raw); } } 65 | public double Double { get { return Fixed64.ToDouble(Raw); } } 66 | public F32 F32 { get { return F32.FromRaw((int)(Raw >> 16)); } } 67 | 68 | // Creates the fixed point number that's a divided by b. 69 | public static F64 Ratio(int a, int b) { return F64.FromRaw(((long)a << 32) / b); } 70 | // Creates the fixed point number that's a divided by 10. 71 | public static F64 Ratio10(int a) { return F64.FromRaw(((long)a << 32) / 10); } 72 | // Creates the fixed point number that's a divided by 100. 73 | public static F64 Ratio100(int a) { return F64.FromRaw(((long)a << 32) / 100); } 74 | // Creates the fixed point number that's a divided by 1000. 75 | public static F64 Ratio1000(int a) { return F64.FromRaw(((long)a << 32) / 1000); } 76 | 77 | // Operators 78 | public static F64 operator -(F64 v1) { return FromRaw(-v1.Raw); } 79 | 80 | public static F64 operator +(F64 v1, F64 v2) { return FromRaw(v1.Raw + v2.Raw); } 81 | public static F64 operator -(F64 v1, F64 v2) { return FromRaw(v1.Raw - v2.Raw); } 82 | public static F64 operator *(F64 v1, F64 v2) { return FromRaw(Fixed64.Mul(v1.Raw, v2.Raw)); } 83 | public static F64 operator /(F64 v1, F64 v2) { return FromRaw(Fixed64.DivPrecise(v1.Raw, v2.Raw)); } 84 | public static F64 operator %(F64 v1, F64 v2) { return FromRaw(Fixed64.Mod(v1.Raw, v2.Raw)); } 85 | 86 | public static F64 operator +(F64 v1, int v2) { return FromRaw(v1.Raw + Fixed64.FromInt(v2)); } 87 | public static F64 operator +(int v1, F64 v2) { return FromRaw(Fixed64.FromInt(v1) + v2.Raw); } 88 | public static F64 operator -(F64 v1, int v2) { return FromRaw(v1.Raw - Fixed64.FromInt(v2)); } 89 | public static F64 operator -(int v1, F64 v2) { return FromRaw(Fixed64.FromInt(v1) - v2.Raw); } 90 | public static F64 operator *(F64 v1, int v2) { return FromRaw(v1.Raw * (long)v2); } 91 | public static F64 operator *(int v1, F64 v2) { return FromRaw((long)v1 * v2.Raw); } 92 | public static F64 operator /(F64 v1, int v2) { return FromRaw(v1.Raw / (long)v2); } 93 | public static F64 operator /(int v1, F64 v2) { return FromRaw(Fixed64.DivPrecise(Fixed64.FromInt(v1), v2.Raw)); } 94 | public static F64 operator %(F64 v1, int v2) { return FromRaw(Fixed64.Mod(v1.Raw, Fixed64.FromInt(v2))); } 95 | public static F64 operator %(int v1, F64 v2) { return FromRaw(Fixed64.Mod(Fixed64.FromInt(v1), v2.Raw)); } 96 | 97 | public static F64 operator ++(F64 v1) { return FromRaw(v1.Raw + Fixed64.One); } 98 | public static F64 operator --(F64 v1) { return FromRaw(v1.Raw - Fixed64.One); } 99 | 100 | public static bool operator ==(F64 v1, F64 v2) { return v1.Raw == v2.Raw; } 101 | public static bool operator !=(F64 v1, F64 v2) { return v1.Raw != v2.Raw; } 102 | public static bool operator <(F64 v1, F64 v2) { return v1.Raw < v2.Raw; } 103 | public static bool operator <=(F64 v1, F64 v2) { return v1.Raw <= v2.Raw; } 104 | public static bool operator >(F64 v1, F64 v2) { return v1.Raw > v2.Raw; } 105 | public static bool operator >=(F64 v1, F64 v2) { return v1.Raw >= v2.Raw; } 106 | 107 | public static bool operator ==(int v1, F64 v2) { return Fixed64.FromInt(v1) == v2.Raw; } 108 | public static bool operator ==(F64 v1, int v2) { return v1.Raw == Fixed64.FromInt(v2); } 109 | public static bool operator !=(int v1, F64 v2) { return Fixed64.FromInt(v1) != v2.Raw; } 110 | public static bool operator !=(F64 v1, int v2) { return v1.Raw != Fixed64.FromInt(v2); } 111 | public static bool operator <(int v1, F64 v2) { return Fixed64.FromInt(v1) < v2.Raw; } 112 | public static bool operator <(F64 v1, int v2) { return v1.Raw < Fixed64.FromInt(v2); } 113 | public static bool operator <=(int v1, F64 v2) { return Fixed64.FromInt(v1) <= v2.Raw; } 114 | public static bool operator <=(F64 v1, int v2) { return v1.Raw <= Fixed64.FromInt(v2); } 115 | public static bool operator >(int v1, F64 v2) { return Fixed64.FromInt(v1) > v2.Raw; } 116 | public static bool operator >(F64 v1, int v2) { return v1.Raw > Fixed64.FromInt(v2); } 117 | public static bool operator >=(int v1, F64 v2) { return Fixed64.FromInt(v1) >= v2.Raw; } 118 | public static bool operator >=(F64 v1, int v2) { return v1.Raw >= Fixed64.FromInt(v2); } 119 | 120 | public static bool operator ==(F32 a, F64 b) { return F64.FromF32(a) == b; } 121 | public static bool operator ==(F64 a, F32 b) { return a == F64.FromF32(b); } 122 | public static bool operator !=(F32 a, F64 b) { return F64.FromF32(a) != b; } 123 | public static bool operator !=(F64 a, F32 b) { return a != F64.FromF32(b); } 124 | public static bool operator <(F32 a, F64 b) { return F64.FromF32(a) < b; } 125 | public static bool operator <(F64 a, F32 b) { return a < F64.FromF32(b); } 126 | public static bool operator <=(F32 a, F64 b) { return F64.FromF32(a) <= b; } 127 | public static bool operator <=(F64 a, F32 b) { return a <= F64.FromF32(b); } 128 | public static bool operator >(F32 a, F64 b) { return F64.FromF32(a) > b; } 129 | public static bool operator >(F64 a, F32 b) { return a > F64.FromF32(b); } 130 | public static bool operator >=(F32 a, F64 b) { return F64.FromF32(a) >= b; } 131 | public static bool operator >=(F64 a, F32 b) { return a >= F64.FromF32(b); } 132 | 133 | public static F64 RadToDeg(F64 a) { return FromRaw(Fixed64.Mul(a.Raw, 246083499198)); } // 180 / F64.Pi 134 | public static F64 DegToRad(F64 a) { return FromRaw(Fixed64.Mul(a.Raw, 74961320)); } // F64.Pi / 180 135 | 136 | public static F64 Div2(F64 a) { return FromRaw(a.Raw >> 1); } 137 | public static F64 Abs(F64 a) { return FromRaw(Fixed64.Abs(a.Raw)); } 138 | public static F64 Nabs(F64 a) { return FromRaw(Fixed64.Nabs(a.Raw)); } 139 | public static int Sign(F64 a) { return Fixed64.Sign(a.Raw); } 140 | public static F64 Ceil(F64 a) { return FromRaw(Fixed64.Ceil(a.Raw)); } 141 | public static F64 Floor(F64 a) { return FromRaw(Fixed64.Floor(a.Raw)); } 142 | public static F64 Round(F64 a) { return FromRaw(Fixed64.Round(a.Raw)); } 143 | public static F64 Fract(F64 a) { return FromRaw(Fixed64.Fract(a.Raw)); } 144 | public static F64 Div(F64 a, F64 b) { return FromRaw(Fixed64.Div(a.Raw, b.Raw)); } 145 | public static F64 DivFast(F64 a, F64 b) { return FromRaw(Fixed64.DivFast(a.Raw, b.Raw)); } 146 | public static F64 DivFastest(F64 a, F64 b) { return FromRaw(Fixed64.DivFastest(a.Raw, b.Raw)); } 147 | public static F64 SqrtPrecise(F64 a) { return FromRaw(Fixed64.SqrtPrecise(a.Raw)); } 148 | public static F64 Sqrt(F64 a) { return FromRaw(Fixed64.Sqrt(a.Raw)); } 149 | public static F64 SqrtFast(F64 a) { return FromRaw(Fixed64.SqrtFast(a.Raw)); } 150 | public static F64 SqrtFastest(F64 a) { return FromRaw(Fixed64.SqrtFastest(a.Raw)); } 151 | public static F64 RSqrt(F64 a) { return FromRaw(Fixed64.RSqrt(a.Raw)); } 152 | public static F64 RSqrtFast(F64 a) { return FromRaw(Fixed64.RSqrtFast(a.Raw)); } 153 | public static F64 RSqrtFastest(F64 a) { return FromRaw(Fixed64.RSqrtFastest(a.Raw)); } 154 | public static F64 Rcp(F64 a) { return FromRaw(Fixed64.Rcp(a.Raw)); } 155 | public static F64 RcpFast(F64 a) { return FromRaw(Fixed64.RcpFast(a.Raw)); } 156 | public static F64 RcpFastest(F64 a) { return FromRaw(Fixed64.RcpFastest(a.Raw)); } 157 | public static F64 Exp(F64 a) { return FromRaw(Fixed64.Exp(a.Raw)); } 158 | public static F64 ExpFast(F64 a) { return FromRaw(Fixed64.ExpFast(a.Raw)); } 159 | public static F64 ExpFastest(F64 a) { return FromRaw(Fixed64.ExpFastest(a.Raw)); } 160 | public static F64 Exp2(F64 a) { return FromRaw(Fixed64.Exp2(a.Raw)); } 161 | public static F64 Exp2Fast(F64 a) { return FromRaw(Fixed64.Exp2Fast(a.Raw)); } 162 | public static F64 Exp2Fastest(F64 a) { return FromRaw(Fixed64.Exp2Fastest(a.Raw)); } 163 | public static F64 Log(F64 a) { return FromRaw(Fixed64.Log(a.Raw)); } 164 | public static F64 LogFast(F64 a) { return FromRaw(Fixed64.LogFast(a.Raw)); } 165 | public static F64 LogFastest(F64 a) { return FromRaw(Fixed64.LogFastest(a.Raw)); } 166 | public static F64 Log2(F64 a) { return FromRaw(Fixed64.Log2(a.Raw)); } 167 | public static F64 Log2Fast(F64 a) { return FromRaw(Fixed64.Log2Fast(a.Raw)); } 168 | public static F64 Log2Fastest(F64 a) { return FromRaw(Fixed64.Log2Fastest(a.Raw)); } 169 | 170 | public static F64 Sin(F64 a) { return FromRaw(Fixed64.Sin(a.Raw)); } 171 | public static F64 SinFast(F64 a) { return FromRaw(Fixed64.SinFast(a.Raw)); } 172 | public static F64 SinFastest(F64 a) { return FromRaw(Fixed64.SinFastest(a.Raw)); } 173 | public static F64 Cos(F64 a) { return FromRaw(Fixed64.Cos(a.Raw)); } 174 | public static F64 CosFast(F64 a) { return FromRaw(Fixed64.CosFast(a.Raw)); } 175 | public static F64 CosFastest(F64 a) { return FromRaw(Fixed64.CosFastest(a.Raw)); } 176 | public static F64 Tan(F64 a) { return FromRaw(Fixed64.Tan(a.Raw)); } 177 | public static F64 TanFast(F64 a) { return FromRaw(Fixed64.TanFast(a.Raw)); } 178 | public static F64 TanFastest(F64 a) { return FromRaw(Fixed64.TanFastest(a.Raw)); } 179 | public static F64 Asin(F64 a) { return FromRaw(Fixed64.Asin(a.Raw)); } 180 | public static F64 AsinFast(F64 a) { return FromRaw(Fixed64.AsinFast(a.Raw)); } 181 | public static F64 AsinFastest(F64 a) { return FromRaw(Fixed64.AsinFastest(a.Raw)); } 182 | public static F64 Acos(F64 a) { return FromRaw(Fixed64.Acos(a.Raw)); } 183 | public static F64 AcosFast(F64 a) { return FromRaw(Fixed64.AcosFast(a.Raw)); } 184 | public static F64 AcosFastest(F64 a) { return FromRaw(Fixed64.AcosFastest(a.Raw)); } 185 | public static F64 Atan(F64 a) { return FromRaw(Fixed64.Atan(a.Raw)); } 186 | public static F64 AtanFast(F64 a) { return FromRaw(Fixed64.AtanFast(a.Raw)); } 187 | public static F64 AtanFastest(F64 a) { return FromRaw(Fixed64.AtanFastest(a.Raw)); } 188 | public static F64 Atan2(F64 y, F64 x) { return FromRaw(Fixed64.Atan2(y.Raw, x.Raw)); } 189 | public static F64 Atan2Fast(F64 y, F64 x) { return FromRaw(Fixed64.Atan2Fast(y.Raw, x.Raw)); } 190 | public static F64 Atan2Fastest(F64 y, F64 x) { return FromRaw(Fixed64.Atan2Fastest(y.Raw, x.Raw)); } 191 | public static F64 Pow(F64 a, F64 b) { return FromRaw(Fixed64.Pow(a.Raw, b.Raw)); } 192 | public static F64 PowFast(F64 a, F64 b) { return FromRaw(Fixed64.PowFast(a.Raw, b.Raw)); } 193 | public static F64 PowFastest(F64 a, F64 b) { return FromRaw(Fixed64.PowFastest(a.Raw, b.Raw)); } 194 | 195 | public static F64 Min(F64 a, F64 b) { return FromRaw(Fixed64.Min(a.Raw, b.Raw)); } 196 | public static F64 Max(F64 a, F64 b) { return FromRaw(Fixed64.Max(a.Raw, b.Raw)); } 197 | public static F64 Clamp(F64 a, F64 min, F64 max) { return FromRaw(Fixed64.Clamp(a.Raw, min.Raw, max.Raw)); } 198 | public static F64 Clamp01(F64 a) { return FromRaw(Fixed64.Clamp(a.Raw, Fixed64.Zero, Fixed64.One)); } 199 | 200 | public static F64 Lerp(F64 a, F64 b, F64 t) 201 | { 202 | long tb = t.Raw; 203 | long ta = Fixed64.One - tb; 204 | return FromRaw(Fixed64.Mul(a.Raw, ta) + Fixed64.Mul(b.Raw, tb)); 205 | } 206 | 207 | public bool Equals(F64 other) 208 | { 209 | return (Raw == other.Raw); 210 | } 211 | 212 | public override bool Equals(object obj) 213 | { 214 | if (!(obj is F64)) 215 | return false; 216 | return ((F64)obj).Raw == Raw; 217 | } 218 | 219 | public int CompareTo(F64 other) 220 | { 221 | if (Raw < other.Raw) return -1; 222 | if (Raw > other.Raw) return +1; 223 | return 0; 224 | } 225 | 226 | public override string ToString() 227 | { 228 | return Fixed64.ToString(Raw); 229 | } 230 | 231 | public override int GetHashCode() 232 | { 233 | return Raw.GetHashCode(); 234 | } 235 | 236 | int IComparable.CompareTo(object obj) 237 | { 238 | if (obj is F64 other) 239 | return CompareTo(other); 240 | else if (obj is null) 241 | return 1; 242 | // don't allow comparisons with other numeric or non-numeric types. 243 | throw new ArgumentException("F64 can only be compared against another F64."); 244 | } 245 | } 246 | } 247 | -------------------------------------------------------------------------------- /Examples/FixMath/F64Quat.cs: -------------------------------------------------------------------------------- 1 | // 2 | // FixPointCS 3 | // 4 | // Copyright(c) Jere Sanisalo, Petri Kero 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | using FixPointCS; 25 | using System; 26 | 27 | namespace FixMath 28 | { 29 | /// 30 | /// Quaternion struct with signed 32.32 fixed point components. 31 | /// 32 | [Serializable] 33 | public struct F64Quat : IEquatable 34 | { 35 | // Constants 36 | public static F64Quat Identity { get { return new F64Quat(Fixed64.Zero, Fixed64.Zero, Fixed64.Zero, Fixed64.One); } } 37 | 38 | public long RawX; 39 | public long RawY; 40 | public long RawZ; 41 | public long RawW; 42 | 43 | public F64 X { get { return F64.FromRaw(RawX); } set { RawX = value.Raw; } } 44 | public F64 Y { get { return F64.FromRaw(RawY); } set { RawY = value.Raw; } } 45 | public F64 Z { get { return F64.FromRaw(RawZ); } set { RawZ = value.Raw; } } 46 | public F64 W { get { return F64.FromRaw(RawW); } set { RawW = value.Raw; } } 47 | 48 | public F64Quat(F64 x, F64 y, F64 z, F64 w) 49 | { 50 | RawX = x.Raw; 51 | RawY = y.Raw; 52 | RawZ = z.Raw; 53 | RawW = w.Raw; 54 | } 55 | 56 | public F64Quat(F64Vec3 v, F64 w) 57 | { 58 | RawX = v.RawX; 59 | RawY = v.RawY; 60 | RawZ = v.RawZ; 61 | RawW = w.Raw; 62 | } 63 | 64 | private F64Quat(long x, long y, long z, long w) 65 | { 66 | RawX = x; 67 | RawY = y; 68 | RawZ = z; 69 | RawW = w; 70 | } 71 | 72 | public static F64Quat FromAxisAngle(F64Vec3 axis, F64 angle) 73 | { 74 | F64 half_angle = F64.Div2(angle); 75 | return new F64Quat(axis * F64.SinFastest(half_angle), F64.CosFastest(half_angle)); 76 | } 77 | 78 | public static F64Quat FromYawPitchRoll(F64 yaw_y, F64 pitch_x, F64 roll_z) 79 | { 80 | // Roll first, about axis the object is facing, then 81 | // pitch upward, then yaw to face into the new heading 82 | F64 half_roll = F64.Div2(roll_z); 83 | F64 sr = F64.SinFastest(half_roll); 84 | F64 cr = F64.CosFastest(half_roll); 85 | 86 | F64 half_pitch = F64.Div2(pitch_x); 87 | F64 sp = F64.SinFastest(half_pitch); 88 | F64 cp = F64.CosFastest(half_pitch); 89 | 90 | F64 half_yaw = F64.Div2(yaw_y); 91 | F64 sy = F64.SinFastest(half_yaw); 92 | F64 cy = F64.CosFastest(half_yaw); 93 | 94 | return new F64Quat( 95 | cy * sp * cr + sy * cp * sr, 96 | sy * cp * cr - cy * sp * sr, 97 | cy * cp * sr - sy * sp * cr, 98 | cy * cp * cr + sy * sp * sr); 99 | } 100 | 101 | // Creates a unit quaternion that represents the rotation from a to b. a and b do not need to be normalized. 102 | public static F64Quat FromTwoVectors(F64Vec3 a, F64Vec3 b) 103 | { // From: http://lolengine.net/blog/2014/02/24/quaternion-from-two-vectors-final 104 | F64 epsilon = F64.Ratio(1, 1000000); 105 | 106 | F64 norm_a_norm_b = F64.SqrtFastest(F64Vec3.LengthSqr(a) * F64Vec3.LengthSqr(b)); 107 | F64 real_part = norm_a_norm_b + F64Vec3.Dot(a, b); 108 | 109 | F64Vec3 v; 110 | 111 | if (real_part < (epsilon * norm_a_norm_b)) 112 | { 113 | /* If u and v are exactly opposite, rotate 180 degrees 114 | * around an arbitrary orthogonal axis. Axis normalization 115 | * can happen later, when we normalize the quaternion. */ 116 | real_part = F64.Zero; 117 | bool cond = F64.Abs(a.X) > F64.Abs(a.Z); 118 | v = cond ? new F64Vec3(-a.Y, a.X, F64.Zero) 119 | : new F64Vec3(F64.Zero, -a.Z, a.Y); 120 | } 121 | else 122 | { 123 | /* Otherwise, build quaternion the standard way. */ 124 | v = F64Vec3.Cross(a, b); 125 | } 126 | 127 | return NormalizeFastest(new F64Quat(v, real_part)); 128 | } 129 | 130 | public static F64Quat LookRotation(F64Vec3 dir, F64Vec3 up) 131 | { // From: https://answers.unity.com/questions/819699/calculate-quaternionlookrotation-manually.html 132 | if (dir == F64Vec3.Zero) 133 | return Identity; 134 | 135 | if (up != dir) 136 | { 137 | up = F64Vec3.NormalizeFastest(up); 138 | F64Vec3 v = dir + up * -F64Vec3.Dot(up, dir); 139 | F64Quat q = FromTwoVectors(F64Vec3.AxisZ, v); 140 | return FromTwoVectors(v, dir) * q; 141 | } 142 | else 143 | return FromTwoVectors(F64Vec3.AxisZ, dir); 144 | } 145 | 146 | public static F64Quat LookAtRotation(F64Vec3 from, F64Vec3 to, F64Vec3 up) 147 | { 148 | F64Vec3 dir = F64Vec3.NormalizeFastest(to - from); 149 | return LookRotation(dir, up); 150 | } 151 | 152 | // Operators 153 | public static F64Quat operator *(F64Quat a, F64Quat b) { return Multiply(a, b); } 154 | 155 | public static bool operator ==(F64Quat a, F64Quat b) { return a.RawX == b.RawX && a.RawY == b.RawY && a.RawZ == b.RawZ && a.RawW == b.RawW; } 156 | public static bool operator !=(F64Quat a, F64Quat b) { return a.RawX != b.RawX || a.RawY != b.RawY || a.RawZ != b.RawZ || a.RawW != b.RawW; } 157 | 158 | public static F64Quat Negate(F64Quat a) { return new F64Quat(-a.RawX, -a.RawY, -a.RawZ, -a.RawW); } 159 | public static F64Quat Conjugate(F64Quat a) { return new F64Quat(-a.RawX, -a.RawY, -a.RawZ, a.RawW); } 160 | public static F64Quat Inverse(F64Quat a) 161 | { 162 | long inv_norm = F64.Rcp(LengthSqr(a)).Raw; 163 | return new F64Quat( 164 | -Fixed64.Mul(a.RawX, inv_norm), 165 | -Fixed64.Mul(a.RawY, inv_norm), 166 | -Fixed64.Mul(a.RawZ, inv_norm), 167 | Fixed64.Mul(a.RawW, inv_norm)); 168 | } 169 | // Inverse for unit quaternions 170 | public static F64Quat InverseUnit(F64Quat a) { return new F64Quat(-a.RawX, -a.RawY, -a.RawZ, a.RawW); } 171 | 172 | public static F64Quat Multiply(F64Quat value1, F64Quat value2) 173 | { 174 | F64 q1x = value1.X; 175 | F64 q1y = value1.Y; 176 | F64 q1z = value1.Z; 177 | F64 q1w = value1.W; 178 | 179 | F64 q2x = value2.X; 180 | F64 q2y = value2.Y; 181 | F64 q2z = value2.Z; 182 | F64 q2w = value2.W; 183 | 184 | // cross(av, bv) 185 | F64 cx = q1y * q2z - q1z * q2y; 186 | F64 cy = q1z * q2x - q1x * q2z; 187 | F64 cz = q1x * q2y - q1y * q2x; 188 | 189 | F64 dot = q1x * q2x + q1y * q2y + q1z * q2z; 190 | 191 | return new F64Quat( 192 | q1x * q2w + q2x * q1w + cx, 193 | q1y * q2w + q2y * q1w + cy, 194 | q1z * q2w + q2z * q1w + cz, 195 | q1w * q2w - dot); 196 | } 197 | 198 | public static F64 Length(F64Quat a) { return F64.Sqrt(LengthSqr(a)); } 199 | public static F64 LengthFast(F64Quat a) { return F64.SqrtFast(LengthSqr(a)); } 200 | public static F64 LengthFastest(F64Quat a) { return F64.SqrtFastest(LengthSqr(a)); } 201 | public static F64 LengthSqr(F64Quat a) { return F64.FromRaw(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ) + Fixed64.Mul(a.RawW, a.RawW)); } 202 | public static F64Quat Normalize(F64Quat a) 203 | { 204 | long inv_norm = F64.Rcp(Length(a)).Raw; 205 | return new F64Quat( 206 | Fixed64.Mul(a.RawX, inv_norm), 207 | Fixed64.Mul(a.RawY, inv_norm), 208 | Fixed64.Mul(a.RawZ, inv_norm), 209 | Fixed64.Mul(a.RawW, inv_norm)); 210 | } 211 | public static F64Quat NormalizeFast(F64Quat a) 212 | { 213 | long inv_norm = F64.RcpFast(LengthFast(a)).Raw; 214 | return new F64Quat( 215 | Fixed64.Mul(a.RawX, inv_norm), 216 | Fixed64.Mul(a.RawY, inv_norm), 217 | Fixed64.Mul(a.RawZ, inv_norm), 218 | Fixed64.Mul(a.RawW, inv_norm)); 219 | } 220 | public static F64Quat NormalizeFastest(F64Quat a) 221 | { 222 | long inv_norm = F64.RcpFastest(LengthFastest(a)).Raw; 223 | return new F64Quat( 224 | Fixed64.Mul(a.RawX, inv_norm), 225 | Fixed64.Mul(a.RawY, inv_norm), 226 | Fixed64.Mul(a.RawZ, inv_norm), 227 | Fixed64.Mul(a.RawW, inv_norm)); 228 | } 229 | 230 | public static F64Quat Slerp(F64Quat q1, F64Quat q2, F64 t) 231 | { 232 | F64 epsilon = F64.Ratio(1, 1000000); 233 | F64 cos_omega = q1.X * q2.X + q1.Y * q2.Y + q1.Z * q2.Z + q1.W * q2.W; 234 | 235 | bool flip = false; 236 | 237 | if (cos_omega < 0) 238 | { 239 | flip = true; 240 | cos_omega = -cos_omega; 241 | } 242 | 243 | F64 s1, s2; 244 | if (cos_omega > (F64.One - epsilon)) 245 | { 246 | // Too close, do straight linear interpolation. 247 | s1 = F64.One - t; 248 | s2 = (flip) ? -t : t; 249 | } 250 | else 251 | { 252 | F64 omega = F64.AcosFastest(cos_omega); 253 | F64 inv_sin_omega = F64.RcpFastest(F64.SinFastest(omega)); 254 | 255 | s1 = F64.SinFastest((F64.One - t) * omega) * inv_sin_omega; 256 | s2 = (flip) 257 | ? -F64.SinFastest(t * omega) * inv_sin_omega 258 | : F64.SinFastest(t * omega) * inv_sin_omega; 259 | } 260 | 261 | return new F64Quat( 262 | s1 * q1.X + s2 * q2.X, 263 | s1 * q1.Y + s2 * q2.Y, 264 | s1 * q1.Z + s2 * q2.Z, 265 | s1 * q1.W + s2 * q2.W); 266 | } 267 | 268 | public static F64Quat Lerp(F64Quat q1, F64Quat q2, F64 t) 269 | { 270 | F64 t1 = F64.One - t; 271 | F64 dot = q1.X * q2.X + q1.Y * q2.Y + q1.Z * q2.Z + q1.W * q2.W; 272 | 273 | F64Quat r; 274 | if (dot >= 0) 275 | r = new F64Quat( 276 | t1 * q1.X + t * q2.X, 277 | t1 * q1.Y + t * q2.Y, 278 | t1 * q1.Z + t * q2.Z, 279 | t1 * q1.W + t * q2.W); 280 | else 281 | r = new F64Quat( 282 | t1 * q1.X - t * q2.X, 283 | t1 * q1.Y - t * q2.Y, 284 | t1 * q1.Z - t * q2.Z, 285 | t1 * q1.W - t * q2.W); 286 | 287 | return NormalizeFastest(r); 288 | } 289 | 290 | // Concatenates two Quaternions; the result represents the value1 rotation followed by the value2 rotation. 291 | public static F64Quat Concatenate(F64Quat value1, F64Quat value2) 292 | { 293 | // Concatenate rotation is actually q2 * q1 instead of q1 * q2. 294 | // So that's why value2 goes q1 and value1 goes q2. 295 | return Multiply(value2, value1); 296 | } 297 | 298 | // Rotates a vector by the unit quaternion. 299 | public static F64Vec3 RotateVector(F64Quat rot, F64Vec3 v) 300 | { // From https://gamedev.stackexchange.com/questions/28395/rotating-vector3-by-a-quaternion 301 | F64Vec3 u = new F64Vec3(rot.X, rot.Y, rot.Z); 302 | F64 s = rot.W; 303 | 304 | return 305 | (F64.Two * F64Vec3.Dot(u, v)) * u + 306 | (s * s - F64Vec3.Dot(u, u)) * v + 307 | (F64.Two * s) * F64Vec3.Cross(u, v); 308 | } 309 | 310 | public bool Equals(F64Quat other) 311 | { 312 | return (this == other); 313 | } 314 | 315 | public override bool Equals(object obj) 316 | { 317 | if (!(obj is F64Quat)) 318 | return false; 319 | return ((F64Quat)obj) == this; 320 | } 321 | 322 | public override string ToString() 323 | { 324 | return "(" + Fixed64.ToString(RawX) + ", " + Fixed64.ToString(RawY) + ", " + Fixed64.ToString(RawZ) + ", " + Fixed64.ToString(RawW) + ")"; 325 | } 326 | 327 | public override int GetHashCode() 328 | { 329 | return X.GetHashCode() ^ (Y.GetHashCode() * 7919) ^ (Z.GetHashCode() * 4513) ^ (W.GetHashCode() * 8923); 330 | } 331 | } 332 | } 333 | -------------------------------------------------------------------------------- /Examples/FixMath/F64Vec2.cs: -------------------------------------------------------------------------------- 1 | // 2 | // FixPointCS 3 | // 4 | // Copyright(c) Jere Sanisalo, Petri Kero 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | using FixPointCS; 25 | using System; 26 | using System.Runtime.CompilerServices; 27 | 28 | namespace FixMath 29 | { 30 | /// 31 | /// Vector2 struct with signed 32.32 fixed point components. 32 | /// 33 | [Serializable] 34 | public struct F64Vec2 : IEquatable 35 | { 36 | // Constants 37 | public static F64Vec2 Zero { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.Zero, Fixed64.Zero); } } 38 | public static F64Vec2 One { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.One, Fixed64.One); } } 39 | public static F64Vec2 Down { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.Zero, Fixed64.Neg1); } } 40 | public static F64Vec2 Up { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.Zero, Fixed64.One); } } 41 | public static F64Vec2 Left { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.Neg1, Fixed64.Zero); } } 42 | public static F64Vec2 Right { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.One, Fixed64.Zero); } } 43 | public static F64Vec2 AxisX { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.One, Fixed64.Zero); } } 44 | public static F64Vec2 AxisY { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec2(Fixed64.Zero, Fixed64.One); } } 45 | 46 | // Raw components 47 | public long RawX; 48 | public long RawY; 49 | 50 | // F64 accessors 51 | public F64 X { get { return F64.FromRaw(RawX); } set { RawX = value.Raw; } } 52 | public F64 Y { get { return F64.FromRaw(RawY); } set { RawY = value.Raw; } } 53 | 54 | public F64Vec2(F64 x, F64 y) 55 | { 56 | RawX = x.Raw; 57 | RawY = y.Raw; 58 | } 59 | 60 | // raw ctor only for internal usage 61 | private F64Vec2(long x, long y) 62 | { 63 | RawX = x; 64 | RawY = y; 65 | } 66 | 67 | public static F64Vec2 FromRaw(long rawX, long rawY) { return new F64Vec2(rawX, rawY); } 68 | public static F64Vec2 FromInt(int x, int y) { return new F64Vec2(Fixed64.FromInt(x), Fixed64.FromInt(y)); } 69 | public static F64Vec2 FromFloat(float x, float y) { return new F64Vec2(Fixed64.FromFloat(x), Fixed64.FromFloat(y)); } 70 | public static F64Vec2 FromDouble(double x, double y) { return new F64Vec2(Fixed64.FromDouble(x), Fixed64.FromDouble(y)); } 71 | 72 | public static F64Vec2 operator -(F64Vec2 a) { return new F64Vec2(-a.RawX, -a.RawY); } 73 | public static F64Vec2 operator +(F64Vec2 a, F64Vec2 b) { return new F64Vec2(a.RawX + b.RawX, a.RawY + b.RawY); } 74 | public static F64Vec2 operator -(F64Vec2 a, F64Vec2 b) { return new F64Vec2(a.RawX - b.RawX, a.RawY - b.RawY); } 75 | public static F64Vec2 operator *(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.Mul(a.RawX, b.RawX), Fixed64.Mul(a.RawY, b.RawY)); } 76 | public static F64Vec2 operator /(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.DivPrecise(a.RawX, b.RawX), Fixed64.DivPrecise(a.RawY, b.RawY)); } 77 | public static F64Vec2 operator %(F64Vec2 a, F64Vec2 b) { return new F64Vec2(a.RawX % b.RawX, a.RawY % b.RawY); } 78 | 79 | public static F64Vec2 operator +(F64 a, F64Vec2 b) { return new F64Vec2(a.Raw + b.RawX, a.Raw + b.RawY); } 80 | public static F64Vec2 operator +(F64Vec2 a, F64 b) { return new F64Vec2(a.RawX + b.Raw, a.RawY + b.Raw); } 81 | public static F64Vec2 operator -(F64 a, F64Vec2 b) { return new F64Vec2(a.Raw - b.RawX, a.Raw - b.RawY); } 82 | public static F64Vec2 operator -(F64Vec2 a, F64 b) { return new F64Vec2(a.RawX - b.Raw, a.RawY - b.Raw); } 83 | public static F64Vec2 operator *(F64 a, F64Vec2 b) { return new F64Vec2(Fixed64.Mul(a.Raw, b.RawX), Fixed64.Mul(a.Raw, b.RawY)); } 84 | public static F64Vec2 operator *(F64Vec2 a, F64 b) { return new F64Vec2(Fixed64.Mul(a.RawX, b.Raw), Fixed64.Mul(a.RawY, b.Raw)); } 85 | public static F64Vec2 operator /(F64 a, F64Vec2 b) { return new F64Vec2(Fixed64.DivPrecise(a.Raw, b.RawX), Fixed64.DivPrecise(a.Raw, b.RawY)); } 86 | public static F64Vec2 operator /(F64Vec2 a, F64 b) { return new F64Vec2(Fixed64.DivPrecise(a.RawX, b.Raw), Fixed64.DivPrecise(a.RawY, b.Raw)); } 87 | public static F64Vec2 operator %(F64 a, F64Vec2 b) { return new F64Vec2(a.Raw % b.RawX, a.Raw % b.RawY); } 88 | public static F64Vec2 operator %(F64Vec2 a, F64 b) { return new F64Vec2(a.RawX % b.Raw, a.RawY % b.Raw); } 89 | 90 | public static bool operator ==(F64Vec2 a, F64Vec2 b) { return a.RawX == b.RawX && a.RawY == b.RawY; } 91 | public static bool operator !=(F64Vec2 a, F64Vec2 b) { return a.RawX != b.RawX || a.RawY != b.RawY; } 92 | 93 | public static F64Vec2 Div(F64Vec2 a, F64 b) { long oob = Fixed64.Rcp(b.Raw); return new F64Vec2(Fixed64.Mul(a.RawX, oob), Fixed64.Mul(a.RawY, oob)); } 94 | public static F64Vec2 DivFast(F64Vec2 a, F64 b) { long oob = Fixed64.RcpFast(b.Raw); return new F64Vec2(Fixed64.Mul(a.RawX, oob), Fixed64.Mul(a.RawY, oob)); } 95 | public static F64Vec2 DivFastest(F64Vec2 a, F64 b) { long oob = Fixed64.RcpFastest(b.Raw); return new F64Vec2(Fixed64.Mul(a.RawX, oob), Fixed64.Mul(a.RawY, oob)); } 96 | public static F64Vec2 Div(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.Div(a.RawX, b.RawX), Fixed64.Div(a.RawY, b.RawY)); } 97 | public static F64Vec2 DivFast(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.DivFast(a.RawX, b.RawX), Fixed64.DivFast(a.RawY, b.RawY)); } 98 | public static F64Vec2 DivFastest(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.DivFastest(a.RawX, b.RawX), Fixed64.DivFastest(a.RawY, b.RawY)); } 99 | public static F64Vec2 SqrtPrecise(F64Vec2 a) { return new F64Vec2(Fixed64.SqrtPrecise(a.RawX), Fixed64.SqrtPrecise(a.RawY)); } 100 | public static F64Vec2 Sqrt(F64Vec2 a) { return new F64Vec2(Fixed64.Sqrt(a.RawX), Fixed64.Sqrt(a.RawY)); } 101 | public static F64Vec2 SqrtFast(F64Vec2 a) { return new F64Vec2(Fixed64.SqrtFast(a.RawX), Fixed64.SqrtFast(a.RawY)); } 102 | public static F64Vec2 SqrtFastest(F64Vec2 a) { return new F64Vec2(Fixed64.SqrtFastest(a.RawX), Fixed64.SqrtFastest(a.RawY)); } 103 | public static F64Vec2 RSqrt(F64Vec2 a) { return new F64Vec2(Fixed64.RSqrt(a.RawX), Fixed64.RSqrt(a.RawY)); } 104 | public static F64Vec2 RSqrtFast(F64Vec2 a) { return new F64Vec2(Fixed64.RSqrtFast(a.RawX), Fixed64.RSqrtFast(a.RawY)); } 105 | public static F64Vec2 RSqrtFastest(F64Vec2 a) { return new F64Vec2(Fixed64.RSqrtFastest(a.RawX), Fixed64.RSqrtFastest(a.RawY)); } 106 | public static F64Vec2 Rcp(F64Vec2 a) { return new F64Vec2(Fixed64.Rcp(a.RawX), Fixed64.Rcp(a.RawY)); } 107 | public static F64Vec2 RcpFast(F64Vec2 a) { return new F64Vec2(Fixed64.RcpFast(a.RawX), Fixed64.RcpFast(a.RawY)); } 108 | public static F64Vec2 RcpFastest(F64Vec2 a) { return new F64Vec2(Fixed64.RcpFastest(a.RawX), Fixed64.RcpFastest(a.RawY)); } 109 | public static F64Vec2 Exp(F64Vec2 a) { return new F64Vec2(Fixed64.Exp(a.RawX), Fixed64.Exp(a.RawY)); } 110 | public static F64Vec2 ExpFast(F64Vec2 a) { return new F64Vec2(Fixed64.ExpFast(a.RawX), Fixed64.ExpFast(a.RawY)); } 111 | public static F64Vec2 ExpFastest(F64Vec2 a) { return new F64Vec2(Fixed64.ExpFastest(a.RawX), Fixed64.ExpFastest(a.RawY)); } 112 | public static F64Vec2 Exp2(F64Vec2 a) { return new F64Vec2(Fixed64.Exp2(a.RawX), Fixed64.Exp2(a.RawY)); } 113 | public static F64Vec2 Exp2Fast(F64Vec2 a) { return new F64Vec2(Fixed64.Exp2Fast(a.RawX), Fixed64.Exp2Fast(a.RawY)); } 114 | public static F64Vec2 Exp2Fastest(F64Vec2 a) { return new F64Vec2(Fixed64.Exp2Fastest(a.RawX), Fixed64.Exp2Fastest(a.RawY)); } 115 | public static F64Vec2 Log(F64Vec2 a) { return new F64Vec2(Fixed64.Log(a.RawX), Fixed64.Log(a.RawY)); } 116 | public static F64Vec2 LogFast(F64Vec2 a) { return new F64Vec2(Fixed64.LogFast(a.RawX), Fixed64.LogFast(a.RawY)); } 117 | public static F64Vec2 LogFastest(F64Vec2 a) { return new F64Vec2(Fixed64.LogFastest(a.RawX), Fixed64.LogFastest(a.RawY)); } 118 | public static F64Vec2 Log2(F64Vec2 a) { return new F64Vec2(Fixed64.Log2(a.RawX), Fixed64.Log2(a.RawY)); } 119 | public static F64Vec2 Log2Fast(F64Vec2 a) { return new F64Vec2(Fixed64.Log2Fast(a.RawX), Fixed64.Log2Fast(a.RawY)); } 120 | public static F64Vec2 Log2Fastest(F64Vec2 a) { return new F64Vec2(Fixed64.Log2Fastest(a.RawX), Fixed64.Log2Fastest(a.RawY)); } 121 | public static F64Vec2 Sin(F64Vec2 a) { return new F64Vec2(Fixed64.Sin(a.RawX), Fixed64.Sin(a.RawY)); } 122 | public static F64Vec2 SinFast(F64Vec2 a) { return new F64Vec2(Fixed64.SinFast(a.RawX), Fixed64.SinFast(a.RawY)); } 123 | public static F64Vec2 SinFastest(F64Vec2 a) { return new F64Vec2(Fixed64.SinFastest(a.RawX), Fixed64.SinFastest(a.RawY)); } 124 | public static F64Vec2 Cos(F64Vec2 a) { return new F64Vec2(Fixed64.Cos(a.RawX), Fixed64.Cos(a.RawY)); } 125 | public static F64Vec2 CosFast(F64Vec2 a) { return new F64Vec2(Fixed64.CosFast(a.RawX), Fixed64.CosFast(a.RawY)); } 126 | public static F64Vec2 CosFastest(F64Vec2 a) { return new F64Vec2(Fixed64.CosFastest(a.RawX), Fixed64.CosFastest(a.RawY)); } 127 | 128 | public static F64Vec2 Pow(F64Vec2 a, F64 b) { return new F64Vec2(Fixed64.Pow(a.RawX, b.Raw), Fixed64.Pow(a.RawY, b.Raw)); } 129 | public static F64Vec2 PowFast(F64Vec2 a, F64 b) { return new F64Vec2(Fixed64.PowFast(a.RawX, b.Raw), Fixed64.PowFast(a.RawY, b.Raw)); } 130 | public static F64Vec2 PowFastest(F64Vec2 a, F64 b) { return new F64Vec2(Fixed64.PowFastest(a.RawX, b.Raw), Fixed64.PowFastest(a.RawY, b.Raw)); } 131 | public static F64Vec2 Pow(F64 a, F64Vec2 b) { return new F64Vec2(Fixed64.Pow(a.Raw, b.RawX), Fixed64.Pow(a.Raw, b.RawY)); } 132 | public static F64Vec2 PowFast(F64 a, F64Vec2 b) { return new F64Vec2(Fixed64.PowFast(a.Raw, b.RawX), Fixed64.PowFast(a.Raw, b.RawY)); } 133 | public static F64Vec2 PowFastest(F64 a, F64Vec2 b) { return new F64Vec2(Fixed64.PowFastest(a.Raw, b.RawX), Fixed64.PowFastest(a.Raw, b.RawY)); } 134 | public static F64Vec2 Pow(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.Pow(a.RawX, b.RawX), Fixed64.Pow(a.RawY, b.RawY)); } 135 | public static F64Vec2 PowFast(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.PowFast(a.RawX, b.RawX), Fixed64.PowFast(a.RawY, b.RawY)); } 136 | public static F64Vec2 PowFastest(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.PowFastest(a.RawX, b.RawX), Fixed64.PowFastest(a.RawY, b.RawY)); } 137 | 138 | public static F64 Length(F64Vec2 a) { return F64.FromRaw(Fixed64.Sqrt(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY))); } 139 | public static F64 LengthFast(F64Vec2 a) { return F64.FromRaw(Fixed64.SqrtFast(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY))); } 140 | public static F64 LengthFastest(F64Vec2 a) { return F64.FromRaw(Fixed64.SqrtFastest(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY))); } 141 | public static F64 LengthSqr(F64Vec2 a) { return F64.FromRaw(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY)); } 142 | public static F64Vec2 Normalize(F64Vec2 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrt(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY))); return ooLen * a; } 143 | public static F64Vec2 NormalizeFast(F64Vec2 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrtFast(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY))); return ooLen * a; } 144 | public static F64Vec2 NormalizeFastest(F64Vec2 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrtFastest(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY))); return ooLen * a; } 145 | 146 | public static F64 Dot(F64Vec2 a, F64Vec2 b) { return F64.FromRaw(Fixed64.Mul(a.RawX, b.RawX) + Fixed64.Mul(a.RawY, b.RawY)); } 147 | public static F64 Distance(F64Vec2 a, F64Vec2 b) { return Length(a - b); } 148 | public static F64 DistanceFast(F64Vec2 a, F64Vec2 b) { return LengthFast(a - b); } 149 | public static F64 DistanceFastest(F64Vec2 a, F64Vec2 b) { return LengthFastest(a - b); } 150 | 151 | public static F64Vec2 Min(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.Min(a.RawX, b.RawX), Fixed64.Min(a.RawY, b.RawY)); } 152 | public static F64Vec2 Max(F64Vec2 a, F64Vec2 b) { return new F64Vec2(Fixed64.Max(a.RawX, b.RawX), Fixed64.Max(a.RawY, b.RawY)); } 153 | 154 | public static F64Vec2 Clamp(F64Vec2 a, F64 min, F64 max) 155 | { 156 | return new F64Vec2( 157 | Fixed64.Clamp(a.RawX, min.Raw, max.Raw), 158 | Fixed64.Clamp(a.RawY, min.Raw, max.Raw)); 159 | } 160 | 161 | public static F64Vec2 Clamp(F64Vec2 a, F64Vec2 min, F64Vec2 max) 162 | { 163 | return new F64Vec2( 164 | Fixed64.Clamp(a.RawX, min.RawX, max.RawX), 165 | Fixed64.Clamp(a.RawY, min.RawY, max.RawY)); 166 | } 167 | 168 | public static F64Vec2 Lerp(F64Vec2 a, F64Vec2 b, F64 t) 169 | { 170 | long tb = t.Raw; 171 | long ta = Fixed64.One - tb; 172 | return new F64Vec2( 173 | Fixed64.Mul(a.RawX, ta) + Fixed64.Mul(b.RawX, tb), 174 | Fixed64.Mul(a.RawY, ta) + Fixed64.Mul(b.RawY, tb)); 175 | } 176 | 177 | public bool Equals(F64Vec2 other) 178 | { 179 | return (this == other); 180 | } 181 | 182 | public override bool Equals(object obj) 183 | { 184 | if (!(obj is F64Vec2)) 185 | return false; 186 | return ((F64Vec2)obj) == this; 187 | } 188 | 189 | public override string ToString() 190 | { 191 | return "(" + Fixed64.ToString(RawX) + ", " + Fixed64.ToString(RawY) + ")"; 192 | } 193 | 194 | public override int GetHashCode() 195 | { 196 | return RawX.GetHashCode() ^ RawY.GetHashCode() * 7919; 197 | } 198 | } 199 | } 200 | -------------------------------------------------------------------------------- /Examples/FixMath/F64Vec3.cs: -------------------------------------------------------------------------------- 1 | // 2 | // FixPointCS 3 | // 4 | // Copyright(c) Jere Sanisalo, Petri Kero 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | using FixPointCS; 25 | using System; 26 | using System.Runtime.CompilerServices; 27 | 28 | namespace FixMath 29 | { 30 | /// 31 | /// Vector3 struct with signed 32.32 fixed point components. 32 | /// 33 | [Serializable] 34 | public struct F64Vec3 : IEquatable 35 | { 36 | public static F64Vec3 Zero { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.Zero, Fixed64.Zero); } } 37 | public static F64Vec3 One { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.One, Fixed64.One, Fixed64.One); } } 38 | public static F64Vec3 Down { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.Neg1, Fixed64.Zero); } } 39 | public static F64Vec3 Up { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.One, Fixed64.Zero); } } 40 | public static F64Vec3 Left { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Neg1, Fixed64.Zero, Fixed64.Zero); } } 41 | public static F64Vec3 Right { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.One, Fixed64.Zero, Fixed64.Zero); } } 42 | public static F64Vec3 Forward { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.Zero, Fixed64.One); } } 43 | public static F64Vec3 Back { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.Zero, Fixed64.Neg1); } } 44 | public static F64Vec3 AxisX { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.One, Fixed64.Zero, Fixed64.Zero); } } 45 | public static F64Vec3 AxisY { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.One, Fixed64.Zero); } } 46 | public static F64Vec3 AxisZ { [MethodImpl(FixedUtil.AggressiveInlining)] get { return new F64Vec3(Fixed64.Zero, Fixed64.Zero, Fixed64.One); } } 47 | 48 | // Raw components 49 | public long RawX; 50 | public long RawY; 51 | public long RawZ; 52 | 53 | public F64 X { get { return F64.FromRaw(RawX); } set { RawX = value.Raw; } } 54 | public F64 Y { get { return F64.FromRaw(RawY); } set { RawY = value.Raw; } } 55 | public F64 Z { get { return F64.FromRaw(RawZ); } set { RawZ = value.Raw; } } 56 | 57 | public F64Vec3(F64 x, F64 y, F64 z) 58 | { 59 | RawX = x.Raw; 60 | RawY = y.Raw; 61 | RawZ = z.Raw; 62 | } 63 | 64 | public F64Vec3(F32Vec3 src) 65 | { 66 | RawX = src.RawX << 16; 67 | RawY = src.RawY << 16; 68 | RawZ = src.RawZ << 16; 69 | } 70 | 71 | // raw ctor for internal use only 72 | private F64Vec3(long x, long y, long z) 73 | { 74 | RawX = x; 75 | RawY = y; 76 | RawZ = z; 77 | } 78 | 79 | public static F64Vec3 FromRaw(long rawX, long rawY, long rawZ) { return new F64Vec3(rawX, rawY, rawZ); } 80 | public static F64Vec3 FromInt(int x, int y, int z) { return new F64Vec3(Fixed64.FromInt(x), Fixed64.FromInt(y), Fixed64.FromInt(z)); } 81 | public static F64Vec3 FromFloat(float x, float y, float z) { return new F64Vec3(Fixed64.FromFloat(x), Fixed64.FromFloat(y), Fixed64.FromFloat(z)); } 82 | public static F64Vec3 FromDouble(double x, double y, double z) { return new F64Vec3(Fixed64.FromDouble(x), Fixed64.FromDouble(y), Fixed64.FromDouble(z)); } 83 | 84 | public static F64Vec3 operator -(F64Vec3 a) { return new F64Vec3(-a.RawX, -a.RawY, -a.RawZ); } 85 | public static F64Vec3 operator +(F64Vec3 a, F64Vec3 b) { return new F64Vec3(a.RawX + b.RawX, a.RawY + b.RawY, a.RawZ + b.RawZ); } 86 | public static F64Vec3 operator -(F64Vec3 a, F64Vec3 b) { return new F64Vec3(a.RawX - b.RawX, a.RawY - b.RawY, a.RawZ - b.RawZ); } 87 | public static F64Vec3 operator *(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.Mul(a.RawX, b.RawX), Fixed64.Mul(a.RawY, b.RawY), Fixed64.Mul(a.RawZ, b.RawZ)); } 88 | public static F64Vec3 operator /(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.DivPrecise(a.RawX, b.RawX), Fixed64.DivPrecise(a.RawY, b.RawY), Fixed64.DivPrecise(a.RawZ, b.RawZ)); } 89 | public static F64Vec3 operator %(F64Vec3 a, F64Vec3 b) { return new F64Vec3(a.RawX % b.RawX, a.RawY % b.RawY, a.RawZ % b.RawZ); } 90 | 91 | public static F64Vec3 operator +(F64 a, F64Vec3 b) { return new F64Vec3(a.Raw + b.RawX, a.Raw + b.RawY, a.Raw + b.RawZ); } 92 | public static F64Vec3 operator +(F64Vec3 a, F64 b) { return new F64Vec3(a.RawX + b.Raw, a.RawY + b.Raw, a.RawZ + b.Raw); } 93 | public static F64Vec3 operator -(F64 a, F64Vec3 b) { return new F64Vec3(a.Raw - b.RawX, a.Raw - b.RawY, a.Raw - b.RawZ); } 94 | public static F64Vec3 operator -(F64Vec3 a, F64 b) { return new F64Vec3(a.RawX - b.Raw, a.RawY - b.Raw, a.RawZ - b.Raw); } 95 | public static F64Vec3 operator *(F64 a, F64Vec3 b) { return new F64Vec3(Fixed64.Mul(a.Raw, b.RawX), Fixed64.Mul(a.Raw, b.RawY), Fixed64.Mul(a.Raw, b.RawZ)); } 96 | public static F64Vec3 operator *(F64Vec3 a, F64 b) { return new F64Vec3(Fixed64.Mul(a.RawX, b.Raw), Fixed64.Mul(a.RawY, b.Raw), Fixed64.Mul(a.RawZ, b.Raw)); } 97 | public static F64Vec3 operator /(F64 a, F64Vec3 b) { return new F64Vec3(Fixed64.DivPrecise(a.Raw, b.RawX), Fixed64.DivPrecise(a.Raw, b.RawY), Fixed64.DivPrecise(a.Raw, b.RawZ)); } 98 | public static F64Vec3 operator /(F64Vec3 a, F64 b) { return new F64Vec3(Fixed64.DivPrecise(a.RawX, b.Raw), Fixed64.DivPrecise(a.RawY, b.Raw), Fixed64.DivPrecise(a.RawZ, b.Raw)); } 99 | public static F64Vec3 operator %(F64 a, F64Vec3 b) { return new F64Vec3(a.Raw % b.RawX, a.Raw % b.RawY, a.Raw % b.RawZ); } 100 | public static F64Vec3 operator %(F64Vec3 a, F64 b) { return new F64Vec3(a.RawX % b.Raw, a.RawY % b.Raw, a.RawZ % b.Raw); } 101 | 102 | public static bool operator ==(F64Vec3 a, F64Vec3 b) { return a.RawX == b.RawX && a.RawY == b.RawY && a.RawZ == b.RawZ; } 103 | public static bool operator !=(F64Vec3 a, F64Vec3 b) { return a.RawX != b.RawX || a.RawY != b.RawY || a.RawZ != b.RawZ; } 104 | 105 | public static F64Vec3 Div(F64Vec3 a, F64 b) { long oob = Fixed64.Rcp(b.Raw); return new F64Vec3(Fixed64.Mul(a.RawX, oob), Fixed64.Mul(a.RawY, oob), Fixed64.Mul(a.RawZ, oob)); } 106 | public static F64Vec3 DivFast(F64Vec3 a, F64 b) { long oob = Fixed64.RcpFast(b.Raw); return new F64Vec3(Fixed64.Mul(a.RawX, oob), Fixed64.Mul(a.RawY, oob), Fixed64.Mul(a.RawZ, oob)); } 107 | public static F64Vec3 DivFastest(F64Vec3 a, F64 b) { long oob = Fixed64.RcpFastest(b.Raw); return new F64Vec3(Fixed64.Mul(a.RawX, oob), Fixed64.Mul(a.RawY, oob), Fixed64.Mul(a.RawZ, oob)); } 108 | public static F64Vec3 Div(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.Div(a.RawX, b.RawX), Fixed64.Div(a.RawY, b.RawY), Fixed64.Div(a.RawZ, b.RawZ)); } 109 | public static F64Vec3 DivFast(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.DivFast(a.RawX, b.RawX), Fixed64.DivFast(a.RawY, b.RawY), Fixed64.DivFast(a.RawZ, b.RawZ)); } 110 | public static F64Vec3 DivFastest(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.DivFastest(a.RawX, b.RawX), Fixed64.DivFastest(a.RawY, b.RawY), Fixed64.DivFastest(a.RawZ, b.RawZ)); } 111 | public static F64Vec3 SqrtPrecise(F64Vec3 a) { return new F64Vec3(Fixed64.SqrtPrecise(a.RawX), Fixed64.SqrtPrecise(a.RawY), Fixed64.SqrtPrecise(a.RawZ)); } 112 | public static F64Vec3 Sqrt(F64Vec3 a) { return new F64Vec3(Fixed64.Sqrt(a.RawX), Fixed64.Sqrt(a.RawY), Fixed64.Sqrt(a.RawZ)); } 113 | public static F64Vec3 SqrtFast(F64Vec3 a) { return new F64Vec3(Fixed64.SqrtFast(a.RawX), Fixed64.SqrtFast(a.RawY), Fixed64.SqrtFast(a.RawZ)); } 114 | public static F64Vec3 SqrtFastest(F64Vec3 a) { return new F64Vec3(Fixed64.SqrtFastest(a.RawX), Fixed64.SqrtFastest(a.RawY), Fixed64.SqrtFastest(a.RawZ)); } 115 | public static F64Vec3 RSqrt(F64Vec3 a) { return new F64Vec3(Fixed64.RSqrt(a.RawX), Fixed64.RSqrt(a.RawY), Fixed64.RSqrt(a.RawZ)); } 116 | public static F64Vec3 RSqrtFast(F64Vec3 a) { return new F64Vec3(Fixed64.RSqrtFast(a.RawX), Fixed64.RSqrtFast(a.RawY), Fixed64.RSqrtFast(a.RawZ)); } 117 | public static F64Vec3 RSqrtFastest(F64Vec3 a) { return new F64Vec3(Fixed64.RSqrtFastest(a.RawX), Fixed64.RSqrtFastest(a.RawY), Fixed64.RSqrtFastest(a.RawZ)); } 118 | public static F64Vec3 Rcp(F64Vec3 a) { return new F64Vec3(Fixed64.Rcp(a.RawX), Fixed64.Rcp(a.RawY), Fixed64.Rcp(a.RawZ)); } 119 | public static F64Vec3 RcpFast(F64Vec3 a) { return new F64Vec3(Fixed64.RcpFast(a.RawX), Fixed64.RcpFast(a.RawY), Fixed64.RcpFast(a.RawZ)); } 120 | public static F64Vec3 RcpFastest(F64Vec3 a) { return new F64Vec3(Fixed64.RcpFastest(a.RawX), Fixed64.RcpFastest(a.RawY), Fixed64.RcpFastest(a.RawZ)); } 121 | public static F64Vec3 Exp(F64Vec3 a) { return new F64Vec3(Fixed64.Exp(a.RawX), Fixed64.Exp(a.RawY), Fixed64.Exp(a.RawZ)); } 122 | public static F64Vec3 ExpFast(F64Vec3 a) { return new F64Vec3(Fixed64.ExpFast(a.RawX), Fixed64.ExpFast(a.RawY), Fixed64.ExpFast(a.RawZ)); } 123 | public static F64Vec3 ExpFastest(F64Vec3 a) { return new F64Vec3(Fixed64.ExpFastest(a.RawX), Fixed64.ExpFastest(a.RawY), Fixed64.ExpFastest(a.RawZ)); } 124 | public static F64Vec3 Exp2(F64Vec3 a) { return new F64Vec3(Fixed64.Exp2(a.RawX), Fixed64.Exp2(a.RawY), Fixed64.Exp2(a.RawZ)); } 125 | public static F64Vec3 Exp2Fast(F64Vec3 a) { return new F64Vec3(Fixed64.Exp2Fast(a.RawX), Fixed64.Exp2Fast(a.RawY), Fixed64.Exp2Fast(a.RawZ)); } 126 | public static F64Vec3 Exp2Fastest(F64Vec3 a) { return new F64Vec3(Fixed64.Exp2Fastest(a.RawX), Fixed64.Exp2Fastest(a.RawY), Fixed64.Exp2Fastest(a.RawZ)); } 127 | public static F64Vec3 Log(F64Vec3 a) { return new F64Vec3(Fixed64.Log(a.RawX), Fixed64.Log(a.RawY), Fixed64.Log(a.RawZ)); } 128 | public static F64Vec3 LogFast(F64Vec3 a) { return new F64Vec3(Fixed64.LogFast(a.RawX), Fixed64.LogFast(a.RawY), Fixed64.LogFast(a.RawZ)); } 129 | public static F64Vec3 LogFastest(F64Vec3 a) { return new F64Vec3(Fixed64.LogFastest(a.RawX), Fixed64.LogFastest(a.RawY), Fixed64.LogFastest(a.RawZ)); } 130 | public static F64Vec3 Log2(F64Vec3 a) { return new F64Vec3(Fixed64.Log2(a.RawX), Fixed64.Log2(a.RawY), Fixed64.Log2(a.RawZ)); } 131 | public static F64Vec3 Log2Fast(F64Vec3 a) { return new F64Vec3(Fixed64.Log2Fast(a.RawX), Fixed64.Log2Fast(a.RawY), Fixed64.Log2Fast(a.RawZ)); } 132 | public static F64Vec3 Log2Fastest(F64Vec3 a) { return new F64Vec3(Fixed64.Log2Fastest(a.RawX), Fixed64.Log2Fastest(a.RawY), Fixed64.Log2Fastest(a.RawZ)); } 133 | public static F64Vec3 Sin(F64Vec3 a) { return new F64Vec3(Fixed64.Sin(a.RawX), Fixed64.Sin(a.RawY), Fixed64.Sin(a.RawZ)); } 134 | public static F64Vec3 SinFast(F64Vec3 a) { return new F64Vec3(Fixed64.SinFast(a.RawX), Fixed64.SinFast(a.RawY), Fixed64.SinFast(a.RawZ)); } 135 | public static F64Vec3 SinFastest(F64Vec3 a) { return new F64Vec3(Fixed64.SinFastest(a.RawX), Fixed64.SinFastest(a.RawY), Fixed64.SinFastest(a.RawZ)); } 136 | public static F64Vec3 Cos(F64Vec3 a) { return new F64Vec3(Fixed64.Cos(a.RawX), Fixed64.Cos(a.RawY), Fixed64.Cos(a.RawZ)); } 137 | public static F64Vec3 CosFast(F64Vec3 a) { return new F64Vec3(Fixed64.CosFast(a.RawX), Fixed64.CosFast(a.RawY), Fixed64.CosFast(a.RawZ)); } 138 | public static F64Vec3 CosFastest(F64Vec3 a) { return new F64Vec3(Fixed64.CosFastest(a.RawX), Fixed64.CosFastest(a.RawY), Fixed64.CosFastest(a.RawZ)); } 139 | 140 | public static F64Vec3 Pow(F64Vec3 a, F64 b) { return new F64Vec3(Fixed64.Pow(a.RawX, b.Raw), Fixed64.Pow(a.RawY, b.Raw), Fixed64.Pow(a.RawZ, b.Raw)); } 141 | public static F64Vec3 PowFast(F64Vec3 a, F64 b) { return new F64Vec3(Fixed64.PowFast(a.RawX, b.Raw), Fixed64.PowFast(a.RawY, b.Raw), Fixed64.PowFast(a.RawZ, b.Raw)); } 142 | public static F64Vec3 PowFastest(F64Vec3 a, F64 b) { return new F64Vec3(Fixed64.PowFastest(a.RawX, b.Raw), Fixed64.PowFastest(a.RawY, b.Raw), Fixed64.PowFastest(a.RawZ, b.Raw)); } 143 | public static F64Vec3 Pow(F64 a, F64Vec3 b) { return new F64Vec3(Fixed64.Pow(a.Raw, b.RawX), Fixed64.Pow(a.Raw, b.RawY), Fixed64.Pow(a.Raw, b.RawZ)); } 144 | public static F64Vec3 PowFast(F64 a, F64Vec3 b) { return new F64Vec3(Fixed64.PowFast(a.Raw, b.RawX), Fixed64.PowFast(a.Raw, b.RawY), Fixed64.PowFast(a.Raw, b.RawZ)); } 145 | public static F64Vec3 PowFastest(F64 a, F64Vec3 b) { return new F64Vec3(Fixed64.PowFastest(a.Raw, b.RawX), Fixed64.PowFastest(a.Raw, b.RawY), Fixed64.PowFastest(a.Raw, b.RawZ)); } 146 | public static F64Vec3 Pow(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.Pow(a.RawX, b.RawX), Fixed64.Pow(a.RawY, b.RawY), Fixed64.Pow(a.RawZ, b.RawZ)); } 147 | public static F64Vec3 PowFast(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.PowFast(a.RawX, b.RawX), Fixed64.PowFast(a.RawY, b.RawY), Fixed64.PowFast(a.RawZ, b.RawZ)); } 148 | public static F64Vec3 PowFastest(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.PowFastest(a.RawX, b.RawX), Fixed64.PowFastest(a.RawY, b.RawY), Fixed64.PowFastest(a.RawZ, b.RawZ)); } 149 | 150 | public static F64 Length(F64Vec3 a) { return F64.FromRaw(Fixed64.Sqrt(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ))); } 151 | public static F64 LengthFast(F64Vec3 a) { return F64.FromRaw(Fixed64.SqrtFast(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ))); } 152 | public static F64 LengthFastest(F64Vec3 a) { return F64.FromRaw(Fixed64.SqrtFastest(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ))); } 153 | public static F64 LengthSqr(F64Vec3 a) { return F64.FromRaw(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ)); } 154 | public static F64Vec3 Normalize(F64Vec3 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrt(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ))); return ooLen * a; } 155 | public static F64Vec3 NormalizeFast(F64Vec3 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrtFast(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ))); return ooLen * a; } 156 | public static F64Vec3 NormalizeFastest(F64Vec3 a) { F64 ooLen = F64.FromRaw(Fixed64.RSqrtFastest(Fixed64.Mul(a.RawX, a.RawX) + Fixed64.Mul(a.RawY, a.RawY) + Fixed64.Mul(a.RawZ, a.RawZ))); return ooLen * a; } 157 | 158 | public static F64 Dot(F64Vec3 a, F64Vec3 b) { return F64.FromRaw(Fixed64.Mul(a.RawX, b.RawX) + Fixed64.Mul(a.RawY, b.RawY) + Fixed64.Mul(a.RawZ, b.RawZ)); } 159 | public static F64 Distance(F64Vec3 a, F64Vec3 b) { return Length(a - b); } 160 | public static F64 DistanceFast(F64Vec3 a, F64Vec3 b) { return LengthFast(a - b); } 161 | public static F64 DistanceFastest(F64Vec3 a, F64Vec3 b) { return LengthFastest(a - b); } 162 | 163 | public static F64Vec3 Min(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.Min(a.RawX, b.RawX), Fixed64.Min(a.RawY, b.RawY), Fixed64.Min(a.RawZ, b.RawZ)); } 164 | public static F64Vec3 Max(F64Vec3 a, F64Vec3 b) { return new F64Vec3(Fixed64.Max(a.RawX, b.RawX), Fixed64.Max(a.RawY, b.RawY), Fixed64.Max(a.RawZ, b.RawZ)); } 165 | 166 | public static F64Vec3 Clamp(F64Vec3 a, F64 min, F64 max) 167 | { 168 | return new F64Vec3( 169 | Fixed64.Clamp(a.RawX, min.Raw, max.Raw), 170 | Fixed64.Clamp(a.RawY, min.Raw, max.Raw), 171 | Fixed64.Clamp(a.RawZ, min.Raw, max.Raw)); 172 | } 173 | 174 | public static F64Vec3 Clamp(F64Vec3 a, F64Vec3 min, F64Vec3 max) 175 | { 176 | return new F64Vec3( 177 | Fixed64.Clamp(a.RawX, min.RawX, max.RawX), 178 | Fixed64.Clamp(a.RawY, min.RawY, max.RawY), 179 | Fixed64.Clamp(a.RawZ, min.RawZ, max.RawZ)); 180 | } 181 | 182 | public static F64Vec3 Lerp(F64Vec3 a, F64Vec3 b, F64 t) 183 | { 184 | long tb = t.Raw; 185 | long ta = Fixed64.One - tb; 186 | return new F64Vec3( 187 | Fixed64.Mul(a.RawX, ta) + Fixed64.Mul(b.RawX, tb), 188 | Fixed64.Mul(a.RawY, ta) + Fixed64.Mul(b.RawY, tb), 189 | Fixed64.Mul(a.RawZ, ta) + Fixed64.Mul(b.RawZ, tb)); 190 | } 191 | 192 | public static F64Vec3 Cross(F64Vec3 a, F64Vec3 b) 193 | { 194 | return new F64Vec3( 195 | Fixed64.Mul(a.RawY, b.RawZ) - Fixed64.Mul(a.RawZ, b.RawY), 196 | Fixed64.Mul(a.RawZ, b.RawX) - Fixed64.Mul(a.RawX, b.RawZ), 197 | Fixed64.Mul(a.RawX, b.RawY) - Fixed64.Mul(a.RawY, b.RawX)); 198 | } 199 | 200 | public bool Equals(F64Vec3 other) 201 | { 202 | return (this == other); 203 | } 204 | 205 | public override bool Equals(object obj) 206 | { 207 | if (!(obj is F64Vec3)) 208 | return false; 209 | return ((F64Vec3)obj) == this; 210 | } 211 | 212 | public override string ToString() 213 | { 214 | return "(" + Fixed64.ToString(RawX) + ", " + Fixed64.ToString(RawY) + ", " + Fixed64.ToString(RawZ) + ")"; 215 | } 216 | 217 | public override int GetHashCode() 218 | { 219 | return RawX.GetHashCode() ^ RawY.GetHashCode() * 7919 ^ RawZ.GetHashCode() * 4513; 220 | } 221 | } 222 | } 223 | -------------------------------------------------------------------------------- /Examples/FixMath/FixMath.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0 4 | FixPointCS 5 | AnyCPU;x64;x86 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /Examples/FixedTracer/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Examples/FixedTracer/DoubleTracer.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System; 3 | using System.Collections.Generic; 4 | 5 | // Based on https://blogs.msdn.microsoft.com/lukeh/2007/04/03/a-ray-tracer-in-c3-0/ 6 | 7 | namespace DoubleTracer 8 | { 9 | public class RayTracer 10 | { 11 | private int screenWidth; 12 | private int screenHeight; 13 | private const int MaxDepth = 5; 14 | 15 | public RayTracer(int screenWidth, int screenHeight) 16 | { 17 | this.screenWidth = screenWidth; 18 | this.screenHeight = screenHeight; 19 | } 20 | 21 | public static System.Drawing.Color[,] RenderDefaultScene(int width, int height) 22 | { 23 | RayTracer tracer = new RayTracer(width, height); 24 | return tracer.Render(tracer.DefaultScene); 25 | } 26 | 27 | private ISect IntersectRay(Ray ray, Scene scene) 28 | { 29 | ISect best = null; 30 | foreach (SceneObject obj in scene.Things) 31 | { 32 | ISect isect = obj.Intersect(ray); 33 | if (isect != null) 34 | { 35 | if (best == null || isect.Dist < best.Dist) 36 | best = isect; 37 | } 38 | } 39 | 40 | return best; 41 | } 42 | 43 | private double TestRay(Ray ray, Scene scene) 44 | { 45 | ISect isect = IntersectRay(ray, scene); 46 | if (isect == null) 47 | return 0; 48 | return isect.Dist; 49 | } 50 | 51 | private Color TraceRay(Ray ray, Scene scene, int depth) 52 | { 53 | ISect isect = IntersectRay(ray, scene); 54 | if (isect == null) 55 | return Color.Background; 56 | return Shade(isect, scene, depth); 57 | } 58 | 59 | private Color GetNaturalColor(SceneObject thing, Vector pos, Vector norm, Vector rd, Scene scene) 60 | { 61 | Color ret = Color.Make(0, 0, 0); 62 | foreach (Light light in scene.Lights) 63 | { 64 | Vector ldis = Vector.Minus(light.Pos, pos); 65 | Vector livec = Vector.Norm(ldis); 66 | double neatIsect = TestRay(new Ray() { Start = pos, Dir = livec }, scene); 67 | bool isInShadow = !((neatIsect > Vector.Mag(ldis)) || (neatIsect == 0)); 68 | if (!isInShadow) 69 | { 70 | double illum = Vector.Dot(livec, norm); 71 | Color lcolor = illum > 0 ? Color.Times(illum, light.Color) : Color.Make(0, 0, 0); 72 | double specular = Vector.Dot(livec, Vector.Norm(rd)); 73 | Color scolor = specular > 0 ? Color.Times(Math.Pow(specular, thing.Surface.Roughness), light.Color) : Color.Make(0, 0, 0); 74 | ret = Color.Plus(ret, Color.Plus(Color.Times(thing.Surface.Diffuse(pos), lcolor), 75 | Color.Times(thing.Surface.Specular(pos), scolor))); 76 | } 77 | } 78 | return ret; 79 | } 80 | 81 | private Color GetReflectionColor(SceneObject thing, Vector pos, Vector norm, Vector rd, Scene scene, int depth) 82 | { 83 | return Color.Times(thing.Surface.Reflect(pos), TraceRay(new Ray() { Start = pos, Dir = rd }, scene, depth + 1)); 84 | } 85 | 86 | private Color Shade(ISect isect, Scene scene, int depth) 87 | { 88 | var d = isect.Ray.Dir; 89 | var pos = Vector.Plus(Vector.Times(isect.Dist, isect.Ray.Dir), isect.Ray.Start); 90 | var normal = isect.Thing.Normal(pos); 91 | var reflectDir = Vector.Minus(d, Vector.Times(2 * Vector.Dot(normal, d), normal)); 92 | Color ret = Color.DefaultColor; 93 | ret = Color.Plus(ret, GetNaturalColor(isect.Thing, pos, normal, reflectDir, scene)); 94 | if (depth >= MaxDepth) 95 | { 96 | return Color.Plus(ret, Color.Make(.5, .5, .5)); 97 | } 98 | return Color.Plus(ret, GetReflectionColor(isect.Thing, Vector.Plus(pos, Vector.Times(.001, reflectDir)), normal, reflectDir, scene, depth)); 99 | } 100 | 101 | private double RecenterX(double x) 102 | { 103 | return (x - (screenWidth / 2.0)) / (2.0 * screenWidth); 104 | } 105 | private double RecenterY(double y) 106 | { 107 | return -(y - (screenHeight / 2.0)) / (2.0 * screenHeight); 108 | } 109 | 110 | private Vector GetPoint(double x, double y, Camera camera) 111 | { 112 | return Vector.Norm(Vector.Plus(camera.Forward, Vector.Plus(Vector.Times(RecenterX(x), camera.Right), 113 | Vector.Times(RecenterY(y), camera.Up)))); 114 | } 115 | 116 | internal System.Drawing.Color[,] Render(Scene scene) 117 | { 118 | System.Drawing.Color[,] pixels = new System.Drawing.Color[screenWidth, screenHeight]; 119 | for (int y = 0; y < screenHeight; y++) 120 | { 121 | for (int x = 0; x < screenWidth; x++) 122 | { 123 | Color color = TraceRay(new Ray() { Start = scene.Camera.Pos, Dir = GetPoint(x, y, scene.Camera) }, scene, 0); 124 | pixels[x, y] = color.ToDrawingColor(); 125 | } 126 | } 127 | return pixels; 128 | } 129 | 130 | internal readonly Scene DefaultScene = 131 | new Scene() 132 | { 133 | Things = new SceneObject[] { 134 | new Plane() { 135 | Norm = Vector.Make(0,1,0), 136 | Offset = 0, 137 | Surface = Surfaces.CheckerBoard 138 | }, 139 | new Sphere() { 140 | Center = Vector.Make(0,1,0), 141 | Radius = 1, 142 | Surface = Surfaces.Shiny 143 | }, 144 | new Sphere() { 145 | Center = Vector.Make(-1,.5,1.5), 146 | Radius = .5, 147 | Surface = Surfaces.Shiny 148 | }}, 149 | Lights = new Light[] { 150 | new Light() { 151 | Pos = Vector.Make(-2,2.5,0), 152 | Color = Color.Make(.49,.07,.07) 153 | }, 154 | new Light() { 155 | Pos = Vector.Make(1.5,2.5,1.5), 156 | Color = Color.Make(.07,.07,.49) 157 | }, 158 | new Light() { 159 | Pos = Vector.Make(1.5,2.5,-1.5), 160 | Color = Color.Make(.07,.49,.071) 161 | }, 162 | new Light() { 163 | Pos = Vector.Make(0,3.5,0), 164 | Color = Color.Make(.21,.21,.35) 165 | }}, 166 | Camera = Camera.Create(Vector.Make(3, 2, 4), Vector.Make(-1, .5, 0)) 167 | }; 168 | } 169 | 170 | static class Surfaces 171 | { 172 | // Only works with X-Z plane. 173 | public static readonly Surface CheckerBoard = 174 | new Surface() 175 | { 176 | Diffuse = pos => ((Math.Floor(pos.Z) + Math.Floor(pos.X)) % 2 != 0) 177 | ? Color.Make(1, 1, 1) 178 | : Color.Make(0, 0, 0), 179 | Specular = pos => Color.Make(1, 1, 1), 180 | Reflect = pos => ((Math.Floor(pos.Z) + Math.Floor(pos.X)) % 2 != 0) 181 | ? .1 182 | : .7, 183 | Roughness = 150 184 | }; 185 | 186 | 187 | public static readonly Surface Shiny = 188 | new Surface() 189 | { 190 | Diffuse = pos => Color.Make(1, 1, 1), 191 | Specular = pos => Color.Make(.5, .5, .5), 192 | Reflect = pos => .6, 193 | Roughness = 50 194 | }; 195 | } 196 | 197 | struct Vector 198 | { 199 | public double X; 200 | public double Y; 201 | public double Z; 202 | 203 | public Vector(double x, double y, double z) { X = x; Y = y; Z = z; } 204 | public Vector(string str) 205 | { 206 | string[] nums = str.Split(','); 207 | if (nums.Length != 3) throw new ArgumentException(); 208 | X = double.Parse(nums[0]); 209 | Y = double.Parse(nums[1]); 210 | Z = double.Parse(nums[2]); 211 | } 212 | public static Vector Make(double x, double y, double z) { return new Vector(x, y, z); } 213 | public static Vector Times(double n, Vector v) 214 | { 215 | return new Vector(v.X * n, v.Y * n, v.Z * n); 216 | } 217 | public static Vector Minus(Vector v1, Vector v2) 218 | { 219 | return new Vector(v1.X - v2.X, v1.Y - v2.Y, v1.Z - v2.Z); 220 | } 221 | public static Vector Plus(Vector v1, Vector v2) 222 | { 223 | return new Vector(v1.X + v2.X, v1.Y + v2.Y, v1.Z + v2.Z); 224 | } 225 | public static double Dot(Vector v1, Vector v2) 226 | { 227 | return (v1.X * v2.X) + (v1.Y * v2.Y) + (v1.Z * v2.Z); 228 | } 229 | public static double Mag(Vector v) { return Math.Sqrt(Dot(v, v)); } 230 | public static Vector Norm(Vector v) 231 | { 232 | double mag = Mag(v); 233 | double div = mag == 0 ? double.PositiveInfinity : 1 / mag; 234 | return Times(div, v); 235 | } 236 | public static Vector Cross(Vector v1, Vector v2) 237 | { 238 | return new Vector(((v1.Y * v2.Z) - (v1.Z * v2.Y)), 239 | ((v1.Z * v2.X) - (v1.X * v2.Z)), 240 | ((v1.X * v2.Y) - (v1.Y * v2.X))); 241 | } 242 | public static bool Equals(Vector v1, Vector v2) 243 | { 244 | return (v1.X == v2.X) && (v1.Y == v2.Y) && (v1.Z == v2.Z); 245 | } 246 | } 247 | 248 | public struct Color 249 | { 250 | public double R; 251 | public double G; 252 | public double B; 253 | 254 | public Color(double r, double g, double b) { R = r; G = g; B = b; } 255 | public Color(string str) 256 | { 257 | string[] nums = str.Split(','); 258 | if (nums.Length != 3) throw new ArgumentException(); 259 | R = double.Parse(nums[0]); 260 | G = double.Parse(nums[1]); 261 | B = double.Parse(nums[2]); 262 | } 263 | 264 | public static Color Make(double r, double g, double b) { return new Color(r, g, b); } 265 | 266 | public static Color Times(double n, Color v) 267 | { 268 | return new Color(n * v.R, n * v.G, n * v.B); 269 | } 270 | public static Color Times(Color v1, Color v2) 271 | { 272 | return new Color(v1.R * v2.R, v1.G * v2.G, v1.B * v2.B); 273 | } 274 | 275 | public static Color Plus(Color v1, Color v2) 276 | { 277 | return new Color(v1.R + v2.R, v1.G + v2.G, v1.B + v2.B); 278 | } 279 | public static Color Minus(Color v1, Color v2) 280 | { 281 | return new Color(v1.R - v2.R, v1.G - v2.G, v1.B - v2.B); 282 | } 283 | 284 | public static readonly Color Background = Make(0, 0, 0); 285 | public static readonly Color DefaultColor = Make(0, 0, 0); 286 | 287 | public double Legalize(double d) 288 | { 289 | return d > 1 ? 1 : d; 290 | } 291 | 292 | internal System.Drawing.Color ToDrawingColor() 293 | { 294 | return System.Drawing.Color.FromArgb((int)(Legalize(R) * 255), (int)(Legalize(G) * 255), (int)(Legalize(B) * 255)); 295 | } 296 | 297 | } 298 | 299 | struct Ray 300 | { 301 | public Vector Start; 302 | public Vector Dir; 303 | } 304 | 305 | class ISect 306 | { 307 | public SceneObject Thing; 308 | public Ray Ray; 309 | public double Dist; 310 | } 311 | 312 | class Surface 313 | { 314 | public Func Diffuse; 315 | public Func Specular; 316 | public Func Reflect; 317 | public double Roughness; 318 | } 319 | 320 | class Camera 321 | { 322 | public Vector Pos; 323 | public Vector Forward; 324 | public Vector Up; 325 | public Vector Right; 326 | 327 | public static Camera Create(Vector pos, Vector lookAt) 328 | { 329 | Vector forward = Vector.Norm(Vector.Minus(lookAt, pos)); 330 | Vector down = new Vector(0, -1, 0); 331 | Vector right = Vector.Times(1.5, Vector.Norm(Vector.Cross(forward, down))); 332 | Vector up = Vector.Times(1.5, Vector.Norm(Vector.Cross(forward, right))); 333 | 334 | return new Camera() { Pos = pos, Forward = forward, Up = up, Right = right }; 335 | } 336 | } 337 | 338 | class Light 339 | { 340 | public Vector Pos; 341 | public Color Color; 342 | } 343 | 344 | abstract class SceneObject 345 | { 346 | public Surface Surface; 347 | public abstract ISect Intersect(Ray ray); 348 | public abstract Vector Normal(Vector pos); 349 | } 350 | 351 | class Sphere : SceneObject 352 | { 353 | public Vector Center; 354 | public double Radius; 355 | 356 | public override ISect Intersect(Ray ray) 357 | { 358 | Vector eo = Vector.Minus(Center, ray.Start); 359 | double v = Vector.Dot(eo, ray.Dir); 360 | double dist; 361 | if (v < 0) 362 | { 363 | dist = 0; 364 | } 365 | else 366 | { 367 | double disc = Math.Pow(Radius, 2) - (Vector.Dot(eo, eo) - Math.Pow(v, 2)); 368 | dist = disc < 0 ? 0 : v - Math.Sqrt(disc); 369 | } 370 | if (dist == 0) return null; 371 | return new ISect() 372 | { 373 | Thing = this, 374 | Ray = ray, 375 | Dist = dist 376 | }; 377 | } 378 | 379 | public override Vector Normal(Vector pos) 380 | { 381 | return Vector.Norm(Vector.Minus(pos, Center)); 382 | } 383 | } 384 | 385 | class Plane : SceneObject 386 | { 387 | public Vector Norm; 388 | public double Offset; 389 | 390 | public override ISect Intersect(Ray ray) 391 | { 392 | double denom = Vector.Dot(Norm, ray.Dir); 393 | if (denom > 0) return null; 394 | return new ISect() 395 | { 396 | Thing = this, 397 | Ray = ray, 398 | Dist = (Vector.Dot(Norm, ray.Start) + Offset) / (-denom) 399 | }; 400 | } 401 | 402 | public override Vector Normal(Vector pos) 403 | { 404 | return Norm; 405 | } 406 | } 407 | 408 | class Scene 409 | { 410 | public SceneObject[] Things; 411 | public Light[] Lights; 412 | public Camera Camera; 413 | 414 | public IEnumerable Intersect(Ray r) 415 | { 416 | return from thing in Things 417 | select thing.Intersect(r); 418 | } 419 | } 420 | } 421 | -------------------------------------------------------------------------------- /Examples/FixedTracer/FixedTracer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | 4 | using FixPointCS; 5 | using FixMath; 6 | 7 | // Based on https://blogs.msdn.microsoft.com/lukeh/2007/04/03/a-ray-tracer-in-c3-0/ 8 | 9 | namespace FixedTracer 10 | { 11 | public static class RayTracer 12 | { 13 | private const int MaxDepth = 5; 14 | 15 | public static System.Drawing.Color[,] RenderDefaultScene(int width, int height, int numSamples) 16 | { 17 | return Render(width, height, numSamples, DefaultScene); 18 | } 19 | 20 | private static ISect IntersectRay(Ray ray, Scene scene) 21 | { 22 | F64 bestDist = F64.MaxValue; 23 | ISect best = null; 24 | 25 | foreach (SceneObject obj in scene.Things) 26 | { 27 | ISect isect = obj.Intersect(ray); 28 | if (isect != null) 29 | { 30 | if (isect.Dist < bestDist) 31 | { 32 | best = isect; 33 | bestDist = isect.Dist; 34 | } 35 | } 36 | } 37 | 38 | return best; 39 | } 40 | 41 | private static Color TraceRay(Ray ray, Scene scene, int depth) 42 | { 43 | ISect isect = IntersectRay(ray, scene); 44 | if (isect == null) 45 | return Color.Background; 46 | return Shade(ray, isect, scene, depth); 47 | } 48 | 49 | private static Color GetNaturalColor(Material material, F64Vec3 pos, F64Vec3 norm, F64Vec3 rd, Scene scene) 50 | { 51 | Color ret = Color.Black; 52 | foreach (Light light in scene.Lights) 53 | { 54 | F64Vec3 ldis = light.Pos - pos; 55 | F64Vec3 livec = F64Vec3.NormalizeFastest(ldis); 56 | ISect isect = IntersectRay(new Ray(pos, livec), scene); 57 | bool isInShadow = (isect != null) && (isect.Dist * isect.Dist) < F64Vec3.LengthSqr(ldis); 58 | if (!isInShadow) 59 | { 60 | F32 illum = F32.Max(F32.Zero, F64Vec3.Dot(livec, norm).F32); 61 | Color lcolor = illum * light.Color; 62 | F32 specular = F64Vec3.Dot(livec, F64Vec3.NormalizeFastest(rd)).F32; 63 | Color scolor = specular > 0 ? F32.PowFastest(specular, material.Roughness) * light.Color : Color.Black; 64 | ret += material.Diffuse * lcolor + material.Specular * scolor; 65 | } 66 | } 67 | return ret; 68 | } 69 | 70 | private static Color GetReflectionColor(Material material, F64Vec3 pos, F64Vec3 norm, F64Vec3 rd, Scene scene, int depth) 71 | { 72 | return material.Reflect * TraceRay(new Ray(pos, rd), scene, depth + 1); 73 | } 74 | 75 | private static Color Shade(Ray ray, ISect isect, Scene scene, int depth) 76 | { 77 | var pos = isect.Pos; 78 | var normal = isect.Normal; 79 | var reflectDir = ray.Dir - 2 * F64Vec3.Dot(normal, ray.Dir) * normal; 80 | Color ret = Color.Black; 81 | Material material = isect.Material; 82 | ret += GetNaturalColor(material, pos, normal, reflectDir, scene); 83 | if (depth >= MaxDepth) 84 | return ret + new Color(F32.Half, F32.Half, F32.Half); 85 | return ret + GetReflectionColor(material, pos + F64.Ratio(1, 1000) * reflectDir, normal, reflectDir, scene, depth); 86 | } 87 | 88 | internal static System.Drawing.Color[,] Render(int screenWidth, int screenHeight, int numSamples, Scene scene) 89 | { 90 | System.Drawing.Color[,] pixels = new System.Drawing.Color[screenWidth, screenHeight]; 91 | Camera camera = scene.Camera; 92 | F64 sx = F64.Ratio(1, 2 * screenWidth); 93 | F64 ox = F64.Half * F64.FromInt(screenWidth); 94 | F64 sy = -F64.Ratio(1, 2 * screenHeight); 95 | F64 oy = F64.Half * F64.FromInt(screenHeight); 96 | F32 ooNumSamples = F32.Ratio(1, numSamples); 97 | 98 | //for (int y = 0; y < screenHeight; y++) 99 | Parallel.For(0, screenHeight, y => 100 | { 101 | Random rnd = new Random(y * 29827341 + 23427343); 102 | for (int x = 0; x < screenWidth; x++) 103 | { 104 | Color accum = Color.Black; 105 | for (int i = 0; i < numSamples; i++) 106 | { 107 | // rx, ry == random offset in range [-0.5, 0.5] 108 | F64 rx = F64.FromRaw(rnd.Next()) * 2 - F64.Half; 109 | F64 ry = F64.FromRaw(rnd.Next()) * 2 - F64.Half; 110 | F64 xx = (F64.FromInt(x) + rx - ox) * sx; 111 | F64 yy = (F64.FromInt(y) + ry - oy) * sy; 112 | F64Vec3 rayDir = F64Vec3.Normalize(camera.Forward + xx * camera.Right + yy * camera.Up); 113 | Color color = TraceRay(new Ray(camera.Pos, rayDir), scene, 0); 114 | accum += color; 115 | } 116 | pixels[x, y] = (ooNumSamples * accum).ToDrawingColor(); 117 | } 118 | }); 119 | return pixels; 120 | } 121 | 122 | static F64Vec3 F64Vec3Ratio100(int x, int y, int z) 123 | { 124 | return new F64Vec3(F64.Ratio100(x), F64.Ratio100(y), F64.Ratio100(z)); 125 | } 126 | 127 | internal static readonly Scene DefaultScene = 128 | new Scene() 129 | { 130 | Things = new SceneObject[] 131 | { 132 | new Checkerboard(F64Vec3Ratio100(0_00, 1_00, 0_00), F64.Zero, Materials.White, Materials.Black), 133 | new Sphere(F64Vec3Ratio100(0_00, 1_00, 0_00), F64.One, Materials.Shiny), 134 | new Sphere(F64Vec3Ratio100(-1_00, 0_50, 1_50), F64.Half, Materials.Shiny), 135 | }, 136 | Lights = new Light[] 137 | { 138 | new Light(F64Vec3Ratio100(-2_00, 2_50, 0_00), Color.Ratio100(49, 7, 7)), 139 | new Light(F64Vec3Ratio100(1_50, 2_50, 1_50), Color.Ratio100(7, 7, 49)), 140 | new Light(F64Vec3Ratio100(1_50, 2_50, -1_50), Color.Ratio100(7, 49, 7)), 141 | new Light(F64Vec3Ratio100(0_00, 3_50, 0_00), Color.Ratio100(21, 21, 35)), 142 | }, 143 | Camera = Camera.Create(F64Vec3.FromInt(3, 2, 4), F64Vec3Ratio100(-1_00, 0_50, 0_00)) 144 | }; 145 | } 146 | 147 | static class Materials 148 | { 149 | public static readonly Material White = new Material 150 | { 151 | Diffuse = Color.White, 152 | Specular = Color.White, 153 | Reflect = F32.Ratio(1, 10), 154 | Roughness = F32.FromInt(150) 155 | }; 156 | 157 | public static readonly Material Black = new Material 158 | { 159 | Diffuse = Color.Black, 160 | Specular = Color.White, 161 | Reflect = F32.Ratio(7, 10), 162 | Roughness = F32.FromInt(150) 163 | }; 164 | 165 | public static readonly Material Shiny = new Material 166 | { 167 | Diffuse = Color.White, 168 | Specular = Color.Ratio100(50, 50, 50), 169 | Reflect = F32.Ratio(6, 10), 170 | Roughness = F32.FromInt(50) 171 | }; 172 | } 173 | 174 | public struct Color 175 | { 176 | public int RawR; 177 | public int RawG; 178 | public int RawB; 179 | 180 | private Color(int r, int g, int b) { RawR = r; RawG = g; RawB = b; } 181 | public Color(F32 r, F32 g, F32 b) { RawR = r.Raw; RawG = g.Raw; RawB = b.Raw; } 182 | 183 | public static Color Ratio100(int r, int g, int b) { return new Color((r << 16) / 100, (g << 16) / 100, (b << 16) / 100); } 184 | 185 | public static Color operator +(Color a, Color b) { return new Color(a.RawR + b.RawR, a.RawG + b.RawG, a.RawB + b.RawB); } 186 | public static Color operator -(Color a, Color b) { return new Color(a.RawR - b.RawR, a.RawG - b.RawG, a.RawB - b.RawB); } 187 | public static Color operator *(Color a, Color b) { return new Color(Fixed32.Mul(a.RawR, b.RawR), Fixed32.Mul(a.RawG, b.RawG), Fixed32.Mul(a.RawB, b.RawB)); } 188 | public static Color operator *(F32 a, Color b) { return new Color(Fixed32.Mul(a.Raw, b.RawR), Fixed32.Mul(a.Raw, b.RawG), Fixed32.Mul(a.Raw, b.RawB)); } 189 | public static Color operator *(Color a, F32 b) { return new Color(Fixed32.Mul(a.RawR, b.Raw), Fixed32.Mul(a.RawG, b.Raw), Fixed32.Mul(a.RawB, b.Raw)); } 190 | 191 | public static Color Black { get { return new Color(0, 0, 0); } } 192 | public static Color White { get { return new Color(Fixed32.One, Fixed32.One, Fixed32.One); } } 193 | public static Color Background { get { return new Color(F32.Ratio(4, 10), F32.Ratio(6, 10), F32.One); } } 194 | 195 | private int ToInt(int raw) 196 | { 197 | int clamped = Fixed32.Clamp(raw, 0, Fixed32.One); 198 | return Fixed32.RoundToInt(clamped * 255); 199 | } 200 | 201 | internal System.Drawing.Color ToDrawingColor() 202 | { 203 | return System.Drawing.Color.FromArgb(ToInt(RawR), ToInt(RawG), ToInt(RawB)); 204 | } 205 | } 206 | 207 | struct Ray 208 | { 209 | public readonly F64Vec3 Start; 210 | public readonly F64Vec3 Dir; 211 | 212 | public Ray(F64Vec3 start, F64Vec3 dir) 213 | { 214 | Start = start; 215 | Dir = dir; 216 | } 217 | } 218 | 219 | class ISect 220 | { 221 | public Material Material; 222 | public F64 Dist; 223 | public F64Vec3 Pos; 224 | public F64Vec3 Normal; 225 | } 226 | 227 | class Material 228 | { 229 | public Color Diffuse; 230 | public Color Specular; 231 | public F32 Reflect; 232 | public F32 Roughness; 233 | } 234 | 235 | class Camera 236 | { 237 | public F64Vec3 Pos; 238 | public F64Vec3 Forward; 239 | public F64Vec3 Up; 240 | public F64Vec3 Right; 241 | 242 | public static Camera Create(F64Vec3 pos, F64Vec3 lookAt) 243 | { 244 | F64Vec3 forward = F64Vec3.Normalize(lookAt - pos); 245 | F64Vec3 down = new F64Vec3(F64.Zero, F64.Neg1, F64.Zero); 246 | F64Vec3 right = F64.Ratio(15, 10) * F64Vec3.Normalize(F64Vec3.Cross(forward, down)); 247 | F64Vec3 up = F64.Ratio(15, 10) * F64Vec3.Normalize(F64Vec3.Cross(forward, right)); 248 | 249 | return new Camera() { Pos = pos, Forward = forward, Up = up, Right = right }; 250 | } 251 | } 252 | 253 | class Light 254 | { 255 | public F64Vec3 Pos; 256 | public Color Color; 257 | 258 | public Light(F64Vec3 pos, Color color) 259 | { 260 | Pos = pos; 261 | Color = color; 262 | } 263 | } 264 | 265 | abstract class SceneObject 266 | { 267 | public abstract ISect Intersect(Ray ray); 268 | } 269 | 270 | class Sphere : SceneObject 271 | { 272 | public readonly F64Vec3 Center; 273 | public readonly F64 Radius; 274 | public readonly Material Material; 275 | 276 | public Sphere(F64Vec3 center, F64 radius, Material material) 277 | { 278 | Center = center; 279 | Radius = radius; 280 | Material = material; 281 | } 282 | 283 | public override ISect Intersect(Ray ray) 284 | { 285 | F64Vec3 eo = Center - ray.Start; 286 | F64 v = F64Vec3.Dot(eo, ray.Dir); 287 | if (v < F64.Zero) 288 | return null; 289 | 290 | F64 disc = (Radius * Radius) - (F64Vec3.Dot(eo, eo) - (v * v)); 291 | F64 dist = disc < F64.Zero ? F64.Zero : v - F64.SqrtFast(disc); 292 | 293 | if (dist == F64.Zero) 294 | return null; 295 | 296 | F64Vec3 pos = ray.Start + dist * ray.Dir; 297 | return new ISect 298 | { 299 | Material = Material, 300 | Dist = dist, 301 | Pos = pos, 302 | Normal = F64Vec3.Normalize(pos - Center) 303 | }; 304 | } 305 | } 306 | 307 | class Checkerboard : SceneObject 308 | { 309 | public F64Vec3 Norm; 310 | public F64 Offset; 311 | public Material White; 312 | public Material Black; 313 | 314 | public Checkerboard(F64Vec3 normal, F64 offset, Material white, Material black) 315 | { 316 | Norm = normal; 317 | Offset = offset; 318 | White = white; 319 | Black = black; 320 | } 321 | 322 | public override ISect Intersect(Ray ray) 323 | { 324 | F64 epsilon = F64.FromRaw(1 << 24); 325 | F64 denom = F64Vec3.Dot(Norm, ray.Dir); 326 | if (denom > -epsilon) return null; 327 | 328 | F64 dist = (F64Vec3.Dot(Norm, ray.Start) + Offset) * F64.RcpFast(-denom); 329 | F64Vec3 pos = ray.Start + dist * ray.Dir; 330 | bool isWhite = ((F64.FloorToInt(pos.Z) + F64.FloorToInt(pos.X)) & 1) != 0; 331 | return new ISect 332 | { 333 | Material = isWhite ? White : Black, 334 | Pos = pos, 335 | Dist = dist, 336 | Normal = Norm 337 | }; 338 | } 339 | } 340 | 341 | class Scene 342 | { 343 | public SceneObject[] Things; 344 | public Light[] Lights; 345 | public Camera Camera; 346 | } 347 | } 348 | -------------------------------------------------------------------------------- /Examples/FixedTracer/FixedTracer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0-windows 5 | Exe 6 | AnyCPU;x64;x86 7 | true 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Examples/FixedTracer/Program.cs: -------------------------------------------------------------------------------- 1 | // 2 | // FixPointCS 3 | // 4 | // Copyright(c) Jere Sanisalo, Petri Kero 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | using System; 26 | using System.Diagnostics; 27 | using System.Drawing; 28 | using System.Windows.Forms; 29 | 30 | namespace RayTracer 31 | { 32 | public partial class RayTracerForm : Form 33 | { 34 | Bitmap bitmap; 35 | PictureBox pictureBox; 36 | const int width = 600; 37 | const int height = 600; 38 | 39 | public RayTracerForm() 40 | { 41 | bitmap = new Bitmap(width,height); 42 | 43 | pictureBox = new PictureBox(); 44 | pictureBox.Dock = DockStyle.Fill; 45 | pictureBox.SizeMode = PictureBoxSizeMode.StretchImage; 46 | pictureBox.Image = bitmap; 47 | 48 | ClientSize = new System.Drawing.Size(width, height + 24); 49 | Controls.Add(pictureBox); 50 | Text = "Ray Tracer"; 51 | Load += RayTracerForm_Load; 52 | 53 | Show(); 54 | } 55 | 56 | private void RayTracerForm_Load(object sender, EventArgs e) 57 | { 58 | this.Show(); 59 | 60 | for (int ndx = 0; ndx < 10; ndx++) 61 | { 62 | Stopwatch sw = Stopwatch.StartNew(); 63 | System.Drawing.Color[,] pixels = FixedTracer.RayTracer.RenderDefaultScene(width, height, 16); 64 | //System.Drawing.Color[,] pixels = DoubleTracer.RayTracer.RenderDefaultScene(width, height); 65 | sw.Stop(); 66 | Console.WriteLine("Elapsed: {0:0.00}s", sw.Elapsed.TotalSeconds); 67 | 68 | for (int y = 0; y < height; y++) 69 | for (int x = 0; x < width; x++) 70 | bitmap.SetPixel(x, y, pixels[x, y]); 71 | 72 | pictureBox.Refresh(); 73 | pictureBox.Invalidate(); 74 | } 75 | } 76 | 77 | [STAThread] 78 | static void Main() { 79 | Application.EnableVisualStyles(); 80 | 81 | Application.Run(new RayTracerForm()); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /FixPointCS.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio Version 16 3 | VisualStudioVersion = 16.0.31729.503 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FixPointCS", "FixPointCS\FixPointCS.csproj", "{53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FixPointCSTest", "FixPointCSTest\FixPointCSTest.csproj", "{9BDCDAF4-3D43-46C2-8085-E59749AB0592}" 8 | ProjectSection(ProjectDependencies) = postProject 9 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2} = {37DF7BEA-1665-482E-9BD5-63D09FF426B2} 10 | EndProjectSection 11 | EndProject 12 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Transpiler", "Transpiler\Transpiler.csproj", "{FAA807BE-4DA8-4A03-9C65-22AC74A83C92}" 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FixMath", "Examples\FixMath\FixMath.csproj", "{37DF7BEA-1665-482E-9BD5-63D09FF426B2}" 15 | ProjectSection(ProjectDependencies) = postProject 16 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1} = {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1} 17 | EndProjectSection 18 | EndProject 19 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FixedTracer", "Examples\FixedTracer\FixedTracer.csproj", "{657E3D4B-38AD-4961-9987-F27D14B677C5}" 20 | ProjectSection(ProjectDependencies) = postProject 21 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2} = {37DF7BEA-1665-482E-9BD5-63D09FF426B2} 22 | EndProjectSection 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Debug|x64 = Debug|x64 28 | Debug|x86 = Debug|x86 29 | Release|Any CPU = Release|Any CPU 30 | Release|x64 = Release|x64 31 | Release|x86 = Release|x86 32 | EndGlobalSection 33 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 34 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Debug|x64.ActiveCfg = Debug|x64 37 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Debug|x64.Build.0 = Debug|x64 38 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Debug|x86.ActiveCfg = Debug|x86 39 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Debug|x86.Build.0 = Debug|x86 40 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Release|Any CPU.ActiveCfg = Release|Any CPU 41 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Release|Any CPU.Build.0 = Release|Any CPU 42 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Release|x64.ActiveCfg = Release|x64 43 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Release|x64.Build.0 = Release|x64 44 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Release|x86.ActiveCfg = Release|x86 45 | {53D6F31F-AE20-4C5C-9674-0CA79E4C36C1}.Release|x86.Build.0 = Release|x86 46 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 47 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Debug|Any CPU.Build.0 = Debug|Any CPU 48 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Debug|x64.ActiveCfg = Debug|x64 49 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Debug|x64.Build.0 = Debug|x64 50 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Debug|x86.ActiveCfg = Debug|x86 51 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Debug|x86.Build.0 = Debug|x86 52 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Release|Any CPU.ActiveCfg = Release|Any CPU 53 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Release|Any CPU.Build.0 = Release|Any CPU 54 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Release|x64.ActiveCfg = Release|x64 55 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Release|x64.Build.0 = Release|x64 56 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Release|x86.ActiveCfg = Release|x86 57 | {9BDCDAF4-3D43-46C2-8085-E59749AB0592}.Release|x86.Build.0 = Release|x86 58 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 59 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Debug|Any CPU.Build.0 = Debug|Any CPU 60 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Debug|x64.ActiveCfg = Debug|x64 61 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Debug|x64.Build.0 = Debug|x64 62 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Debug|x86.ActiveCfg = Debug|x86 63 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Debug|x86.Build.0 = Debug|x86 64 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Release|Any CPU.ActiveCfg = Release|Any CPU 65 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Release|Any CPU.Build.0 = Release|Any CPU 66 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Release|x64.ActiveCfg = Release|x64 67 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Release|x64.Build.0 = Release|x64 68 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Release|x86.ActiveCfg = Release|x86 69 | {FAA807BE-4DA8-4A03-9C65-22AC74A83C92}.Release|x86.Build.0 = Release|x86 70 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 71 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Debug|Any CPU.Build.0 = Debug|Any CPU 72 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Debug|x64.ActiveCfg = Debug|x64 73 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Debug|x64.Build.0 = Debug|x64 74 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Debug|x86.ActiveCfg = Debug|x86 75 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Debug|x86.Build.0 = Debug|x86 76 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Release|Any CPU.ActiveCfg = Release|Any CPU 77 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Release|Any CPU.Build.0 = Release|Any CPU 78 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Release|x64.ActiveCfg = Release|x64 79 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Release|x64.Build.0 = Release|x64 80 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Release|x86.ActiveCfg = Release|x86 81 | {37DF7BEA-1665-482E-9BD5-63D09FF426B2}.Release|x86.Build.0 = Release|x86 82 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 83 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Debug|Any CPU.Build.0 = Debug|Any CPU 84 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Debug|x64.ActiveCfg = Debug|x64 85 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Debug|x64.Build.0 = Debug|x64 86 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Debug|x86.ActiveCfg = Debug|x86 87 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Debug|x86.Build.0 = Debug|x86 88 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Release|Any CPU.ActiveCfg = Release|Any CPU 89 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Release|Any CPU.Build.0 = Release|Any CPU 90 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Release|x64.ActiveCfg = Release|x64 91 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Release|x64.Build.0 = Release|x64 92 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Release|x86.ActiveCfg = Release|x86 93 | {657E3D4B-38AD-4961-9987-F27D14B677C5}.Release|x86.Build.0 = Release|x86 94 | EndGlobalSection 95 | GlobalSection(SolutionProperties) = preSolution 96 | HideSolutionNode = FALSE 97 | EndGlobalSection 98 | GlobalSection(ExtensibilityGlobals) = postSolution 99 | SolutionGuid = {E163824E-0159-48F8-AF9E-8030860D1890} 100 | EndGlobalSection 101 | EndGlobal 102 | -------------------------------------------------------------------------------- /FixPointCS/FixPointCS.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | net6.0 4 | FixPointCS 5 | AnyCPU;x64;x86 6 | 7 | 8 | -------------------------------------------------------------------------------- /FixPointCSTest/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /FixPointCSTest/FixPointCSTest.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net6.0 5 | Exe 6 | AnyCPU;x64;x86 7 | . 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Java/FixPointTest.java: -------------------------------------------------------------------------------- 1 | package fixpointcs.test; 2 | 3 | import fixpointcs.Fixed32; 4 | import fixpointcs.Fixed64; 5 | 6 | class Util 7 | { 8 | public static void Check(String opName, long output, long expected, long input0) 9 | { 10 | if (output != expected) 11 | System.out.printf("MISMATCH in %s: got %d, expected %d, inputs %d\n", opName, output, expected, input0); 12 | } 13 | 14 | public static void Check(String opName, long output, long expected, long input0, long input1) 15 | { 16 | if (output != expected) 17 | System.out.printf("MISMATCH in %s: got %d, expected %d, inputs %d %d\n", opName, output, expected, input0, input1); 18 | } 19 | 20 | public static void Check(String opName, int output, int expected, int input0) 21 | { 22 | if (output != expected) 23 | System.out.printf("MISMATCH in %s: got %d, expected %d, inputs %d\n", opName, output, expected, input0); 24 | } 25 | 26 | public static void Check(String opName, int output, int expected, int input0, int input1) 27 | { 28 | if (output != expected) 29 | System.out.printf("MISMATCH in %s: got %d, expected %d, inputs %d %d\n", opName, output, expected, input0, input1); 30 | } 31 | } 32 | 33 | public class FixPointTest 34 | { 35 | public static void main(String[] args) 36 | { 37 | System.out.println("Executing all unit tests.."); 38 | UnitTest.TestAll(); 39 | System.out.println("Unit tests finished!"); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Java/test.bat: -------------------------------------------------------------------------------- 1 | @mkdir classes 2 | @javac -d classes *.java || exit /b !ERRORLEVEL! 3 | @java -cp classes fixpointcs.test.FixPointTest 4 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright(c) Jere Sanisalo, Petri Kero 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /Polyfit/polyfit.py: -------------------------------------------------------------------------------- 1 | # 2 | # FixPointCS 3 | # 4 | # Copyright(c) Jere Sanisalo, Petri Kero 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | # OBSOLETE -- remez.py is used instead 26 | 27 | # Utility script for generating fitted polynomials to approximate various 28 | # trascendental functions for use in Fixed64.cs. 29 | # 30 | # The current algorithm optimize least squares error (LSE), which produces 31 | # good results. Better polynomials could be generated by optimizing for the 32 | # worst case error, for example with the Remez algorithm. 33 | # 34 | # The generated code may require some modifications like renaming the input 35 | # variable or dropping some zero constants. Also, not all the generated 36 | # polynomials can be calculated with s2.30 due to overflows. 37 | 38 | import numpy as np 39 | import numpy.polynomial.polynomial as poly 40 | import matplotlib.pyplot as plt 41 | 42 | FIT_SAMPLES = 500 # number of samples to use for fitting polynomials 43 | ERR_SAMPLES = 50000 # number of samples to use for computing error in fitted polynomials 44 | 45 | # see: https://stackoverflow.com/questions/15191088/how-to-do-a-polynomial-fit-with-fixed-points 46 | def polyfitWithFixedPoints(n, x, y, xf, yf): 47 | mat = np.empty((n + 1 + len(xf),) * 2) 48 | vec = np.empty((n + 1 + len(xf),)) 49 | x_n = x**np.arange(2 * n + 1)[:, None] 50 | yx_n = np.sum(x_n[:n + 1] * y, axis=1) 51 | x_n = np.sum(x_n, axis=1) 52 | idx = np.arange(n + 1) + np.arange(n + 1)[:, None] 53 | mat[:n + 1, :n + 1] = np.take(x_n, idx) 54 | xf_n = xf**np.arange(n + 1)[:, None] 55 | mat[:n + 1, n + 1:] = xf_n / 2 56 | mat[n + 1:, :n + 1] = xf_n.T 57 | mat[n + 1:, n + 1:] = 0 58 | vec[:n + 1] = yx_n 59 | vec[n + 1:] = yf 60 | params = np.linalg.solve(mat, vec) 61 | return params[:n + 1] 62 | 63 | def genQmul(inputName, ndx, order): 64 | if ndx == order: 65 | return f"Qmul30(C{ndx}, {inputName})" 66 | else: 67 | return f"Qmul30({genQmul(inputName, ndx+1, order)} + C{ndx}, {inputName})" 68 | 69 | def fitFunc(name, order, func, domain, fixed, inputName): 70 | (mn, mx) = domain 71 | x = mn + (mx - mn) * (np.arange(FIT_SAMPLES * 1.0) / (FIT_SAMPLES - 1)) 72 | y = func(x) 73 | 74 | # fit polynomial (with fixed points) 75 | coefs = polyfitWithFixedPoints(order, x, y, fixed, func(np.array(fixed))) 76 | fitted = poly.Polynomial(coefs) 77 | 78 | # compute max error 79 | ex = mn + (mx - mn) * (np.arange(ERR_SAMPLES * 1.0) / (ERR_SAMPLES - 1)) 80 | ey = func(ex) 81 | err = ey - fitted(ex) 82 | errmax = np.max(err) 83 | 84 | # print polynomial error and code 85 | print(f' order {order}: {-np.log2(errmax):0.4} bits') 86 | 87 | for ndx in range(len(coefs)): 88 | coef = coefs[ndx] 89 | if np.abs(coef) < 1e-12: 90 | coef = 0 91 | print(f" const int C{ndx} = {int(coef * (1 << 30))}; // {coef}") 92 | 93 | print(f" int y = {genQmul(inputName, 1, order)} + C0;") 94 | print() 95 | 96 | return fitted 97 | 98 | FUNCS = { 99 | 'sin': ((-1.0, 1.0), 'z', lambda x: np.sin(x * 0.5*np.pi), [-1.0, 0.0, 1.0]), 100 | # 'cos': ((0.0, 1.0), 'z', lambda x: np.cos(x * 0.5*np.pi), [0.0, 1.0]), 101 | 'atan': ((0.0, 1.0), 'k', lambda x: np.arctan(x), [0.0]), 102 | 'rcpm1': ((0.0, 1.0), 'k', lambda x: 1.0 / (x + 1), [0.0, 1.0]), 103 | 'sqrt': ((1.0, 2.0), 'n', lambda x: np.sqrt(x), [1.0, 2.0]), 104 | 'rsqrtm1': ((0.0, 1.0), 'k', lambda x: 1.0 / np.sqrt(x + 1), [0.0, 1.0]), 105 | 'logm1': ((0.0, 1.0), 'k', lambda x: np.log(x+1), [0.0, 1.0]), 106 | 'exp2': ((0.0, 1.0), 'k', lambda x: np.exp2(x), [0.0, 1.0]), 107 | } 108 | 109 | for name in FUNCS.keys(): 110 | if name != 'logm1': 111 | continue 112 | 113 | # extract func domain and function 114 | (domain, inputName, func, fixed) = FUNCS[name] 115 | (mn, mx) = domain 116 | 117 | print('') 118 | print(f'{name}({mn} .. {mx}):') 119 | 120 | for order in [3, 4, 5, 6, 7, 8, 9]: 121 | fitted = fitFunc(name, order, func, domain, fixed, inputName) 122 | if order >= 9: 123 | mne = mn + 0.0001 124 | x = mne + (mx - mne) * (np.arange(ERR_SAMPLES * 1.0) / (ERR_SAMPLES - 1)) 125 | plt.plot(x, (fitted(x) - func(x)) / func(x)) 126 | 127 | plt.show() 128 | -------------------------------------------------------------------------------- /Polyfit/remez.py: -------------------------------------------------------------------------------- 1 | # 2 | # FixPointCS 3 | # 4 | # Copyright(c) Jere Sanisalo, Petri Kero 5 | # 6 | # Permission is hereby granted, free of charge, to any person obtaining a copy 7 | # of this software and associated documentation files (the "Software"), to deal 8 | # in the Software without restriction, including without limitation the rights 9 | # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | # copies of the Software, and to permit persons to whom the Software is 11 | # furnished to do so, subject to the following conditions: 12 | # 13 | # The above copyright notice and this permission notice shall be included in all 14 | # copies or substantial portions of the Software. 15 | # 16 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | # SOFTWARE. 23 | # 24 | 25 | # Implementation of Remez algorithm for optimal polynomial fitting for usage in 26 | # function approximations. 27 | 28 | # References: 29 | # - https://github.com/samhocevar/lolremez 30 | # - https://www.cs.ox.ac.uk/files/1948/NA-08-20.pdf 31 | # - https://news.ycombinator.com/item?id=10115336 32 | # - http://sollya.gforge.inria.fr/ 33 | 34 | import math 35 | import random 36 | import mpmath as mp 37 | import numpy.polynomial as P 38 | import numpy.polynomial.chebyshev as C 39 | 40 | # set precision 41 | #dps = 30 42 | dps = 120 43 | mp.mp.dps = dps 44 | mp.mp.pretty = True 45 | 46 | def vzeros(size): 47 | return mp.matrix([0.0] * size) 48 | 49 | def vones(size): 50 | return mp.matrix([1.0] * size) 51 | 52 | def nth(size): 53 | return mp.matrix([0.0] * size + [1.0]) 54 | 55 | class Remez: 56 | def __init__(self, func, weightFunc, domain, order): 57 | #print('Remez()') 58 | self.func = func 59 | self.weightFunc = weightFunc 60 | self.domain = domain 61 | self.order = order 62 | self.limit = mp.mpf(f'1e-{dps}') 63 | self.errorThreshold = mp.mpf(f'1e-{dps}') 64 | self.initRemez() 65 | 66 | def initRemez(self): 67 | #print('Remez.init()') 68 | 69 | # constants for domain 70 | (xMin, xMax) = self.domain 71 | self.k1 = (xMax + xMin) * 0.5 72 | self.k2 = (xMax - xMin) * 0.5 73 | 74 | # initial estimates for function roots (where error == 0.0) 75 | size = self.order + 1 76 | roots = vzeros(size) 77 | fxn = vzeros(size) 78 | # \todo [petri] use linspace 79 | for i in range(size): 80 | roots[i] = (2.0 * i - self.order) / size 81 | fxn[i] = self.evalFunc(roots[i]) 82 | 83 | # build matrix of Chebyshev evaluations 84 | system = mp.zeros(size) 85 | for order in range(size): 86 | for i in range(size): 87 | system[i,order] = mp.chebyt(order, roots[i]) 88 | 89 | # solve system 90 | solved = system ** -1 91 | 92 | # compute Chebyshev weights of new approximation 93 | weights = vzeros(size) 94 | for n in range(size): 95 | weights[n] = mp.fdot(solved[n,:], fxn) 96 | #print(f' weights: {weights.T}') 97 | 98 | # store roots & weights 99 | self.roots = roots 100 | self.weights = weights 101 | self.maxError = 1000.0 102 | 103 | # estimate error 104 | #est = [self.evalEstimate(x) for x in roots] 105 | #print(' est:', est) 106 | #print(' fxn:', fxn.T) 107 | 108 | def step(self): 109 | (control, maxErr) = self.findExtrema(self.roots) 110 | #print(f' maxErr: {maxErr} ({-math.log(maxErr, 2.0)} bits)') 111 | # for x in control: 112 | # print(f' ctrl: {x}') 113 | self.weights = self.remezStep(control) 114 | 115 | # update error 116 | errDiff = mp.fabs(maxErr - self.maxError) 117 | self.maxError = maxErr 118 | 119 | if errDiff < self.errorThreshold: 120 | return True 121 | 122 | self.roots = self.findRoots(control) 123 | return False 124 | 125 | # for (ax, bx, rx, rerr) in scannedRoots: 126 | # print(f' scanned: {rx} in [{ax, bx}]') 127 | 128 | # scannedRoots = self.scanRoots(1000) 129 | # for ndx in range(len(self.roots)): 130 | # x = self.roots[ndx] 131 | # (ax, bx, rx, rerr) = scannedRoots[ndx] 132 | # print(f' root: {x} [{ax}, {bx}] {rx}') 133 | # if x < ax or x > bx: 134 | # print(' ROOT FIND MISMATCH') 135 | 136 | def findRoot(self, ax, bx, aerr, berr): 137 | cx = (ax + bx) * 0.5 138 | cerr = self.evalEstimate(cx) - self.evalFunc(cx) 139 | 140 | if cerr == 0.0 or (bx - ax) <= self.limit: 141 | return (cx, cerr) 142 | else: 143 | if (aerr < 0.0 and cerr < 0.0) or (aerr > 0.0 and cerr > 0.0): 144 | (ax, aerr) = (cx, cerr) 145 | else: 146 | (bx, berr) = (cx, cerr) 147 | return self.findRoot(ax, bx, aerr, berr) 148 | 149 | def findRoots(self, control): 150 | roots = vzeros(self.order + 1) 151 | for n in range(self.order + 1): 152 | ax = control[n] 153 | bx = control[n+1] 154 | aerr = self.evalEstimate(ax) - self.evalFunc(ax) 155 | berr = self.evalEstimate(bx) - self.evalFunc(bx) 156 | (rx, rerr) = self.findRoot(ax, bx, aerr, berr) 157 | assert(rx >= ax and rx <= bx) # root must be inside search range 158 | roots[n] = rx 159 | #print(f' root[{n}]: {rx} <{rerr}> || {ax} {bx}') 160 | return roots 161 | 162 | def remezStep(self, control): 163 | # eval at control points 164 | fxn = mp.matrix([self.evalFunc(c) for c in control]) 165 | 166 | # create linear system with chebyshev polynomials 167 | size = self.order + 2 168 | system = mp.zeros(size) 169 | for n in range(self.order + 1): 170 | for i in range(self.order + 2): 171 | system[i,n] = mp.chebyt(n, control[i]) 172 | 173 | # last column is oscillating error 174 | for i in range(size): 175 | sign = -1 if ((i & 1) == 0) else +1 176 | scale = 0.0 if i in [0, size-1] else 1.0 177 | system[i,size-1] = sign * scale * mp.fabs(self.evalWeight(control[i])) 178 | #print(system) 179 | 180 | # solve the system 181 | solved = system ** -1 182 | #print(solved) 183 | 184 | # compute polynomial estimate (as Chebyshev weights) 185 | weights = vzeros(size-1) 186 | for n in range(size-1): 187 | weights[n] = mp.fdot(solved[n,:], fxn) 188 | #print(f' weights: {weights}') 189 | 190 | # estimate error 191 | # self.weights = weights 192 | # est = [self.evalEstimate(x) for x in control] 193 | # print(' est:', est) 194 | # print(' fxn:', fxn.T) 195 | 196 | return weights 197 | 198 | def findExtremum(self, ax, bx, cx, aerr, berr, cerr): 199 | # \todo [petri] implement golden ratio search? 200 | 201 | dx = (ax + bx) * 0.5 202 | derr = self.evalError(dx) 203 | 204 | # update coords based on error 205 | if derr < cerr: 206 | if dx > cx: 207 | (bx, berr) = (dx, derr) 208 | else: 209 | (ax, aerr) = (dx, derr) 210 | else: 211 | if dx > cx: 212 | (ax, aerr) = (cx, cerr) 213 | else: 214 | (bx, berr) = (cx, cerr) 215 | (cx, cerr) = (dx, derr) 216 | 217 | # check if limit reached 218 | if (bx - ax) <= self.limit: 219 | #print(f' fin: {cx} <{cerr}> || {ax} {bx}') 220 | return (cx, cerr) 221 | else: 222 | #print(f' cur: {cx} <{cerr}>') 223 | return self.findExtremum(ax, bx, cx, aerr, berr, cerr) 224 | 225 | def findExtrema(self, roots): 226 | control = vzeros(self.order + 2) 227 | control[0] = -1 228 | control[self.order + 1] = 1 229 | 230 | maxErr = 0.0 231 | 232 | for n in range(self.order): 233 | ax = roots[n] 234 | bx = roots[n+1] 235 | cx = ax + (bx - ax) * random.uniform(0.4, 0.6) 236 | aerr = self.evalError(ax) 237 | berr = self.evalError(bx) 238 | cerr = self.evalError(cx) 239 | #print(f' find[{n}]: {ax}, {bx}') 240 | (rx, rerr) = self.findExtremum(ax, bx, cx, aerr, berr, cerr) 241 | assert(rx >= ax and rx <= bx) # extremum must be inside search range 242 | #print(f' extrema[{n}]: {rx} <{rerr}> || {ax} {bx}') 243 | 244 | control[n + 1] = rx 245 | maxErr = max(maxErr, rerr) 246 | 247 | return (control, maxErr) 248 | 249 | def scanRoots(self, numSteps): 250 | found = [] 251 | coords = mp.linspace(-1.0, 1.0, numSteps) 252 | for ndx in range(len(coords)-1): 253 | ax = coords[ndx] 254 | bx = coords[ndx+1] 255 | aerr = self.evalEstimate(ax) - self.evalFunc(ax) 256 | berr = self.evalEstimate(bx) - self.evalFunc(bx) 257 | #print(f'bucket: {ax} <{aerr}>') 258 | #print(f' to: {bx} <{berr}>') 259 | if mp.sign(aerr) != mp.sign(berr): 260 | (rx, rerr) = self.findRoot(ax, bx, aerr, berr) 261 | #print(f' root in range: [{ax}, {bx}]: {rx} <{rerr}>') 262 | found.append((ax, bx, rx, rerr)) 263 | return found 264 | 265 | def evalFunc(self, x): 266 | return self.func(x * self.k2 + self.k1) 267 | 268 | def evalWeight(self, x): 269 | return self.weightFunc(x * self.k2 + self.k1) 270 | 271 | def evalEstimate(self, x): 272 | sum = mp.mpf(0.0) 273 | for i in range(len(self.weights)): 274 | sum += self.weights[i] * mp.chebyt(i, x) 275 | return sum 276 | 277 | def evalError(self, x): 278 | return mp.fabs(self.evalEstimate(x) - self.evalFunc(x)) / self.evalWeight(x) 279 | 280 | # Main 281 | 282 | MAX_REMEZ_ITER = 10 283 | 284 | # convert polynomials to assume input in [0.0, 1.0] range (instead of [-1.0, 1.0]) 285 | rebase = P.Polynomial([-1.0, 2.0]) 286 | 287 | def remezFit(name, func, weightFunc, domain, order): 288 | remez = Remez(func, weightFunc, domain, order) 289 | 290 | for iter in range(MAX_REMEZ_ITER): 291 | #print(f'ITER {iter}:') 292 | if remez.step(): 293 | break 294 | 295 | return remez 296 | 297 | def remezToPoly(remez): 298 | cheby = C.Chebyshev(remez.weights) 299 | p = cheby.convert(kind=P.Polynomial) 300 | p = p(rebase) 301 | # (x0, x1) = remez.domain 302 | # p = p(P.Polynomial([-x0 / (x1 - x0), 1.0 / (x1 - x0)])) 303 | return p 304 | 305 | def writeCoefficients(file, name, maxError, order, segments): 306 | file.write('\n') 307 | 308 | if len(segments) == 1: 309 | precision = -math.log(maxError, 2.0) 310 | file.write(f'\t\t// Precision: {precision:.2f} bits\n') 311 | file.write('\t\t[MethodImpl(AggressiveInlining)]\n') 312 | file.write(f'\t\tpublic static int {name}Poly{order}(int a)\n') 313 | file.write('\t\t{\n') 314 | 315 | # get polynomial 316 | p = remezToPoly(segments[0]) 317 | 318 | for ndx in reversed(range(order + 1)): 319 | c = p.coef[ndx] 320 | ic = (int)(c * (1 << 30) + 0.5) 321 | # file.write(f'\t\t\tconst int C{ndx} = {ic}; // {c}\n') 322 | 323 | if ndx == len(p.coef)-1: 324 | file.write(f'\t\t\tint y = Qmul30(a, {ic}); // {c}\n') 325 | elif ndx > 0: 326 | file.write(f'\t\t\ty = Qmul30(a, y + {ic}); // {c}\n') 327 | else: 328 | file.write(f'\t\t\ty = y + {ic}; // {c}\n') 329 | 330 | file.write('\t\t\treturn y;\n') 331 | 332 | else: 333 | numSegments = len(segments) 334 | precision = -math.log(maxError, 2.0) 335 | funcName = f'{name}Poly{order}Lut{numSegments}' 336 | tableName = f'{funcName}Table' 337 | 338 | # write constant table 339 | file.write(f'\t\tprivate static readonly int[] {tableName} =\n') 340 | file.write('\t\t{\n') 341 | 342 | for remez in segments: 343 | # get polynomial 344 | p = remezToPoly(remez) 345 | # map [x0, x1] to [0.0, 1.0] 346 | (x0, x1) = remez.domain 347 | p = p(P.Polynomial([-x0 / (x1 - x0), 1.0 / (x1 - x0)])) 348 | # write coefficients 349 | coefs = ' '.join(f'{int(c * (1<<30) + 0.5)},' for c in reversed(p.coef)) 350 | file.write(f'\t\t\t{coefs}\n') 351 | 352 | file.write('\t\t};\n') 353 | 354 | # write function 355 | file.write('\n') 356 | file.write(f'\t\t// Precision: {precision:.2f} bits\n') 357 | file.write('\t\t[MethodImpl(AggressiveInlining)]\n') 358 | file.write(f'\t\tpublic static int {funcName}(int a)\n') 359 | file.write('\t\t{\n') 360 | 361 | segmentBits = int(math.log2(numSegments)) 362 | file.write(f'\t\t\tint offset = (a >> {30 - segmentBits}) * {order + 1};\n') 363 | 364 | for ndx in reversed(range(order + 1)): 365 | if ndx == order: 366 | file.write(f'\t\t\tint y = Qmul30(a, {tableName}[offset + {0}]);\n') 367 | elif ndx > 0: 368 | file.write(f'\t\t\ty = Qmul30(a, y + {tableName}[offset + {order - ndx}]);\n') 369 | else: 370 | file.write(f'\t\t\ty = y + {tableName}[offset + {order}];\n') 371 | 372 | file.write('\t\t\treturn y;\n') 373 | 374 | file.write('\t\t}\n') 375 | file.flush() 376 | 377 | def remezFitSegmented(name, func, weightFunc, domain, numSegments, order): 378 | (xMin, xMax) = domain 379 | maxError = 0.0 380 | 381 | segments = [] 382 | for segment in range(numSegments): 383 | x0 = xMin + (xMax - xMin) * mp.mpf(segment) / numSegments 384 | x1 = xMin + (xMax - xMin) * mp.mpf(segment + 1) / numSegments 385 | remez = remezFit(name, func, weightFunc, (x0, x1), order) 386 | #print(f' segment[{segment}]: {-math.log(remez.maxError, 2.0):.2f} bits [{x0} .. {x1}]') 387 | maxError = max(maxError, remez.maxError) 388 | segments.append(remez) 389 | 390 | return (maxError, segments) 391 | 392 | epsilon = mp.mpf(f'1e-{dps//3}') 393 | 394 | # Implementation of sin() with the assumption that input has been normalized to [-1.0, 1.0] range 395 | # and squared. Also assumes the output will be multiplied by x once more (to make it odd). 396 | # See: https://github.com/samhocevar/lolremez/wiki/Tutorial-3-of-5:-changing-variables-for-simpler-polynomials 397 | def sinSqr(x): 398 | x = x * 0.25 * mp.pi * mp.pi 399 | xx = mp.sqrt(x) 400 | y = mp.sin(xx) / xx 401 | return y * 0.5*mp.pi 402 | 403 | FUNCS = [ 404 | ('Exp', lambda x: mp.exp(x), lambda x: mp.exp(x), (0.0, 1.0)), 405 | ('Exp2', lambda x: 2.0**x, lambda x: 2.0**x, (0.0, 1.0)), 406 | ('Log', lambda x: mp.log(x+1), lambda x: mp.log(x+1) * (mp.log(2.0) - mp.log(x+1)), (epsilon, 1.0-epsilon)), 407 | ('Log2', lambda x: mp.log(x+1, 2.0), lambda x: mp.log(x+1, 2.0) * (1 - mp.log(x+1, 2.0)), (epsilon, 1.0-epsilon)), 408 | ('Rcp', lambda x: 1.0 / (x+1), lambda x: 1.0 / (x+1), (0.0, 1.0)), 409 | ('Sqrt', lambda x: mp.sqrt(x+1), lambda x: mp.sqrt(x+1), (0.0, 1.0)), 410 | ('RSqrt', lambda x: 1.0 / mp.sqrt(x+1), lambda x: 1.0 / mp.sqrt(x+1), (0.0, 1.0)), 411 | ('Atan', lambda x: mp.atan(x), lambda x: mp.atan(x), (0.0, 1.0)), 412 | ('Sin', sinSqr, sinSqr, (epsilon, 1.0)), 413 | ] 414 | 415 | def generateCode(): 416 | with open(f'fitted.txt', 'w', newline='\n') as file: 417 | file.write('\t// AUTO-GENERATED POLYNOMIAL APPROXIMATIONS\n') 418 | file.write('\n') 419 | file.write('\tpublic static class Util\n') 420 | file.write('\t{\n') 421 | 422 | for (name, func, weightFunc, domain) in FUNCS: 423 | print() 424 | print(f'{name}():') 425 | orderRange = range(3, 10) if name != 'Sin' else range(1, 5) 426 | # orderRange = range(3, 6) if name != 'sin' else range(1, 4) 427 | for order in orderRange: 428 | remez = remezFit(name, func, weightFunc, domain, order) 429 | print(f' {name}<{order}>(): {-math.log(remez.maxError, 2.0):.2f} bits') 430 | writeCoefficients(file, name, remez.maxError, order, [remez]) 431 | 432 | for numSegments in [4, 8, 16, 32]: 433 | orders = [2, 3, 4, 5] if name != 'Sin' else [1, 2, 3] 434 | # orders = [3, 4] if name != 'sin' else [1, 2, 3] 435 | for order in orders: 436 | (maxError, segments) = remezFitSegmented(name, func, weightFunc, domain, numSegments, order) 437 | print(f' {name}<{order}>[{numSegments}](): {-math.log(maxError, 2.0):.2f} bits') 438 | writeCoefficients(file, name, maxError, order, segments) 439 | 440 | file.write('\t}\n') 441 | 442 | def plotError(): 443 | print('Plotting error..') 444 | #func = lambda x: 1.0 / (x + 1.0) 445 | func = sinSqr 446 | remez = remezFit('Sin', func, func, (epsilon, 1.0), 4) 447 | # remez = remezFit('Rcp', func, func, (1.0, 2.0), 3) 448 | print('err:', remez.maxError) 449 | est = remezToPoly(remez) 450 | err = lambda x: (func(x) - est(x)) / func(x) 451 | mp.plot([err], [0.0, 1.0]) 452 | #mp.plot([func], [0.0, 1.0]) 453 | 454 | # Main 455 | 456 | generateCode() 457 | #plotError() 458 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FixPointCS 2 | 3 | #### A fast, multi-language, multi-precision fixed-point library! 4 | 5 | --- 6 | 7 | ## Key Features 8 | 9 | - **Deterministic**: Operations produce bit-identical results across languages and compilers 10 | - **Fast**: All operations use efficient algorithms and are highly optimized 11 | - **Multi-Language**: Supports C#, Java, and C++ 12 | - **Multiple Types**: Supports signed 32.32 and 16.16 fixed-point numbers 13 | - **Multiple Precisions**: Operations provide variants with 24, 16, and 10 bits of precision 14 | - **Extensive**: All standard math functions supported, except hyperbolic trigonometry 15 | - **Well Tested**: The library comes with a comprehensive performance and precision testing suite 16 | 17 | ## Overview 18 | 19 | FixPointCS aims to provide as robust and efficient core math operations as possible, while keeping 20 | the API simple to stay compatible with multiple languages. It is intended to be a building block 21 | for higher-level math libraries, not to be used directly from application code. 22 | 23 | For convenience, FixPointCS does include a higher-level math library as well (only for C#). It is 24 | provided as an example on how to utilize the core primitives. It also be used as-is, or customized 25 | to a project's needs. The convenience library is located in *Examples/FixMath*. 26 | 27 | ## Getting Started 28 | 29 | ### Core Math Library (all languages) 30 | 31 | The simplest and recommended way to use FixPointCS is to copy the relevant source files directly into 32 | your project. The core math operations reside in the following files: 33 | - For **C#**: FixPointCS/Fixed32.cs, FixPointCS/Fixed64.cs and FixPointCS/FixedUtil.cs 34 | - For **Java**: Java/Fixed32.java, Java/Fixed64.java and Java/FixedUtil.java 35 | - For **C++**: Cpp/Fixed64.h, Cpp/Fixed32.h and Cpp/FixedUtil.h 36 | 37 | ### FixMath Convenience Library (C# only) 38 | 39 | For C#, you can also use the provided higher-level math library, located under *Examples/FixMath*. In 40 | order to get started writing applications as easily as possible, using the example library is recommended. 41 | 42 | The FixMath library provides basic scalar types (*F32* and *F64*, signed 16.16 and 32.32 fixed-point 43 | values, respectively), as well as vector types (*F32VecN* and *F64VecN*). 44 | 45 | For examples on how to use the FixMath library, see *Examples/FixedTracer/FixedTracer.cs*, which 46 | implements a simple fixed-point raytracer. 47 | 48 | ## Supported Functions 49 | 50 | Supported operations include: 51 | - Arithmetic: Add(), Sub(), Mul(), Div(), Rcp() (reciprocal), Mod() (modulo) 52 | - Trigonometry: Sin(), Cos(), Tan(), Asin(), Acos(), Atan(), Atan2() 53 | - Exponential: Exp(), Exp2(), Log(), Log2(), Pow() 54 | - Square root: Sqrt(), RSqrt() (reciprocal square root) 55 | - Utility: Abs(), Nabs(), Sign(), Ceil(), Floor(), Round(), Fract(), Min(), Max(), Clamp(), Lerp() 56 | - Conversions: CeilToInt(), FloorToInt(), RoundToInt(), FromDouble(), FromFloat(), ToDouble(), ToFloat() 57 | 58 | Note that the conversions to/from floating point values are not guaranteed to be deterministic. 59 | 60 | For full list of supported operations, see [functions.md](functions.md). 61 | 62 | ## Precision Guide 63 | 64 | The library supports both signed 32.32 fixed-point type (Fixed64), and signed 16.16 fixed-point (Fixed32). 65 | For each operation, for both types, each approximate function comes with three precision variants with 66 | different speed/precision trade off. For example, *Sin()* has 24 bits of precision, *SinFast()* has 16 67 | bits of precision and *SinFastest()* has 10 bits of precision. 68 | 69 | The precision is relative to the output value, so for example 10 bits of precision means that the answer 70 | is accurate to about 1 part in 1000 (or has error margin of 0.1%). The Fast variant is typically about 71 | 10-15% faster than the highest precision, and the Fastest variant about 20-30% faster. See 72 | [functions.md](functions.md) for details. 73 | 74 | Div and Sqrt also come with a Precise variant (*DivPrecise()*, *SqrtPrecise()*), which produce a result 75 | that is exactly correct within representable fixed-point numbers. 76 | 77 | ## Known Limitations 78 | 79 | - Few operations are much slower without a 64-bit CPU, most notably s32.32 multiply and division 80 | - The library opts for performance and determinism over full correctness in edge cases like overflows 81 | 82 | ## License 83 | 84 | Available under MIT license, see LICENSE.txt for details. 85 | -------------------------------------------------------------------------------- /Transpiler/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /Transpiler/GenerateCpp.cs: -------------------------------------------------------------------------------- 1 | // 2 | // FixPointCS 3 | // 4 | // Copyright(c) Jere Sanisalo, Petri Kero 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | using System; 25 | using System.Collections.Generic; 26 | using System.IO; 27 | using System.Linq; 28 | using System.Text; 29 | using System.Text.RegularExpressions; 30 | using System.Threading.Tasks; 31 | 32 | namespace Transpiler 33 | { 34 | public static class GenerateCpp 35 | { 36 | public enum Mode 37 | { 38 | Fp32, 39 | Fp64, 40 | Util, 41 | } 42 | 43 | public static void ConvertFile(string inPath, string outPath, Mode mode) 44 | { 45 | Console.WriteLine("Generating {0}..", outPath); 46 | 47 | // Read the file 48 | string[] lines = File.ReadAllLines(inPath); 49 | 50 | // Prepare output 51 | StringBuilder sb = new StringBuilder(); 52 | 53 | // Create the prefix and suffix 54 | string prefix = MakePrefix(mode); 55 | string suffix = MakeSuffix(mode); 56 | 57 | // Run hacky preprocessor 58 | lines = Util.Preprocess(lines, "CPP"); 59 | lines = Util.Unindent(lines, 1); 60 | 61 | foreach (string line_in in lines) 62 | { 63 | string line = line_in; 64 | string line_trimmed = line.Trim(); 65 | 66 | // Ignore [...] -lines (attributes) 67 | if (line_trimmed.StartsWith("[")) 68 | continue; 69 | 70 | // Process the line 71 | line = Util.ReplaceWholeWord(line, "public", ""); 72 | line = Util.ReplaceWholeWord(line, "private", ""); 73 | line = Util.ReplaceWholeWord(line, "const", "static const"); 74 | line = Util.ReplaceWholeWord(line, "out int", "int&"); 75 | line = Util.ReplaceWholeWord(line, "out uint", "uint&"); 76 | line = Util.ReplaceWholeWord(line, "out long", "long&"); 77 | line = Util.ReplaceWholeWord(line, "out ulong", "ulong&"); 78 | line = Util.ReplaceWholeWord(line, "out", ""); 79 | line = Util.ReplaceWholeWord(line, "int", "FP_INT"); 80 | line = Util.ReplaceWholeWord(line, "uint", "FP_UINT"); 81 | line = Util.ReplaceWholeWord(line, "long", "FP_LONG"); 82 | line = Util.ReplaceWholeWord(line, "ulong", "FP_ULONG"); 83 | line = Util.ReplaceWholeWord(line, "Debug.Assert", "FP_ASSERT"); 84 | line = line.Replace("FixedUtil.", "FixedUtil::"); 85 | line = line.Replace("Fixed64.", "Fixed64::"); 86 | line = Util.ReplaceWholeWord(line, "-2147483648", "INT32_MIN"); 87 | line = Util.ReplaceWholeWord(line, "2147483647", "INT32_MAX"); 88 | line = Util.ReplaceWholeWord(line, "-9223372036854775808L", "INT64_MIN"); 89 | line = Util.ReplaceWholeWord(line, "9223372036854775807L", "INT64_MAX"); 90 | line = Util.ReplaceWholeWord(line, "// PREFIX", prefix); 91 | line = Util.ReplaceWholeWord(line, "// SUFFIX", suffix); 92 | line = ConvertArrays(line); 93 | line = Convert64bitConstants(line); 94 | 95 | // Add the line 96 | sb.AppendLine(line); 97 | } 98 | 99 | // Save the file 100 | File.WriteAllText(outPath, sb.ToString().Replace("\r", ""), Encoding.ASCII); 101 | } 102 | 103 | private static string ConvertArrays(string str) 104 | { 105 | return Regex.Replace(str, "static readonly FP_INT\\[\\] ([a-zA-Z0-9_]+)", "static FP_INT $1[]"); 106 | } 107 | 108 | // Converts all 64bit constants into C++ form. 109 | // A bit hacky, requires a leading white space 110 | private static string Convert64bitConstants(string str) 111 | { 112 | return Regex.Replace(str, " (-?0?x?[0-9a-fA-F]+)L", " INT64_C($1)", RegexOptions.Singleline); 113 | } 114 | 115 | private static string GetModeShortDesc(Mode mode) 116 | { 117 | switch (mode) 118 | { 119 | case Mode.Fp32: return "32"; 120 | case Mode.Fp64: return "64"; 121 | case Mode.Util: return "Util"; 122 | default: throw new Exception(); 123 | } 124 | } 125 | 126 | // Prefix for the generated file 127 | private static string MakePrefix(Mode mode) 128 | { 129 | string desc = GetModeShortDesc(mode); 130 | 131 | string includes = ""; 132 | if (mode != Mode.Util) 133 | includes += "#include \"FixedUtil.h\"\n"; 134 | if (mode == Mode.Fp32) 135 | includes += "#include \"Fixed64.h\"\n"; 136 | 137 | // Main header 138 | string header = $@"// 139 | // GENERATED FILE!!! 140 | // 141 | // Generated from Fixed{desc}.cs, part of the FixPointCS project (MIT license). 142 | // 143 | #pragma once 144 | #ifndef __FIXED{desc.ToUpperInvariant()}_H 145 | #define __FIXED{desc.ToUpperInvariant()}_H 146 | 147 | // Include numeric types 148 | #include 149 | {includes} 150 | 151 | // If FP_ASSERT is not custom-defined, then use the standard one 152 | #ifndef FP_ASSERT 153 | # include 154 | # define FP_ASSERT(x) assert(x) 155 | #endif 156 | 157 | // If FP_CUSTOM_INVALID_ARGS is defined, then the used is expected to implement the following functions in 158 | // the FixedUtil namespace: 159 | // void InvalidArgument(const char* funcName, const char* argName, FP_INT argValue); 160 | // void InvalidArgument(const char* funcName, const char* argName, FP_INT argValue1, FP_INT argValue2); 161 | // void InvalidArgument(const char* funcName, const char* argName, FP_LONG argValue); 162 | // void InvalidArgument(const char* funcName, const char* argName, FP_LONG argValue1, FP_LONG argValue2); 163 | // These functions should handle the cases for invalid arguments in any desired way (assert, exception, log, ignore etc). 164 | //#define FP_CUSTOM_INVALID_ARGS 165 | 166 | namespace Fixed{desc} 167 | {{ 168 | typedef int32_t FP_INT; 169 | typedef uint32_t FP_UINT; 170 | typedef int64_t FP_LONG; 171 | typedef uint64_t FP_ULONG; 172 | 173 | static_assert(sizeof(FP_INT) == 4, ""Wrong bytesize for FP_INT""); 174 | static_assert(sizeof(FP_UINT) == 4, ""Wrong bytesize for FP_UINT""); 175 | static_assert(sizeof(FP_LONG) == 8, ""Wrong bytesize for FP_LONG""); 176 | static_assert(sizeof(FP_ULONG) == 8, ""Wrong bytesize for FP_ULONG""); 177 | "; 178 | // Additional Util headers 179 | if (mode == Mode.Util) 180 | { 181 | header += $@" 182 | #ifdef FP_CUSTOM_INVALID_ARGS 183 | extern void InvalidArgument(const char* funcName, const char* argName, FP_INT argValue); 184 | extern void InvalidArgument(const char* funcName, const char* argName, FP_INT argValue1, FP_INT argValue2); 185 | extern void InvalidArgument(const char* funcName, const char* argName, FP_LONG argValue); 186 | extern void InvalidArgument(const char* funcName, const char* argName, FP_LONG argValue1, FP_LONG argValue2); 187 | #else 188 | static inline void InvalidArgument(const char* funcName, const char* argName, FP_INT argValue) {{ }} 189 | static inline void InvalidArgument(const char* funcName, const char* argName, FP_INT argValue1, FP_INT argValue2) {{ }} 190 | static inline void InvalidArgument(const char* funcName, const char* argName, FP_LONG argValue) {{ }} 191 | static inline void InvalidArgument(const char* funcName, const char* argName, FP_LONG argValue1, FP_LONG argValue2) {{ }} 192 | #endif 193 | "; 194 | } 195 | 196 | return header; 197 | } 198 | 199 | private static string MakeSuffix(Mode mode) 200 | { 201 | string desc = GetModeShortDesc(mode); 202 | return $@" 203 | 204 | #undef FP_ASSERT 205 | }}; 206 | #endif // __FIXED{desc.ToUpperInvariant()}_H 207 | "; 208 | } 209 | } 210 | } 211 | -------------------------------------------------------------------------------- /Transpiler/GenerateJava.cs: -------------------------------------------------------------------------------- 1 | // 2 | // FixPointCS 3 | // 4 | // Copyright(c) Jere Sanisalo, Petri Kero 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | using System; 25 | using System.Collections.Generic; 26 | using System.IO; 27 | using System.Linq; 28 | using System.Text; 29 | using System.Text.RegularExpressions; 30 | using System.Threading.Tasks; 31 | 32 | namespace Transpiler 33 | { 34 | public static class GenerateJava 35 | { 36 | public static void ConvertFile(string inPath, string outPath) 37 | { 38 | Console.WriteLine("Generating {0}..", outPath); 39 | 40 | // Read the file 41 | string[] lines = File.ReadAllLines(inPath); 42 | 43 | // Preprocess 44 | lines = Util.Preprocess(lines, "JAVA"); 45 | lines = Util.Unindent(lines, 1); 46 | 47 | // Prepare output 48 | StringBuilder sb = new StringBuilder(); 49 | 50 | // \todo [petri] actual conversion! 51 | foreach (string lineIn in lines) 52 | { 53 | string line = lineIn; 54 | string trimmed = line.Trim(); 55 | 56 | // Ignore [...] -lines (attributes) 57 | if (trimmed.StartsWith("[")) 58 | continue; 59 | 60 | // Process line 61 | line = Util.ReplaceWholeWord(line, "// PREFIX", g_prefix); 62 | line = Util.ReplaceWholeWord(line, "// SUFFIX", g_suffix); 63 | line = Util.ReplaceWholeWord(line, "static class", "class"); // static classes 64 | line = Util.ReplaceWholeWord(line, "static readonly", "static final"); // const arrays 65 | line = Util.ReplaceWholeWord(line, "public const", "public static final"); // global variables 66 | line = Util.ReplaceWholeWord(line, "private const", "private static final"); // global variables 67 | line = Util.ReplaceWholeWord(line, "const", "final"); // method-local constants 68 | line = Util.ReplaceWholeWord(line, "Debug.Assert", "assert"); 69 | line = line.Replace("Nlz((ulong)", "Nlz("); 70 | line = line.Replace("Nlz((uint)", "Nlz("); 71 | 72 | sb.AppendLine(line); 73 | } 74 | 75 | // Save the file 76 | File.WriteAllText(outPath, sb.ToString().Replace("\r", ""), Encoding.ASCII); 77 | } 78 | 79 | // Prefix for the generated file 80 | private static string g_prefix = @"// 81 | // GENERATED FILE!!! 82 | // 83 | // Generated from Fixed64.cs, part of the FixPointCS project (MIT license). 84 | // 85 | "; 86 | 87 | private static string g_suffix = @"// END GENERATED FILE"; 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /Transpiler/Program.cs: -------------------------------------------------------------------------------- 1 | // 2 | // FixPointCS 3 | // 4 | // Copyright(c) Jere Sanisalo, Petri Kero 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | using System.IO; 25 | 26 | namespace Transpiler 27 | { 28 | class Program 29 | { 30 | const string InputPath = "../FixPointCS"; 31 | const string CppOutputPath = "../Cpp"; 32 | const string JavaOutputPath = "../Java"; 33 | 34 | static void Main(string[] args) 35 | { 36 | // Generate C++ files 37 | GenerateCpp.ConvertFile(Path.Combine(InputPath, "FixedUtil.cs"), Path.Combine(CppOutputPath, "FixedUtil.h"), GenerateCpp.Mode.Util); 38 | GenerateCpp.ConvertFile(Path.Combine(InputPath, "Fixed32.cs"), Path.Combine(CppOutputPath, "Fixed32.h"), GenerateCpp.Mode.Fp32); 39 | GenerateCpp.ConvertFile(Path.Combine(InputPath, "Fixed64.cs"), Path.Combine(CppOutputPath, "Fixed64.h"), GenerateCpp.Mode.Fp64); 40 | 41 | // Generate Java files 42 | GenerateJava.ConvertFile(Path.Combine(InputPath, "FixedUtil.cs"), Path.Combine(JavaOutputPath, "FixedUtil.java")); 43 | GenerateJava.ConvertFile(Path.Combine(InputPath, "Fixed32.cs"), Path.Combine(JavaOutputPath, "Fixed32.java")); 44 | GenerateJava.ConvertFile(Path.Combine(InputPath, "Fixed64.cs"), Path.Combine(JavaOutputPath, "Fixed64.java")); 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Transpiler/Transpiler.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net6.0 5 | Exe 6 | AnyCPU;x64;x86 7 | . 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Transpiler/Util.cs: -------------------------------------------------------------------------------- 1 | // 2 | // FixPointCS 3 | // 4 | // Copyright(c) Jere Sanisalo, Petri Kero 5 | // 6 | // Permission is hereby granted, free of charge, to any person obtaining a copy 7 | // of this software and associated documentation files (the "Software"), to deal 8 | // in the Software without restriction, including without limitation the rights 9 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | // copies of the Software, and to permit persons to whom the Software is 11 | // furnished to do so, subject to the following conditions: 12 | // 13 | // The above copyright notice and this permission notice shall be included in all 14 | // copies or substantial portions of the Software. 15 | // 16 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | // SOFTWARE. 23 | // 24 | 25 | using System; 26 | using System.Collections.Generic; 27 | using System.Diagnostics; 28 | using System.Linq; 29 | using System.Text; 30 | 31 | namespace Transpiler 32 | { 33 | public static class Util 34 | { 35 | public static string[] Preprocess(string[] lines, string languageDefine) 36 | { 37 | List output = new List(); 38 | 39 | // Process the lines 40 | bool included = true; 41 | bool langChecked = false; 42 | foreach (string line in lines) 43 | { 44 | // Poor mans preprocessor handling 45 | if (line.StartsWith("#")) 46 | { 47 | Debug.Assert(line == line.Trim(), "Must not have whitespace on preprocessor lines"); 48 | if (line == $"#if {languageDefine}" || line == $"#elif {languageDefine}") 49 | { 50 | included = true; // matching language block 51 | langChecked = true; 52 | } 53 | else if (line.StartsWith("#if ") || line.StartsWith("#elif ")) 54 | included = false; // non-matching language block 55 | else if (line == "#if !TRANSPILE") 56 | included = false; 57 | else if (line.StartsWith("#else")) 58 | included = !langChecked; 59 | else if (line.StartsWith("#endif")) 60 | { 61 | included = true; 62 | langChecked = false; 63 | } 64 | else 65 | Debug.Fail("Unrecognized preprocessor line: '{0}'", line); 66 | 67 | continue; 68 | } 69 | 70 | if (!included) 71 | continue; 72 | 73 | output.Add(line); 74 | } 75 | 76 | return output.ToArray(); 77 | } 78 | 79 | public static string[] Unindent(string[] lines, int amount) 80 | { 81 | return lines.Select(line => 82 | { 83 | for (int i = 0; i < amount; i++) 84 | { 85 | if (line.StartsWith(" ")) 86 | line = line.Substring(4); 87 | else if (line.StartsWith("\t")) 88 | line = line.Substring(1); 89 | } 90 | 91 | return line; 92 | }).ToArray(); 93 | } 94 | 95 | // Replaces all occurances of the whole word (in the C# sense) with the given text 96 | public static string ReplaceWholeWord(string str, string old_word, string new_word) 97 | { 98 | int old_len = old_word.Length; 99 | 100 | // Early exits 101 | if (old_len == 0) 102 | return str; 103 | if (!str.Contains(old_word)) 104 | return str; 105 | 106 | StringBuilder sb = new StringBuilder(); 107 | int search_pos = 0; 108 | 109 | while (true) 110 | { 111 | // Find the next hit 112 | search_pos = Math.Min(search_pos, str.Length); // Clamp the search area 113 | int next_hit = str.IndexOf(old_word, search_pos); 114 | 115 | // No more hits? 116 | if (next_hit == -1) 117 | { 118 | sb.Append(str.Substring(search_pos)); 119 | return sb.ToString(); 120 | } 121 | 122 | // Copy to the hit 123 | if (next_hit > search_pos) 124 | { 125 | sb.Append(str.Substring(search_pos, next_hit - search_pos)); 126 | search_pos = next_hit; 127 | } 128 | 129 | // Check if the hit is on a word boundary 130 | bool is_ws_start = (next_hit == 0) || (IsWordBoundary(str[next_hit - 1])); 131 | bool is_ws_end = ((next_hit + old_len) == str.Length) || (IsWordBoundary(str[next_hit + old_len])); 132 | 133 | if (is_ws_start && is_ws_end) 134 | { // On a word boundary 135 | if (new_word.Length > 0) 136 | { // Fit the new word in 137 | sb.Append(new_word); 138 | } 139 | else 140 | { // Replacing with empty; skip the next whitespace in the source 141 | search_pos += 1; 142 | } 143 | } 144 | else 145 | { // Not on a word boundary 146 | sb.Append(str.Substring(next_hit, old_len)); 147 | } 148 | search_pos += old_len; 149 | } 150 | } 151 | 152 | // Tests if the character is a valid C# identifier boundary character 153 | private static bool IsWordBoundary(char c) 154 | { 155 | if (Char.IsLetterOrDigit(c) || c == '_') 156 | return false; 157 | return true; 158 | } 159 | } 160 | } -------------------------------------------------------------------------------- /functions.md: -------------------------------------------------------------------------------- 1 | 2 | # Full Function List 3 | 4 | Below is a table listing all the supported functions and their relative performance. The performance was measured 5 | with C# (Release build) on Windows 10 using a Core i7-4700MQ @ 2.40GHz using a single thread. 6 | 7 | ## Fixed64 (signed 32.32 fixed-point) 8 | 9 | | Operation | Mops/s 10 | |----------------|-----------: 11 | | Add | 1013.76 12 | | Sub | 1011.74 13 | | Mul | 354.42 14 | | Mod | 99.64 15 | | Min | 571.16 16 | | Max | 622.08 17 | | Ceil | 901.12 18 | | Floor | 998.40 19 | | Round | 516.09 20 | | Fract | 1123.84 21 | | CeilToInt | 1118.72 22 | | FloorToInt | 1456.64 23 | | RoundToInt | 1116.16 24 | | Abs | 952.32 25 | | Nabs | 691.20 26 | | Rcp | 86.87 27 | | RcpFast | 102.50 28 | | RcpFastest | 103.72 29 | | DivPrecise | 33.81 30 | | Div | 78.42 31 | | DivFast | 93.97 32 | | DivFastest | 102.40 33 | | SqrtPrecise | 7.29 34 | | Sqrt | 56.83 35 | | SqrtFast | 113.16 36 | | SqrtFastest | 119.60 37 | | RSqrt | 67.76 38 | | RSqrtFast | 106.25 39 | | RSqrtFastest | 111.97 40 | | Exp | 216.30 41 | | ExpFast | 214.61 42 | | ExpFastest | 215.87 43 | | Exp2 | 374.82 44 | | Exp2Fast | 385.79 45 | | Exp2Fastest | 349.32 46 | | Log | 90.18 47 | | LogFast | 108.35 48 | | LogFastest | 121.18 49 | | Log2 | 102.20 50 | | Log2Fast | 102.70 51 | | Log2Fastest | 120.32 52 | | Pow | 43.89 53 | | PowFast | 57.17 54 | | PowFastest | 57.39 55 | | Sin | 160.32 56 | | SinFast | 187.19 57 | | SinFastest | 220.16 58 | | Cos | 151.78 59 | | CosFast | 169.82 60 | | CosFastest | 202.24 61 | | Tan | 42.01 62 | | TanFast | 50.69 63 | | TanFastest | 63.24 64 | | Asin | 23.79 65 | | AsinFast | 29.15 66 | | AsinFastest | 37.87 67 | | Acos | 23.53 68 | | AcosFast | 28.24 69 | | AcosFastest | 38.32 70 | | Atan | 34.59 71 | | AtanFast | 51.10 72 | | AtanFastest | 76.04 73 | | Atan2 | 41.29 74 | | Atan2Fast | 51.00 75 | | Atan2Fastest | 74.85 76 | 77 | ## Fixed32 (signed 16.16 fixed-point) 78 | 79 | | Operation | Mops/s 80 | |----------------|-----------: 81 | | Add | 993.28 82 | | Sub | 993.28 83 | | Mul | 740.92 84 | | Mod | 246.35 85 | | Min | 783.36 86 | | Max | 629.76 87 | | Ceil | 1128.96 88 | | Floor | 1226.24 89 | | Round | 1123.84 90 | | Fract | 1221.12 91 | | CeilToInt | 1328.64 92 | | FloorToInt | 1249.28 93 | | RoundToInt | 1320.96 94 | | Abs | 686.08 95 | | Nabs | 707.70 96 | | Rcp | 107.52 97 | | RcpFast | 125.19 98 | | RcpFastest | 130.04 99 | | DivPrecise | 101.99 100 | | Div | 100.99 101 | | DivFast | 111.97 102 | | DivFastest | 138.58 103 | | SqrtPrecise | 33.28 104 | | Sqrt | 112.42 105 | | SqrtFast | 131.02 106 | | SqrtFastest | 135.68 107 | | RSqrt | 70.83 108 | | RSqrtFast | 137.96 109 | | RSqrtFastest | 150.74 110 | | Exp | 386.56 111 | | ExpFast | 395.22 112 | | ExpFastest | 362.07 113 | | Exp2 | 411.34 114 | | Exp2Fast | 413.89 115 | | Exp2Fastest | 433.47 116 | | Log | 108.77 117 | | LogFast | 129.27 118 | | LogFastest | 166.40 119 | | Log2 | 110.65 120 | | Log2Fast | 129.01 121 | | Log2Fastest | 157.77 122 | | Pow | 69.12 123 | | PowFast | 88.54 124 | | PowFastest | 100.39 125 | | Sin | 176.29 126 | | SinFast | 210.38 127 | | SinFastest | 246.84 128 | | Cos | 172.36 129 | | CosFast | 203.98 130 | | CosFastest | 246.35 131 | | Tan | 50.59 132 | | TanFast | 58.88 133 | | TanFastest | 78.57 134 | | Asin | 26.03 135 | | AsinFast | 29.37 136 | | AsinFastest | 39.77 137 | | Acos | 25.35 138 | | AcosFast | 28.98 139 | | AcosFastest | 39.69 140 | | Atan | 34.26 141 | | AtanFast | 52.81 142 | | AtanFastest | 68.57 143 | | Atan2 | 43.97 144 | | Atan2Fast | 57.39 145 | | Atan2Fastest | 81.92 146 | --------------------------------------------------------------------------------