├── .gitignore ├── FixedPointSharp.sln ├── FixedPointSharp ├── FixedPointSharp.csproj ├── LUT │ ├── AsinLut.cs │ ├── SinCosLut.cs │ ├── SinLut.cs │ └── TanLut.cs ├── Random.cs ├── fbool.cs ├── fixlut.cs ├── fixmath.cs ├── fixmath2.cs ├── fixmath3.cs ├── fixmath4.cs ├── fp.cs ├── fp2.cs ├── fp3.cs └── fp4.cs ├── LICENSE ├── LUTGenerator ├── Data.cs ├── Generator.cs ├── LUTGenerator.csproj ├── Properties │ └── AssemblyInfo.cs └── Writer.cs ├── README.md └── UnitTests ├── FP_Tests.cs ├── FP_fixmath3Tests.cs ├── FP_fixmathTests.cs ├── FP_randomTests.cs ├── UnitTests.csproj └── plotting.cs /.gitignore: -------------------------------------------------------------------------------- 1 | .idea/ 2 | bin/ 3 | obj/ 4 | FixedPointSharp.sln.DotSettings.user 5 | -------------------------------------------------------------------------------- /FixedPointSharp.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "FixedPointSharp", "FixedPointSharp\FixedPointSharp.csproj", "{20E87959-A8C3-4526-8415-3656D19E6C49}" 4 | EndProject 5 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "UnitTests", "UnitTests\UnitTests.csproj", "{69F2B676-5D86-47AA-8DC7-996219496F2C}" 6 | EndProject 7 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LUTGenerator", "LUTGenerator\LUTGenerator.csproj", "{49D328B2-A15B-42A7-9C16-B04E160FE7B3}" 8 | EndProject 9 | Global 10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 11 | Debug|Any CPU = Debug|Any CPU 12 | Release|Any CPU = Release|Any CPU 13 | EndGlobalSection 14 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 15 | {20E87959-A8C3-4526-8415-3656D19E6C49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 16 | {20E87959-A8C3-4526-8415-3656D19E6C49}.Debug|Any CPU.Build.0 = Debug|Any CPU 17 | {20E87959-A8C3-4526-8415-3656D19E6C49}.Release|Any CPU.ActiveCfg = Release|Any CPU 18 | {20E87959-A8C3-4526-8415-3656D19E6C49}.Release|Any CPU.Build.0 = Release|Any CPU 19 | {69F2B676-5D86-47AA-8DC7-996219496F2C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {69F2B676-5D86-47AA-8DC7-996219496F2C}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {69F2B676-5D86-47AA-8DC7-996219496F2C}.Release|Any CPU.ActiveCfg = Release|Any CPU 22 | {69F2B676-5D86-47AA-8DC7-996219496F2C}.Release|Any CPU.Build.0 = Release|Any CPU 23 | {49D328B2-A15B-42A7-9C16-B04E160FE7B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 24 | {49D328B2-A15B-42A7-9C16-B04E160FE7B3}.Debug|Any CPU.Build.0 = Debug|Any CPU 25 | {49D328B2-A15B-42A7-9C16-B04E160FE7B3}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {49D328B2-A15B-42A7-9C16-B04E160FE7B3}.Release|Any CPU.Build.0 = Release|Any CPU 27 | EndGlobalSection 28 | EndGlobal 29 | -------------------------------------------------------------------------------- /FixedPointSharp/FixedPointSharp.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2.1 5 | true 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /FixedPointSharp/LUT/SinCosLut.cs: -------------------------------------------------------------------------------- 1 | namespace Deterministic.FixedPoint { 2 | public static partial class fixlut { 3 | public static readonly int[] SinCosLut = { 4 | 0, 65536, 804, 65531, 1608, 65516, 2412, 65492, 3216, 65457, 5 | 4019, 65413, 4821, 65358, 5623, 65294, 6424, 65220, 7224, 65137, 6 | 8022, 65043, 8820, 64940, 9616, 64827, 10411, 64704, 11204, 64571, 7 | 11996, 64429, 12785, 64277, 13573, 64115, 14359, 63944, 15143, 63763, 8 | 15924, 63572, 16703, 63372, 17479, 63162, 18253, 62943, 19024, 62714, 9 | 19792, 62476, 20557, 62228, 21320, 61971, 22078, 61705, 22834, 61429, 10 | 23586, 61145, 24335, 60851, 25080, 60547, 25821, 60235, 26558, 59914, 11 | 27291, 59583, 28020, 59244, 28745, 58896, 29466, 58538, 30182, 58172, 12 | 30893, 57798, 31600, 57414, 32303, 57022, 33000, 56621, 33692, 56212, 13 | 34380, 55794, 35062, 55368, 35738, 54934, 36410, 54491, 37076, 54040, 14 | 37736, 53581, 38391, 53114, 39040, 52639, 39683, 52156, 40320, 51665, 15 | 40951, 51166, 41576, 50660, 42194, 50146, 42806, 49624, 43412, 49095, 16 | 44011, 48559, 44604, 48015, 45190, 47464, 45769, 46906, 46341, 46341, 17 | 46906, 45769, 47464, 45190, 48015, 44604, 48559, 44011, 49095, 43412, 18 | 49624, 42806, 50146, 42194, 50660, 41576, 51166, 40951, 51665, 40320, 19 | 52156, 39683, 52639, 39040, 53114, 38391, 53581, 37736, 54040, 37076, 20 | 54491, 36410, 54934, 35738, 55368, 35062, 55794, 34380, 56212, 33692, 21 | 56621, 33000, 57022, 32303, 57414, 31600, 57798, 30893, 58172, 30182, 22 | 58538, 29466, 58896, 28745, 59244, 28020, 59583, 27291, 59914, 26558, 23 | 60235, 25821, 60547, 25080, 60851, 24335, 61145, 23586, 61429, 22834, 24 | 61705, 22078, 61971, 21320, 62228, 20557, 62476, 19792, 62714, 19024, 25 | 62943, 18253, 63162, 17479, 63372, 16703, 63572, 15924, 63763, 15143, 26 | 63944, 14359, 64115, 13573, 64277, 12785, 64429, 11996, 64571, 11204, 27 | 64704, 10411, 64827, 9616, 64940, 8820, 65043, 8022, 65137, 7224, 28 | 65220, 6424, 65294, 5623, 65358, 4821, 65413, 4019, 65457, 3216, 29 | 65492, 2412, 65516, 1608, 65531, 804, 65536, 0, 65531, -804, 30 | 65516, -1608, 65492, -2412, 65457, -3216, 65413, -4019, 65358, -4821, 31 | 65294, -5623, 65220, -6424, 65137, -7224, 65043, -8022, 64940, -8820, 32 | 64827, -9616, 64704, -10411, 64571, -11204, 64429, -11996, 64277, -12785, 33 | 64115, -13573, 63944, -14359, 63763, -15143, 63572, -15924, 63372, -16703, 34 | 63162, -17479, 62943, -18253, 62714, -19024, 62476, -19792, 62228, -20557, 35 | 61971, -21320, 61705, -22078, 61429, -22834, 61145, -23586, 60851, -24335, 36 | 60547, -25080, 60235, -25821, 59914, -26558, 59583, -27291, 59244, -28020, 37 | 58896, -28745, 58538, -29466, 58172, -30182, 57798, -30893, 57414, -31600, 38 | 57022, -32303, 56621, -33000, 56212, -33692, 55794, -34380, 55368, -35062, 39 | 54934, -35738, 54491, -36410, 54040, -37076, 53581, -37736, 53114, -38391, 40 | 52639, -39040, 52156, -39683, 51665, -40320, 51166, -40951, 50660, -41576, 41 | 50146, -42194, 49624, -42806, 49095, -43412, 48559, -44011, 48015, -44604, 42 | 47464, -45190, 46906, -45769, 46341, -46341, 45769, -46906, 45190, -47464, 43 | 44604, -48015, 44011, -48559, 43412, -49095, 42806, -49624, 42194, -50146, 44 | 41576, -50660, 40951, -51166, 40320, -51665, 39683, -52156, 39040, -52639, 45 | 38391, -53114, 37736, -53581, 37076, -54040, 36410, -54491, 35738, -54934, 46 | 35062, -55368, 34380, -55794, 33692, -56212, 33000, -56621, 32303, -57022, 47 | 31600, -57414, 30893, -57798, 30182, -58172, 29466, -58538, 28745, -58896, 48 | 28020, -59244, 27291, -59583, 26558, -59914, 25821, -60235, 25080, -60547, 49 | 24335, -60851, 23586, -61145, 22834, -61429, 22078, -61705, 21320, -61971, 50 | 20557, -62228, 19792, -62476, 19024, -62714, 18253, -62943, 17479, -63162, 51 | 16703, -63372, 15924, -63572, 15143, -63763, 14359, -63944, 13573, -64115, 52 | 12785, -64277, 11996, -64429, 11204, -64571, 10411, -64704, 9616, -64827, 53 | 8820, -64940, 8022, -65043, 7224, -65137, 6424, -65220, 5623, -65294, 54 | 4821, -65358, 4019, -65413, 3216, -65457, 2412, -65492, 1608, -65516, 55 | 804, -65531, 0, -65536, -804, -65531, -1608, -65516, -2412, -65492, 56 | -3216, -65457, -4019, -65413, -4821, -65358, -5623, -65294, -6424, -65220, 57 | -7224, -65137, -8022, -65043, -8820, -64940, -9616, -64827, -10411, -64704, 58 | -11204, -64571, -11996, -64429, -12785, -64277, -13573, -64115, -14359, -63944, 59 | -15143, -63763, -15924, -63572, -16703, -63372, -17479, -63162, -18253, -62943, 60 | -19024, -62714, -19792, -62476, -20557, -62228, -21320, -61971, -22078, -61705, 61 | -22834, -61429, -23586, -61145, -24335, -60851, -25080, -60547, -25821, -60235, 62 | -26558, -59914, -27291, -59583, -28020, -59244, -28745, -58896, -29466, -58538, 63 | -30182, -58172, -30893, -57798, -31600, -57414, -32303, -57022, -33000, -56621, 64 | -33692, -56212, -34380, -55794, -35062, -55368, -35738, -54934, -36410, -54491, 65 | -37076, -54040, -37736, -53581, -38391, -53114, -39040, -52639, -39683, -52156, 66 | -40320, -51665, -40951, -51166, -41576, -50660, -42194, -50146, -42806, -49624, 67 | -43412, -49095, -44011, -48559, -44604, -48015, -45190, -47464, -45769, -46906, 68 | -46341, -46341, -46906, -45769, -47464, -45190, -48015, -44604, -48559, -44011, 69 | -49095, -43412, -49624, -42806, -50146, -42194, -50660, -41576, -51166, -40951, 70 | -51665, -40320, -52156, -39683, -52639, -39040, -53114, -38391, -53581, -37736, 71 | -54040, -37076, -54491, -36410, -54934, -35738, -55368, -35062, -55794, -34380, 72 | -56212, -33692, -56621, -33000, -57022, -32303, -57414, -31600, -57798, -30893, 73 | -58172, -30182, -58538, -29466, -58896, -28745, -59244, -28020, -59583, -27291, 74 | -59914, -26558, -60235, -25821, -60547, -25080, -60851, -24335, -61145, -23586, 75 | -61429, -22834, -61705, -22078, -61971, -21320, -62228, -20557, -62476, -19792, 76 | -62714, -19024, -62943, -18253, -63162, -17479, -63372, -16703, -63572, -15924, 77 | -63763, -15143, -63944, -14359, -64115, -13573, -64277, -12785, -64429, -11996, 78 | -64571, -11204, -64704, -10411, -64827, -9616, -64940, -8820, -65043, -8022, 79 | -65137, -7224, -65220, -6424, -65294, -5623, -65358, -4821, -65413, -4019, 80 | -65457, -3216, -65492, -2412, -65516, -1608, -65531, -804, -65536, 0, 81 | -65531, 804, -65516, 1608, -65492, 2412, -65457, 3216, -65413, 4019, 82 | -65358, 4821, -65294, 5623, -65220, 6424, -65137, 7224, -65043, 8022, 83 | -64940, 8820, -64827, 9616, -64704, 10411, -64571, 11204, -64429, 11996, 84 | -64277, 12785, -64115, 13573, -63944, 14359, -63763, 15143, -63572, 15924, 85 | -63372, 16703, -63162, 17479, -62943, 18253, -62714, 19024, -62476, 19792, 86 | -62228, 20557, -61971, 21320, -61705, 22078, -61429, 22834, -61145, 23586, 87 | -60851, 24335, -60547, 25080, -60235, 25821, -59914, 26558, -59583, 27291, 88 | -59244, 28020, -58896, 28745, -58538, 29466, -58172, 30182, -57798, 30893, 89 | -57414, 31600, -57022, 32303, -56621, 33000, -56212, 33692, -55794, 34380, 90 | -55368, 35062, -54934, 35738, -54491, 36410, -54040, 37076, -53581, 37736, 91 | -53114, 38391, -52639, 39040, -52156, 39683, -51665, 40320, -51166, 40951, 92 | -50660, 41576, -50146, 42194, -49624, 42806, -49095, 43412, -48559, 44011, 93 | -48015, 44604, -47464, 45190, -46906, 45769, -46341, 46341, -45769, 46906, 94 | -45190, 47464, -44604, 48015, -44011, 48559, -43412, 49095, -42806, 49624, 95 | -42194, 50146, -41576, 50660, -40951, 51166, -40320, 51665, -39683, 52156, 96 | -39040, 52639, -38391, 53114, -37736, 53581, -37076, 54040, -36410, 54491, 97 | -35738, 54934, -35062, 55368, -34380, 55794, -33692, 56212, -33000, 56621, 98 | -32303, 57022, -31600, 57414, -30893, 57798, -30182, 58172, -29466, 58538, 99 | -28745, 58896, -28020, 59244, -27291, 59583, -26558, 59914, -25821, 60235, 100 | -25080, 60547, -24335, 60851, -23586, 61145, -22834, 61429, -22078, 61705, 101 | -21320, 61971, -20557, 62228, -19792, 62476, -19024, 62714, -18253, 62943, 102 | -17479, 63162, -16703, 63372, -15924, 63572, -15143, 63763, -14359, 63944, 103 | -13573, 64115, -12785, 64277, -11996, 64429, -11204, 64571, -10411, 64704, 104 | -9616, 64827, -8820, 64940, -8022, 65043, -7224, 65137, -6424, 65220, 105 | -5623, 65294, -4821, 65358, -4019, 65413, -3216, 65457, -2412, 65492, 106 | -1608, 65516, -804, 65531, 0, 65536 107 | }; 108 | } 109 | } -------------------------------------------------------------------------------- /FixedPointSharp/LUT/SinLut.cs: -------------------------------------------------------------------------------- 1 | namespace Deterministic.FixedPoint { 2 | public static partial class fixlut { 3 | public static readonly int[] SinLut = { 4 | 0, 804, 1608, 2412, 3216, 4019, 4821, 5623, 6424, 7224, 5 | 8022, 8820, 9616, 10411, 11204, 11996, 12785, 13573, 14359, 15143, 6 | 15924, 16703, 17479, 18253, 19024, 19792, 20557, 21320, 22078, 22834, 7 | 23586, 24335, 25080, 25821, 26558, 27291, 28020, 28745, 29466, 30182, 8 | 30893, 31600, 32303, 33000, 33692, 34380, 35062, 35738, 36410, 37076, 9 | 37736, 38391, 39040, 39683, 40320, 40951, 41576, 42194, 42806, 43412, 10 | 44011, 44604, 45190, 45769, 46341, 46906, 47464, 48015, 48559, 49095, 11 | 49624, 50146, 50660, 51166, 51665, 52156, 52639, 53114, 53581, 54040, 12 | 54491, 54934, 55368, 55794, 56212, 56621, 57022, 57414, 57798, 58172, 13 | 58538, 58896, 59244, 59583, 59914, 60235, 60547, 60851, 61145, 61429, 14 | 61705, 61971, 62228, 62476, 62714, 62943, 63162, 63372, 63572, 63763, 15 | 63944, 64115, 64277, 64429, 64571, 64704, 64827, 64940, 65043, 65137, 16 | 65220, 65294, 65358, 65413, 65457, 65492, 65516, 65531, 65536, 65531, 17 | 65516, 65492, 65457, 65413, 65358, 65294, 65220, 65137, 65043, 64940, 18 | 64827, 64704, 64571, 64429, 64277, 64115, 63944, 63763, 63572, 63372, 19 | 63162, 62943, 62714, 62476, 62228, 61971, 61705, 61429, 61145, 60851, 20 | 60547, 60235, 59914, 59583, 59244, 58896, 58538, 58172, 57798, 57414, 21 | 57022, 56621, 56212, 55794, 55368, 54934, 54491, 54040, 53581, 53114, 22 | 52639, 52156, 51665, 51166, 50660, 50146, 49624, 49095, 48559, 48015, 23 | 47464, 46906, 46341, 45769, 45190, 44604, 44011, 43412, 42806, 42194, 24 | 41576, 40951, 40320, 39683, 39040, 38391, 37736, 37076, 36410, 35738, 25 | 35062, 34380, 33692, 33000, 32303, 31600, 30893, 30182, 29466, 28745, 26 | 28020, 27291, 26558, 25821, 25080, 24335, 23586, 22834, 22078, 21320, 27 | 20557, 19792, 19024, 18253, 17479, 16703, 15924, 15143, 14359, 13573, 28 | 12785, 11996, 11204, 10411, 9616, 8820, 8022, 7224, 6424, 5623, 29 | 4821, 4019, 3216, 2412, 1608, 804, 0, -804, -1608, -2412, 30 | -3216, -4019, -4821, -5623, -6424, -7224, -8022, -8820, -9616, -10411, 31 | -11204, -11996, -12785, -13573, -14359, -15143, -15924, -16703, -17479, -18253, 32 | -19024, -19792, -20557, -21320, -22078, -22834, -23586, -24335, -25080, -25821, 33 | -26558, -27291, -28020, -28745, -29466, -30182, -30893, -31600, -32303, -33000, 34 | -33692, -34380, -35062, -35738, -36410, -37076, -37736, -38391, -39040, -39683, 35 | -40320, -40951, -41576, -42194, -42806, -43412, -44011, -44604, -45190, -45769, 36 | -46341, -46906, -47464, -48015, -48559, -49095, -49624, -50146, -50660, -51166, 37 | -51665, -52156, -52639, -53114, -53581, -54040, -54491, -54934, -55368, -55794, 38 | -56212, -56621, -57022, -57414, -57798, -58172, -58538, -58896, -59244, -59583, 39 | -59914, -60235, -60547, -60851, -61145, -61429, -61705, -61971, -62228, -62476, 40 | -62714, -62943, -63162, -63372, -63572, -63763, -63944, -64115, -64277, -64429, 41 | -64571, -64704, -64827, -64940, -65043, -65137, -65220, -65294, -65358, -65413, 42 | -65457, -65492, -65516, -65531, -65536, -65531, -65516, -65492, -65457, -65413, 43 | -65358, -65294, -65220, -65137, -65043, -64940, -64827, -64704, -64571, -64429, 44 | -64277, -64115, -63944, -63763, -63572, -63372, -63162, -62943, -62714, -62476, 45 | -62228, -61971, -61705, -61429, -61145, -60851, -60547, -60235, -59914, -59583, 46 | -59244, -58896, -58538, -58172, -57798, -57414, -57022, -56621, -56212, -55794, 47 | -55368, -54934, -54491, -54040, -53581, -53114, -52639, -52156, -51665, -51166, 48 | -50660, -50146, -49624, -49095, -48559, -48015, -47464, -46906, -46341, -45769, 49 | -45190, -44604, -44011, -43412, -42806, -42194, -41576, -40951, -40320, -39683, 50 | -39040, -38391, -37736, -37076, -36410, -35738, -35062, -34380, -33692, -33000, 51 | -32303, -31600, -30893, -30182, -29466, -28745, -28020, -27291, -26558, -25821, 52 | -25080, -24335, -23586, -22834, -22078, -21320, -20557, -19792, -19024, -18253, 53 | -17479, -16703, -15924, -15143, -14359, -13573, -12785, -11996, -11204, -10411, 54 | -9616, -8820, -8022, -7224, -6424, -5623, -4821, -4019, -3216, -2412, 55 | -1608, -804, 0 56 | }; 57 | } 58 | } -------------------------------------------------------------------------------- /FixedPointSharp/LUT/TanLut.cs: -------------------------------------------------------------------------------- 1 | namespace Deterministic.FixedPoint { 2 | public static partial class fixlut { 3 | public static readonly int[] TanLut = { 4 | 0, 804, 1609, 2414, 3220, 4026, 4834, 5644, 6455, 7268, 5 | 8083, 8901, 9721, 10545, 11372, 12202, 13036, 13874, 14717, 15564, 6 | 16416, 17273, 18136, 19005, 19880, 20762, 21650, 22546, 23449, 24360, 7 | 25280, 26208, 27146, 28093, 29050, 30018, 30996, 31986, 32988, 34002, 8 | 35030, 36071, 37126, 38196, 39281, 40382, 41500, 42636, 43790, 44963, 9 | 46156, 47369, 48605, 49863, 51145, 52451, 53784, 55144, 56532, 57950, 10 | 59398, 60880, 62395, 63947, 65536, 67165, 68835, 70548, 72308, 74116, 11 | 75974, 77887, 79856, 81885, 83977, 86135, 88365, 90670, 93054, 95523, 12 | 98082, 100736, 103493, 106358, 109340, 112447, 115687, 119071, 122609, 126314, 13 | 130198, 134276, 138564, 143081, 147847, 152884, 158218, 163878, 169896, 176309, 14 | 183161, 190499, 198380, 206870, 216043, 225990, 236817, 248648, 261634, 275959, 15 | 291845, 309568, 329472, 351993, 377693, 407305, 441808, 482534, 531352, 590958, 16 | 665398, 761030, 888450, 1066730, 1334016, 1779314, 2669641, 5340086, -2147483648, -5340086, 17 | -2669641, -1779314, -1334016, -1066730, -888450, -761030, -665398, -590958, -531352, -482534, 18 | -441808, -407305, -377693, -351993, -329472, -309568, -291845, -275959, -261634, -248648, 19 | -236817, -225990, -216043, -206870, -198380, -190499, -183161, -176309, -169896, -163878, 20 | -158218, -152884, -147847, -143081, -138564, -134276, -130198, -126314, -122609, -119071, 21 | -115687, -112447, -109340, -106358, -103493, -100736, -98082, -95523, -93054, -90670, 22 | -88365, -86135, -83977, -81885, -79856, -77887, -75974, -74116, -72308, -70548, 23 | -68835, -67165, -65536, -63947, -62395, -60880, -59398, -57950, -56532, -55144, 24 | -53784, -52451, -51145, -49863, -48605, -47369, -46156, -44963, -43790, -42636, 25 | -41500, -40382, -39281, -38196, -37126, -36071, -35030, -34002, -32988, -31986, 26 | -30996, -30018, -29050, -28093, -27146, -26208, -25280, -24360, -23449, -22546, 27 | -21650, -20762, -19880, -19005, -18136, -17273, -16416, -15564, -14717, -13874, 28 | -13036, -12202, -11372, -10545, -9721, -8901, -8083, -7268, -6455, -5644, 29 | -4834, -4026, -3220, -2414, -1609, -804, 0, 804, 1609, 2414, 30 | 3220, 4026, 4834, 5644, 6455, 7268, 8083, 8901, 9721, 10545, 31 | 11372, 12202, 13036, 13874, 14717, 15564, 16416, 17273, 18136, 19005, 32 | 19880, 20762, 21650, 22546, 23449, 24360, 25280, 26208, 27146, 28093, 33 | 29050, 30018, 30996, 31986, 32988, 34002, 35030, 36071, 37126, 38196, 34 | 39281, 40382, 41500, 42636, 43790, 44963, 46156, 47369, 48605, 49863, 35 | 51145, 52451, 53784, 55144, 56532, 57950, 59398, 60880, 62395, 63947, 36 | 65536, 67165, 68835, 70548, 72308, 74116, 75974, 77887, 79856, 81885, 37 | 83977, 86135, 88365, 90670, 93054, 95523, 98082, 100736, 103493, 106358, 38 | 109340, 112447, 115687, 119071, 122609, 126314, 130198, 134276, 138564, 143081, 39 | 147847, 152884, 158218, 163878, 169896, 176309, 183161, 190499, 198380, 206870, 40 | 216043, 225990, 236817, 248648, 261634, 275959, 291845, 309568, 329472, 351993, 41 | 377693, 407305, 441808, 482534, 531352, 590958, 665398, 761030, 888450, 1066730, 42 | 1334016, 1779314, 2669641, 5340086, -2147483648, -5340086, -2669641, -1779314, -1334016, -1066730, 43 | -888450, -761030, -665398, -590958, -531352, -482534, -441808, -407305, -377693, -351993, 44 | -329472, -309568, -291845, -275959, -261634, -248648, -236817, -225990, -216043, -206870, 45 | -198380, -190499, -183161, -176309, -169896, -163878, -158218, -152884, -147847, -143081, 46 | -138564, -134276, -130198, -126314, -122609, -119071, -115687, -112447, -109340, -106358, 47 | -103493, -100736, -98082, -95523, -93054, -90670, -88365, -86135, -83977, -81885, 48 | -79856, -77887, -75974, -74116, -72308, -70548, -68835, -67165, -65536, -63947, 49 | -62395, -60880, -59398, -57950, -56532, -55144, -53784, -52451, -51145, -49863, 50 | -48605, -47369, -46156, -44963, -43790, -42636, -41500, -40382, -39281, -38196, 51 | -37126, -36071, -35030, -34002, -32988, -31986, -30996, -30018, -29050, -28093, 52 | -27146, -26208, -25280, -24360, -23449, -22546, -21650, -20762, -19880, -19005, 53 | -18136, -17273, -16416, -15564, -14717, -13874, -13036, -12202, -11372, -10545, 54 | -9721, -8901, -8083, -7268, -6455, -5644, -4834, -4026, -3220, -2414, 55 | -1609, -804, 0 56 | }; 57 | } 58 | } -------------------------------------------------------------------------------- /FixedPointSharp/Random.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace Deterministic.FixedPoint { 5 | [StructLayout(LayoutKind.Explicit, Size = SIZE)] 6 | public struct Random { 7 | public const int SIZE = 4; 8 | 9 | [FieldOffset(0)] 10 | public uint state; 11 | 12 | /// 13 | /// Seed must be non-zero 14 | /// 15 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 16 | public Random(uint seed) 17 | { 18 | state = seed; 19 | NextState(); 20 | } 21 | 22 | /// 23 | /// Seed must be non-zero 24 | /// 25 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 26 | public void SetState(uint seed) 27 | { 28 | state = seed; 29 | NextState(); 30 | } 31 | 32 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 33 | private uint NextState() { 34 | var t = state; 35 | state ^= state << 13; 36 | state ^= state >> 17; 37 | state ^= state << 5; 38 | return t; 39 | } 40 | 41 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 42 | public bool NextBool() 43 | { 44 | return (NextState() & 1) == 1; 45 | } 46 | 47 | /// Returns value in range [-2147483647, 2147483647] 48 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 49 | public int NextInt() 50 | { 51 | return (int)NextState() ^ -2147483648; 52 | } 53 | 54 | /// Returns value in range [0, max] 55 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 56 | public int NextInt(int max) 57 | { 58 | return (int)((NextState() * (ulong)max) >> 32); 59 | } 60 | 61 | /// Returns value in range [min, max]. 62 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 63 | public int NextInt(int min, int max) 64 | { 65 | var range = (uint)(max - min); 66 | return (int)(NextState() * (ulong)range >> 32) + min; 67 | } 68 | 69 | /// Returns value in range [0, 1] 70 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 71 | public fp NextFp() { 72 | return new fp(NextInt(0, 65535)); 73 | } 74 | 75 | /// Returns vector with all components in range [0, 1] 76 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 77 | public fp2 NextFp2() { 78 | return new fp2(NextFp(), NextFp()); 79 | } 80 | 81 | /// Returns vector with all components in range [0, 1] 82 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 83 | public fp3 NextFp3() { 84 | return new fp3(NextFp(), NextFp(), NextFp()); 85 | } 86 | 87 | /// Returns vector with all components in range [0, 1] 88 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 89 | public fp4 NextFp4() { 90 | return new fp4(NextFp(), NextFp(), NextFp(), NextFp()); 91 | } 92 | 93 | 94 | /// Returns value in range [0, max] 95 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 96 | public fp NextFp(fp max) { 97 | return NextFp() * max; 98 | } 99 | 100 | /// Returns vector with all components in range [0, max] 101 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 102 | public fp2 NextFp2(fp2 max) { 103 | return NextFp2() * max; 104 | } 105 | 106 | /// Returns vector with all components in range [0, max] 107 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 108 | public fp3 NextFp3(fp3 max) { 109 | return NextFp3() * max; 110 | } 111 | 112 | /// Returns vector with all components in range [0, max] 113 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 114 | public fp4 NextFp4(fp4 max) { 115 | return NextFp4() * max; 116 | } 117 | 118 | /// Returns value in range [min, max] 119 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 120 | public fp NextFp(fp min, fp max) { 121 | return NextFp() * (max - min) + min; 122 | } 123 | 124 | /// Returns vector with all components in range [min, max] 125 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 126 | public fp2 NextFp2(fp2 min, fp2 max) { 127 | return NextFp2() * (max - min) + min; 128 | } 129 | 130 | /// Returns vector with all components in range [min, max] 131 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 132 | public fp3 NextFp3(fp3 min, fp3 max) { 133 | return NextFp3() * (max - min) + min; 134 | } 135 | 136 | /// Returns vector with all components in range [min, max] 137 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 138 | public fp4 NextFp4(fp4 min, fp4 max) { 139 | return NextFp4() * (max - min) + min; 140 | } 141 | 142 | /// Returns a normalized 2D direction 143 | public fp2 NextDirection2D() { 144 | var angle = NextFp() * fp.pi * fp._2; 145 | fixmath.SinCos(angle, out var sin, out var cos); 146 | return new fp2(sin,cos); 147 | } 148 | 149 | /// Returns a normalized 3D direction 150 | public fp3 NextDirection3D() { 151 | var z = NextFp(fp._2) - fp._1; 152 | var r = fixmath.Sqrt(fixmath.Max(fp._1 - z * z, fp._0)); 153 | var angle = NextFp(fp.pi2); 154 | fixmath.SinCos(angle, out var sin, out var cos); 155 | return new fp3(cos * r, sin * r, z); 156 | } 157 | } 158 | } -------------------------------------------------------------------------------- /FixedPointSharp/fbool.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace Deterministic.FixedPoint { 4 | [StructLayout(LayoutKind.Explicit, Size = SIZE)] 5 | // ReSharper disable once InconsistentNaming 6 | public struct fbool { 7 | public const int SIZE = 2; 8 | 9 | [FieldOffset(0)] 10 | public ushort Value; 11 | 12 | public static implicit operator bool(fbool b) 13 | { 14 | return b.Value > 0; 15 | } 16 | 17 | public static implicit operator fbool(bool b) 18 | { 19 | fbool fbool; 20 | fbool.Value = (ushort) (b ? 1 : 0); 21 | return fbool; 22 | } 23 | 24 | public bool Equals(fbool other) { 25 | return Value == other.Value; 26 | } 27 | 28 | public override bool Equals(object obj) { 29 | return obj is fbool other && Equals(other); 30 | } 31 | 32 | public static bool operator ==(fbool left, fbool right) { 33 | return left.Value == right.Value; 34 | } 35 | 36 | public static bool operator !=(fbool left, fbool right) { 37 | return left.Value != right.Value; 38 | } 39 | 40 | public override int GetHashCode() { 41 | return Value; 42 | } 43 | 44 | public override string ToString() { 45 | return ((bool) this).ToString(); 46 | } 47 | } 48 | } -------------------------------------------------------------------------------- /FixedPointSharp/fixlut.cs: -------------------------------------------------------------------------------- 1 | namespace Deterministic.FixedPoint { 2 | public static partial class fixlut { 3 | public const int FRACTIONS_COUNT = 5; 4 | public const int PRECISION = 16; 5 | public const int SHIFT = 16 - 9; 6 | public const long PI = 205887L; 7 | public const long ONE = 1 << PRECISION; 8 | public const long HALF = 1 << (PRECISION-1); 9 | public const long ZERO = 0; 10 | 11 | 12 | public static long sin(long value) { 13 | var sign = 1; 14 | if (value < 0) { 15 | value = -value; 16 | sign = -1; 17 | } 18 | 19 | var index = (int) (value >> SHIFT); 20 | var fraction = (value - (index << SHIFT)) << 9; 21 | var a = SinLut[index]; 22 | var b = SinLut[index + 1]; 23 | var v2 = a + (((b - a) * fraction) >> PRECISION); 24 | return v2 * sign; 25 | } 26 | 27 | public static long cos(long value) { 28 | if (value < 0) { 29 | value = -value; 30 | } 31 | 32 | value += fp._0_25.value; 33 | 34 | if (value >= 65536) { 35 | value -= 65536; 36 | } 37 | 38 | var index = (int) (value >> SHIFT); 39 | var fraction = (value - (index << SHIFT)) << 9; 40 | var a = SinLut[index]; 41 | var b = SinLut[index + 1]; 42 | var v2 = a + (((b - a) * fraction) >> PRECISION); 43 | return v2; 44 | } 45 | 46 | 47 | public static long tan(long value) { 48 | var sign = 1; 49 | 50 | if (value < 0) { 51 | value = -value; 52 | sign = -1; 53 | } 54 | 55 | var index = (int) (value >> SHIFT); 56 | var fraction = (value - (index << SHIFT)) << 9; 57 | var a = TanLut[index]; 58 | var b = TanLut[index + 1]; 59 | var v2 = a + (((b - a) * fraction) >> PRECISION); 60 | return v2 * sign; 61 | } 62 | 63 | public static void sin_cos(long value, out long sin, out long cos) { 64 | var sign = 1; 65 | if (value < 0) { 66 | value = -value; 67 | sign = -1; 68 | } 69 | 70 | var index = (int) (value >> SHIFT); 71 | var doubleIndex = index * 2; 72 | var fractions = (value - (index << SHIFT)) << 9; 73 | 74 | var sinA = SinCosLut[doubleIndex]; 75 | var cosA = SinCosLut[doubleIndex + 1]; 76 | var sinB = SinCosLut[doubleIndex + 2]; 77 | var cosB = SinCosLut[doubleIndex + 3]; 78 | 79 | sin = (sinA + (((sinB - sinA) * fractions) >> PRECISION)) * sign; 80 | cos = cosA + (((cosB - cosA) * fractions) >> PRECISION); 81 | } 82 | 83 | public static long asin(long value) { 84 | bool flag = false; 85 | if (value < 0) { 86 | flag = true; 87 | value = -value; 88 | } 89 | 90 | var result = AsinLut[value]; 91 | if (flag) 92 | result = -result; 93 | return result; 94 | } 95 | 96 | public static long acos(long value) { 97 | bool flag = false; 98 | if (value < 0) { 99 | flag = true; 100 | value = -value; 101 | } 102 | 103 | long result = -AsinLut[value]; 104 | if (flag) 105 | result = -result; 106 | 107 | result += fp.pi_half.value; 108 | return result; 109 | } 110 | } 111 | } -------------------------------------------------------------------------------- /FixedPointSharp/fixmath.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace Deterministic.FixedPoint { 4 | public partial struct fixmath { 5 | private static readonly fp _atan2Number1 = new fp(-883); 6 | private static readonly fp _atan2Number2 = new fp(3767); 7 | private static readonly fp _atan2Number3 = new fp(7945); 8 | private static readonly fp _atan2Number4 = new fp(12821); 9 | private static readonly fp _atan2Number5 = new fp(21822); 10 | private static readonly fp _atan2Number6 = new fp(65536); 11 | private static readonly fp _atan2Number7 = new fp(102943); 12 | private static readonly fp _atan2Number8 = new fp(205887); 13 | private static readonly fp _atanApproximatedNumber1 = new fp(16036); 14 | private static readonly fp _atanApproximatedNumber2 = new fp(4345); 15 | private static readonly byte[] _bsrLookup = {0, 9, 1, 10, 13, 21, 2, 29, 11, 14, 16, 18, 22, 25, 3, 30, 8, 12, 20, 28, 15, 17, 24, 7, 19, 27, 23, 6, 26, 5, 4, 31}; 16 | 17 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 18 | public static int BitScanReverse(uint num) { 19 | num |= num >> 1; 20 | num |= num >> 2; 21 | num |= num >> 4; 22 | num |= num >> 8; 23 | num |= num >> 16; 24 | return _bsrLookup[(num * 0x07C4ACDDU) >> 27]; 25 | } 26 | 27 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 28 | public static int CountLeadingZeroes(uint num) { 29 | return num == 0 ? 32 : BitScanReverse(num) ^ 31; 30 | } 31 | 32 | /// Angle in radians 33 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 34 | public static fp Sin(fp num) { 35 | num.value %= fp.pi2.value; 36 | num *= fp.one_div_pi2; 37 | var raw = fixlut.sin(num.value); 38 | fp result; 39 | result.value = raw; 40 | return result; 41 | } 42 | 43 | /// Angle in radians 44 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 45 | public static fp Cos(fp num) { 46 | num.value %= fp.pi2.value; 47 | num *= fp.one_div_pi2; 48 | return new fp(fixlut.cos(num.value)); 49 | } 50 | 51 | /// Angle in radians 52 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 53 | public static fp Tan(fp num) { 54 | num.value %= fp.pi2.value; 55 | num *= fp.one_div_pi2; 56 | return new fp(fixlut.tan(num.value)); 57 | } 58 | 59 | /// Cos [-1, 1] 60 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 61 | public static fp Acos(fp num) { 62 | return new fp(fixlut.acos(num.value)); 63 | } 64 | 65 | /// Sin [-1, 1] 66 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 67 | public static fp Asin(fp num) { 68 | return new fp(fixlut.asin(num.value)); 69 | } 70 | 71 | /// Tan 72 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 73 | public static fp Atan(fp num) { 74 | return Atan2(num, fp._1); 75 | } 76 | 77 | /// Tan [-1, 1] 78 | /// Max error ~0.0015 79 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 80 | public static fp AtanApproximated(fp num) { 81 | var absX = Abs(num); 82 | return fp.pi_quarter * num - num * (absX - fp._1) * (_atanApproximatedNumber1 + _atanApproximatedNumber2 * absX); 83 | } 84 | 85 | /// Denominator 86 | /// Numerator 87 | public static fp Atan2(fp y, fp x) { 88 | var absX = Abs(x); 89 | var absY = Abs(y); 90 | var t3 = absX; 91 | var t1 = absY; 92 | var t0 = Max(t3, t1); 93 | t1 = Min(t3, t1); 94 | t3 = fp._1 / t0; 95 | t3 = t1 * t3; 96 | var t4 = t3 * t3; 97 | t0 = _atan2Number1; 98 | t0 = t0 * t4 + _atan2Number2; 99 | t0 = t0 * t4 - _atan2Number3; 100 | t0 = t0 * t4 + _atan2Number4; 101 | t0 = t0 * t4 - _atan2Number5; 102 | t0 = t0 * t4 + _atan2Number6; 103 | t3 = t0 * t3; 104 | t3 = absY > absX ? _atan2Number7 - t3 : t3; 105 | t3 = x < fp._0 ? _atan2Number8 - t3 : t3; 106 | t3 = y < fp._0 ? -t3 : t3; 107 | return t3; 108 | } 109 | 110 | /// Angle in radians 111 | public static void SinCos(fp num, out fp sin, out fp cos) { 112 | num.value %= fp.pi2.value; 113 | num *= fp.one_div_pi2; 114 | fixlut.sin_cos(num.value, out var sinVal, out var cosVal); 115 | sin.value = sinVal; 116 | cos.value = cosVal; 117 | } 118 | 119 | public static fp Rcp(fp num) { 120 | //(fp.1 << 16) 121 | return new fp(4294967296 / num.value); 122 | } 123 | 124 | public static fp Rsqrt(fp num) { 125 | //(fp.1 << 16) 126 | return new fp(4294967296 / Sqrt(num).value); 127 | } 128 | 129 | public static fp Sqrt(fp num) { 130 | fp r; 131 | 132 | if (num.value == 0) { 133 | r.value = 0; 134 | } 135 | else { 136 | var b = (num.value >> 1) + 1L; 137 | var c = (b + (num.value / b)) >> 1; 138 | 139 | while (c < b) { 140 | b = c; 141 | c = (b + (num.value / b)) >> 1; 142 | } 143 | 144 | r.value = b << (fixlut.PRECISION >> 1); 145 | } 146 | 147 | return r; 148 | } 149 | 150 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 151 | public static fp Floor(fp num) { 152 | num.value = num.value >> fixlut.PRECISION << fixlut.PRECISION; 153 | return num; 154 | } 155 | 156 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 157 | public static fp Ceil(fp num) { 158 | var fractions = num.value & 0x000000000000FFFFL; 159 | 160 | if (fractions == 0) { 161 | return num; 162 | } 163 | 164 | num.value = num.value >> fixlut.PRECISION << fixlut.PRECISION; 165 | num.value += fixlut.ONE; 166 | return num; 167 | } 168 | 169 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 170 | public static fp Fractions(fp num) { 171 | return new fp(num.value & 0x000000000000FFFFL); 172 | } 173 | 174 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 175 | public static int RoundToInt(fp num) { 176 | var fraction = num.value & 0x000000000000FFFFL; 177 | 178 | if (fraction >= fixlut.HALF) { 179 | return num.AsInt + 1; 180 | } 181 | 182 | return num.AsInt; 183 | } 184 | 185 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 186 | public static fp Min(fp a, fp b) { 187 | return a.value < b.value ? a : b; 188 | } 189 | 190 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 191 | public static int Min(int a, int b) { 192 | return a < b ? a : b; 193 | } 194 | 195 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 196 | public static long Min(long a, long b) { 197 | return a < b ? a : b; 198 | } 199 | 200 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 201 | public static fp Max(fp a, fp b) { 202 | return a.value > b.value ? a : b; 203 | } 204 | 205 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 206 | public static int Max(int a, int b) { 207 | return a > b ? a : b; 208 | } 209 | 210 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 211 | public static long Max(long a, long b) { 212 | return a > b ? a : b; 213 | } 214 | 215 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 216 | public static fp Abs(fp num) { 217 | return new fp(num.value < 0 ? -num.value : num.value); 218 | } 219 | 220 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 221 | public static fp Clamp(fp num, fp min, fp max) { 222 | if (num.value < min.value) { 223 | return min; 224 | } 225 | 226 | if (num.value > max.value) { 227 | return max; 228 | } 229 | 230 | return num; 231 | } 232 | 233 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 234 | public static int Clamp(int num, int min, int max) { 235 | return num < min ? min : num > max ? max : num; 236 | } 237 | 238 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 239 | public static long Clamp(long num, long min, long max) { 240 | return num < min ? min : num > max ? max : num; 241 | } 242 | 243 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 244 | public static fp Clamp01(fp num) { 245 | if (num.value < 0) { 246 | return fp._0; 247 | } 248 | 249 | return num.value > fp._1.value ? fp._1 : num; 250 | } 251 | 252 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 253 | public static fp Lerp(fp from, fp to, fp t) { 254 | t = Clamp01(t); 255 | return from + (to - from) * t; 256 | } 257 | 258 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 259 | public static fbool Lerp(fbool from, fbool to, fp t) { 260 | return t.value > fp._0_50.value ? to : from; 261 | } 262 | 263 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 264 | public static fp Repeat(fp value, fp length) { 265 | return Clamp(value - Floor(value / length) * length, 0, length); 266 | } 267 | 268 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 269 | public static fp LerpAngle(fp from, fp to, fp t) { 270 | var num = Repeat(to - from, fp.pi2); 271 | return Lerp(from, from + (num > fp.pi ? num - fp.pi2 : num), t); 272 | } 273 | 274 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 275 | public static fp NormalizeRadians(fp angle) { 276 | angle.value %= fixlut.PI; 277 | return angle; 278 | } 279 | 280 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 281 | public static fp LerpUnclamped(fp from, fp to, fp t) { 282 | return from + (to - from) * t; 283 | } 284 | 285 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 286 | public static fp Sign(fp num) { 287 | return num.value < fixlut.ZERO ? fp.minus_one : fp._1; 288 | } 289 | 290 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 291 | public static bool IsOppositeSign(fp a, fp b) { 292 | return ((a.value ^ b.value) < 0); 293 | } 294 | 295 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 296 | public static fp SetSameSign(fp target, fp reference) { 297 | return IsOppositeSign(target, reference) ? target * fp.minus_one : target; 298 | } 299 | 300 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 301 | public static fp Pow2(int power) { 302 | return new fp(fixlut.ONE << power); 303 | } 304 | 305 | public static fp Exp(fp num) { 306 | if (num == fp._0) return fp._1; 307 | if (num == fp._1) return fp.e; 308 | if (num.value >= 2097152) return fp.max; 309 | if (num.value <= -786432) return fp._0; 310 | 311 | var neg = num.value < 0; 312 | if (neg) num = -num; 313 | 314 | var result = num + fp._1; 315 | var term = num; 316 | 317 | for (var i = 2; i < 30; i++) { 318 | term *= num / (fp)i; 319 | result += term; 320 | 321 | if (term.value < 500 && ((i > 15) || (term.value < 20))) 322 | break; 323 | } 324 | 325 | if (neg) result = fp._1 / result; 326 | 327 | return result; 328 | } 329 | } 330 | } -------------------------------------------------------------------------------- /FixedPointSharp/fixmath2.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace Deterministic.FixedPoint 4 | { 5 | public partial struct fixmath 6 | { 7 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 8 | public static fp Sum(fp2 v) { 9 | return new fp(v.x.value + v.y.value); 10 | } 11 | 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public static fp2 Min(fp2 a, fp2 b) 14 | { 15 | var ret = a.x.value < b.x.value ? a.x : b.x; 16 | var ret1 = a.y.value < b.y.value ? a.y : b.y; 17 | 18 | return new fp2(ret, ret1); 19 | } 20 | 21 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 22 | public static fp2 Max(fp2 a, fp2 b) 23 | { 24 | var ret = a.x.value > b.x.value ? a.x : b.x; 25 | var ret1 = a.y.value > b.y.value ? a.y : b.y; 26 | return new fp2(ret, ret1); 27 | } 28 | 29 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 30 | public static fp Dot(fp2 a, fp2 b) 31 | { 32 | a.x.value = ((a.x.value * b.x.value) >> fixlut.PRECISION) + ((a.y.value * b.y.value) >> fixlut.PRECISION); 33 | return a.x; 34 | } 35 | 36 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 37 | public static fp Cross(fp2 a, fp2 b) { 38 | a.x.value = (a.x.value * b.y.value >> fixlut.PRECISION) - (a.y.value * b.x.value >> fixlut.PRECISION); 39 | return a.x; 40 | } 41 | 42 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 43 | public static fp2 Cross(fp2 a, fp s) 44 | { 45 | return new fp2(s * a.y, -s * a.x); 46 | } 47 | 48 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 49 | public static fp2 Cross(fp s, fp2 a) { 50 | fp2 result; 51 | result.x.value = -s.value * a.y.value >> fixlut.PRECISION; 52 | result.y.value = s.value * a.x.value >> fixlut.PRECISION; 53 | return result; 54 | } 55 | 56 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 57 | public static fp2 Clamp(fp2 num, fp2 min, fp2 max) 58 | { 59 | fp2 r; 60 | 61 | if (num.x.value < min.x.value) 62 | { 63 | r.x = min.x; 64 | } 65 | else { 66 | r.x = num.x.value > max.x.value ? max.x : num.x; 67 | } 68 | 69 | if (num.y.value < min.y.value) 70 | { 71 | r.y = min.y; 72 | } 73 | else { 74 | r.y = num.y.value > max.y.value ? max.y : num.y; 75 | } 76 | 77 | return r; 78 | } 79 | 80 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 81 | public static fp2 ClampMagnitude(fp2 v, fp length) 82 | { 83 | fp2 value = v; 84 | fp r; 85 | 86 | r.value = 87 | ((value.x.value * value.x.value) >> fixlut.PRECISION) + 88 | ((value.y.value * value.y.value) >> fixlut.PRECISION); 89 | if (r.value <= ((length.value * length.value) >> fixlut.PRECISION)) 90 | { 91 | } 92 | else 93 | { 94 | fp2 v1 = value; 95 | fp m = default; 96 | fp r2; 97 | 98 | r2.value = 99 | ((v1.x.value * v1.x.value) >> fixlut.PRECISION) + 100 | ((v1.y.value * v1.y.value) >> fixlut.PRECISION); 101 | fp r1; 102 | 103 | if (r2.value == 0) 104 | { 105 | r1.value = 0; 106 | } 107 | else 108 | { 109 | var b = (r2.value >> 1) + 1L; 110 | var c = (b + (r2.value / b)) >> 1; 111 | 112 | while (c < b) 113 | { 114 | b = c; 115 | c = (b + (r2.value / b)) >> 1; 116 | } 117 | 118 | r1.value = b << (fixlut.PRECISION >> 1); 119 | } 120 | 121 | m = r1; 122 | 123 | if (m.value <= fp.epsilon.value) 124 | { 125 | v1 = default; 126 | } 127 | else 128 | { 129 | v1.x.value = ((v1.x.value << fixlut.PRECISION) / m.value); 130 | v1.y.value = ((v1.y.value << fixlut.PRECISION) / m.value); 131 | } 132 | 133 | value = v1 * length; 134 | } 135 | 136 | return value; 137 | } 138 | 139 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 140 | public static fp Magnitude(fp2 v) 141 | { 142 | fp r; 143 | 144 | r.value = 145 | ((v.x.value * v.x.value) >> fixlut.PRECISION) + 146 | ((v.y.value * v.y.value) >> fixlut.PRECISION); 147 | fp r1; 148 | 149 | if (r.value == 0) 150 | { 151 | r1.value = 0; 152 | } 153 | else 154 | { 155 | var b = (r.value >> 1) + 1L; 156 | var c = (b + (r.value / b)) >> 1; 157 | 158 | while (c < b) 159 | { 160 | b = c; 161 | c = (b + (r.value / b)) >> 1; 162 | } 163 | 164 | r1.value = b << (fixlut.PRECISION >> 1); 165 | } 166 | 167 | return r1; 168 | } 169 | 170 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 171 | public static fp MagnitudeSqr(fp2 v) 172 | { 173 | v.x.value = 174 | ((v.x.value * v.x.value) >> fixlut.PRECISION) + 175 | ((v.y.value * v.y.value) >> fixlut.PRECISION); 176 | 177 | return v.x; 178 | } 179 | 180 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 181 | public static fp Distance(fp2 a, fp2 b) 182 | { 183 | fp2 v; 184 | 185 | v.x.value = a.x.value - b.x.value; 186 | v.y.value = a.y.value - b.y.value; 187 | 188 | fp r; 189 | 190 | r.value = 191 | ((v.x.value * v.x.value) >> fixlut.PRECISION) + 192 | ((v.y.value * v.y.value) >> fixlut.PRECISION); 193 | fp r1; 194 | 195 | if (r.value == 0) 196 | { 197 | r1.value = 0; 198 | } 199 | else 200 | { 201 | var b1 = (r.value >> 1) + 1L; 202 | var c = (b1 + (r.value / b1)) >> 1; 203 | 204 | while (c < b1) 205 | { 206 | b1 = c; 207 | c = (b1 + (r.value / b1)) >> 1; 208 | } 209 | 210 | r1.value = b1 << (fixlut.PRECISION >> 1); 211 | } 212 | 213 | return r1; 214 | } 215 | 216 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 217 | public static fp DistanceSqr(fp2 a, fp2 b) 218 | { 219 | var x = a.x.value - b.x.value; 220 | var z = a.y.value - b.y.value; 221 | 222 | a.x.value = ((x * x) >> fixlut.PRECISION) + ((z * z) >> fixlut.PRECISION); 223 | return a.x; 224 | } 225 | 226 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 227 | public static fp2 Normalize(fp2 v) 228 | { 229 | fp2 v1 = v; 230 | fp m = default; 231 | fp r; 232 | 233 | r.value = 234 | ((v1.x.value * v1.x.value) >> fixlut.PRECISION) + 235 | ((v1.y.value * v1.y.value) >> fixlut.PRECISION); 236 | fp r1; 237 | 238 | if (r.value == 0) 239 | { 240 | r1.value = 0; 241 | } 242 | else 243 | { 244 | var b = (r.value >> 1) + 1L; 245 | var c = (b + (r.value / b)) >> 1; 246 | 247 | while (c < b) 248 | { 249 | b = c; 250 | c = (b + (r.value / b)) >> 1; 251 | } 252 | 253 | r1.value = b << (fixlut.PRECISION >> 1); 254 | } 255 | 256 | m = r1; 257 | 258 | if (m.value <= fp.epsilon.value) 259 | { 260 | v1 = default; 261 | } 262 | else 263 | { 264 | v1.x.value = ((v1.x.value << fixlut.PRECISION) / m.value); 265 | v1.y.value = ((v1.y.value << fixlut.PRECISION) / m.value); 266 | } 267 | 268 | return v1; 269 | } 270 | 271 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 272 | public static fp2 Normalize(fp2 v, out fp magnitude) 273 | { 274 | if (v == fp2.zero) 275 | { 276 | magnitude = fp._0; 277 | return fp2.zero; 278 | } 279 | 280 | magnitude = Magnitude(v); 281 | return v / magnitude; 282 | } 283 | 284 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 285 | public static fp2 Lerp(fp2 from, fp2 to, fp t) { 286 | t = Clamp01(t); 287 | return new fp2(LerpUnclamped(from.x, to.x, t), LerpUnclamped(from.y, to.y, t)); 288 | } 289 | 290 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 291 | public static fp2 LerpUnclamped(fp2 from, fp2 to, fp t) 292 | { 293 | return new fp2(LerpUnclamped(from.x, to.x, t), LerpUnclamped(from.y, to.y, t)); 294 | } 295 | 296 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 297 | public static fp Angle(fp2 a, fp2 b) 298 | { 299 | fp2 v = a; 300 | fp m = default; 301 | fp r2; 302 | 303 | r2.value = 304 | ((v.x.value * v.x.value) >> fixlut.PRECISION) + 305 | ((v.y.value * v.y.value) >> fixlut.PRECISION); 306 | fp r1; 307 | 308 | if (r2.value == 0) 309 | { 310 | r1.value = 0; 311 | } 312 | else 313 | { 314 | var b2 = (r2.value >> 1) + 1L; 315 | var c = (b2 + (r2.value / b2)) >> 1; 316 | 317 | while (c < b2) 318 | { 319 | b2 = c; 320 | c = (b2 + (r2.value / b2)) >> 1; 321 | } 322 | 323 | r1.value = b2 << (fixlut.PRECISION >> 1); 324 | } 325 | 326 | m = r1; 327 | 328 | if (m.value <= fp.epsilon.value) 329 | { 330 | v = default; 331 | } 332 | else 333 | { 334 | v.x.value = ((v.x.value << fixlut.PRECISION) / m.value); 335 | v.y.value = ((v.y.value << fixlut.PRECISION) / m.value); 336 | } 337 | 338 | fp2 v1 = b; 339 | fp m1 = default; 340 | fp r3; 341 | 342 | r3.value = 343 | ((v1.x.value * v1.x.value) >> fixlut.PRECISION) + 344 | ((v1.y.value * v1.y.value) >> fixlut.PRECISION); 345 | fp r4; 346 | 347 | if (r3.value == 0) 348 | { 349 | r4.value = 0; 350 | } 351 | else 352 | { 353 | var b3 = (r3.value >> 1) + 1L; 354 | var c1 = (b3 + (r3.value / b3)) >> 1; 355 | 356 | while (c1 < b3) 357 | { 358 | b3 = c1; 359 | c1 = (b3 + (r3.value / b3)) >> 1; 360 | } 361 | 362 | r4.value = b3 << (fixlut.PRECISION >> 1); 363 | } 364 | 365 | m1 = r4; 366 | 367 | if (m1.value <= fp.epsilon.value) 368 | { 369 | v1 = default; 370 | } 371 | else 372 | { 373 | v1.x.value = ((v1.x.value << fixlut.PRECISION) / m1.value); 374 | v1.y.value = ((v1.y.value << fixlut.PRECISION) / m1.value); 375 | } 376 | 377 | fp2 a1 = v; 378 | fp2 b1 = v1; 379 | var x = ((a1.x.value * b1.x.value) >> fixlut.PRECISION); 380 | var z = ((a1.y.value * b1.y.value) >> fixlut.PRECISION); 381 | 382 | fp r; 383 | 384 | r.value = x + z; 385 | var dot = r; 386 | fp min = -fp._1; 387 | fp max = +fp._1; 388 | fp ret; 389 | if (dot.value < min.value) 390 | { 391 | ret = min; 392 | } 393 | else { 394 | ret = dot.value > max.value ? max : dot; 395 | } 396 | 397 | return new fp(fixlut.acos(ret.value)) * fp.rad2deg; 398 | } 399 | 400 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 401 | public static fp Radians(fp2 a, fp2 b) 402 | { 403 | fp2 v = a; 404 | fp m = default; 405 | fp r2; 406 | 407 | r2.value = 408 | ((v.x.value * v.x.value) >> fixlut.PRECISION) + 409 | ((v.y.value * v.y.value) >> fixlut.PRECISION); 410 | fp r1; 411 | 412 | if (r2.value == 0) 413 | { 414 | r1.value = 0; 415 | } 416 | else 417 | { 418 | var b2 = (r2.value >> 1) + 1L; 419 | var c = (b2 + (r2.value / b2)) >> 1; 420 | 421 | while (c < b2) 422 | { 423 | b2 = c; 424 | c = (b2 + (r2.value / b2)) >> 1; 425 | } 426 | 427 | r1.value = b2 << (fixlut.PRECISION >> 1); 428 | } 429 | 430 | m = r1; 431 | 432 | if (m.value <= fp.epsilon.value) 433 | { 434 | v = default; 435 | } 436 | else 437 | { 438 | v.x.value = ((v.x.value << fixlut.PRECISION) / m.value); 439 | v.y.value = ((v.y.value << fixlut.PRECISION) / m.value); 440 | } 441 | 442 | fp2 v1 = b; 443 | fp m1 = default; 444 | fp r3; 445 | 446 | r3.value = 447 | ((v1.x.value * v1.x.value) >> fixlut.PRECISION) + 448 | ((v1.y.value * v1.y.value) >> fixlut.PRECISION); 449 | fp r4; 450 | 451 | if (r3.value == 0) 452 | { 453 | r4.value = 0; 454 | } 455 | else 456 | { 457 | var b3 = (r3.value >> 1) + 1L; 458 | var c1 = (b3 + (r3.value / b3)) >> 1; 459 | 460 | while (c1 < b3) 461 | { 462 | b3 = c1; 463 | c1 = (b3 + (r3.value / b3)) >> 1; 464 | } 465 | 466 | r4.value = b3 << (fixlut.PRECISION >> 1); 467 | } 468 | 469 | m1 = r4; 470 | 471 | if (m1.value <= fp.epsilon.value) 472 | { 473 | v1 = default; 474 | } 475 | else 476 | { 477 | v1.x.value = ((v1.x.value << fixlut.PRECISION) / m1.value); 478 | v1.y.value = ((v1.y.value << fixlut.PRECISION) / m1.value); 479 | } 480 | 481 | fp2 a1 = v; 482 | fp2 b1 = v1; 483 | var x = ((a1.x.value * b1.x.value) >> fixlut.PRECISION); 484 | var z = ((a1.y.value * b1.y.value) >> fixlut.PRECISION); 485 | 486 | fp r; 487 | 488 | r.value = x + z; 489 | var dot = r; 490 | fp min = -fp._1; 491 | fp max = +fp._1; 492 | fp ret; 493 | if (dot.value < min.value) 494 | { 495 | ret = min; 496 | } 497 | else 498 | { 499 | if (dot.value > max.value) 500 | { 501 | ret = max; 502 | } 503 | else 504 | { 505 | ret = dot; 506 | } 507 | } 508 | 509 | return new fp(fixlut.acos(ret.value)); 510 | } 511 | 512 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 513 | public static fp RadiansSigned(fp2 a, fp2 b) 514 | { 515 | fp2 v = a; 516 | fp m = default; 517 | fp r2; 518 | 519 | r2.value = 520 | ((v.x.value * v.x.value) >> fixlut.PRECISION) + 521 | ((v.y.value * v.y.value) >> fixlut.PRECISION); 522 | fp r1; 523 | 524 | if (r2.value == 0) 525 | { 526 | r1.value = 0; 527 | } 528 | else 529 | { 530 | var b2 = (r2.value >> 1) + 1L; 531 | var c = (b2 + (r2.value / b2)) >> 1; 532 | 533 | while (c < b2) 534 | { 535 | b2 = c; 536 | c = (b2 + (r2.value / b2)) >> 1; 537 | } 538 | 539 | r1.value = b2 << (fixlut.PRECISION >> 1); 540 | } 541 | 542 | m = r1; 543 | 544 | if (m.value <= fp.epsilon.value) 545 | { 546 | v = default; 547 | } 548 | else 549 | { 550 | v.x.value = ((v.x.value << fixlut.PRECISION) / m.value); 551 | v.y.value = ((v.y.value << fixlut.PRECISION) / m.value); 552 | } 553 | 554 | fp2 v1 = b; 555 | fp m1 = default; 556 | fp r3; 557 | 558 | r3.value = 559 | ((v1.x.value * v1.x.value) >> fixlut.PRECISION) + 560 | ((v1.y.value * v1.y.value) >> fixlut.PRECISION); 561 | fp r4; 562 | 563 | if (r3.value == 0) 564 | { 565 | r4.value = 0; 566 | } 567 | else 568 | { 569 | var b3 = (r3.value >> 1) + 1L; 570 | var c1 = (b3 + (r3.value / b3)) >> 1; 571 | 572 | while (c1 < b3) 573 | { 574 | b3 = c1; 575 | c1 = (b3 + (r3.value / b3)) >> 1; 576 | } 577 | 578 | r4.value = b3 << (fixlut.PRECISION >> 1); 579 | } 580 | 581 | m1 = r4; 582 | 583 | if (m1.value <= fp.epsilon.value) 584 | { 585 | v1 = default; 586 | } 587 | else 588 | { 589 | v1.x.value = ((v1.x.value << fixlut.PRECISION) / m1.value); 590 | v1.y.value = ((v1.y.value << fixlut.PRECISION) / m1.value); 591 | } 592 | 593 | fp2 a1 = v; 594 | fp2 b1 = v1; 595 | var x = ((a1.x.value * b1.x.value) >> fixlut.PRECISION); 596 | var z = ((a1.y.value * b1.y.value) >> fixlut.PRECISION); 597 | 598 | fp r; 599 | 600 | r.value = x + z; 601 | var dot = r; 602 | fp min = -fp._1; 603 | fp max = +fp._1; 604 | fp ret; 605 | if (dot.value < min.value) 606 | { 607 | ret = min; 608 | } 609 | else 610 | { 611 | if (dot.value > max.value) 612 | { 613 | ret = max; 614 | } 615 | else 616 | { 617 | ret = dot; 618 | } 619 | } 620 | 621 | var rad = new fp(fixlut.acos(ret.value)); 622 | var sign = ((a.x * b.y - a.y * b.x).value < fixlut.ZERO) ? fp.minus_one : fp._1; 623 | 624 | return rad * sign; 625 | } 626 | 627 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 628 | public static fp RadiansSkipNormalize(fp2 a, fp2 b) 629 | { 630 | fp2 a1 = a; 631 | fp2 b1 = b; 632 | var x = ((a1.x.value * b1.x.value) >> fixlut.PRECISION); 633 | var z = ((a1.y.value * b1.y.value) >> fixlut.PRECISION); 634 | 635 | fp r; 636 | 637 | r.value = x + z; 638 | var dot = r; 639 | fp min = -fp._1; 640 | fp max = +fp._1; 641 | fp ret; 642 | if (dot.value < min.value) 643 | { 644 | ret = min; 645 | } 646 | else { 647 | ret = dot.value > max.value ? max : dot; 648 | } 649 | 650 | return new fp(fixlut.acos(ret.value)); 651 | } 652 | 653 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 654 | public static fp RadiansSignedSkipNormalize(fp2 a, fp2 b) 655 | { 656 | fp2 a1 = a; 657 | fp2 b1 = b; 658 | var x = ((a1.x.value * b1.x.value) >> fixlut.PRECISION); 659 | var z = ((a1.y.value * b1.y.value) >> fixlut.PRECISION); 660 | 661 | fp r; 662 | 663 | r.value = x + z; 664 | var dot = r; 665 | fp min = -fp._1; 666 | fp max = +fp._1; 667 | fp ret; 668 | if (dot.value < min.value) 669 | { 670 | ret = min; 671 | } 672 | else 673 | { 674 | if (dot.value > max.value) 675 | { 676 | ret = max; 677 | } 678 | else 679 | { 680 | ret = dot; 681 | } 682 | } 683 | 684 | var rad = new fp(fixlut.acos(ret.value)); 685 | var sign = ((a.x * b.y - a.y * b.x).value < fixlut.ZERO) ? fp.minus_one : fp._1; 686 | 687 | return rad * sign; 688 | } 689 | 690 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 691 | public static fp2 Reflect(fp2 vector, fp2 normal) 692 | { 693 | fp dot = (vector.x * normal.x) + (vector.y * normal.y); 694 | 695 | fp2 result; 696 | 697 | result.x = vector.x - ((fp._2 * dot) * normal.x); 698 | result.y = vector.y - ((fp._2 * dot) * normal.y); 699 | 700 | return result; 701 | } 702 | 703 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 704 | public static fp2 Rotate(fp2 vector, fp angle) 705 | { 706 | fp2 vector1 = vector; 707 | var cs = Cos(angle); 708 | var sn = Sin(angle); 709 | 710 | var px = (vector1.x * cs) - (vector1.y * sn); 711 | var pz = (vector1.x * sn) + (vector1.y * cs); 712 | 713 | vector1.x = px; 714 | vector1.y = pz; 715 | 716 | return vector1; 717 | } 718 | } 719 | } -------------------------------------------------------------------------------- /FixedPointSharp/fixmath3.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace Deterministic.FixedPoint 4 | { 5 | public partial struct fixmath 6 | { 7 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 8 | public static fp Sum(fp3 v) { 9 | return new fp(v.x.value + v.y.value + v.z.value); 10 | } 11 | 12 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 13 | public static fp3 Abs(fp3 v) 14 | { 15 | return new fp3(Abs(v.x), Abs(v.y), Abs(v.z)); 16 | } 17 | 18 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 19 | public static fp Dot(fp3 a, fp3 b) 20 | { 21 | return new fp(((a.x.value * b.x.value) >> fixlut.PRECISION) + ((a.y.value * b.y.value) >> fixlut.PRECISION) + ((a.z.value * b.z.value) >> fixlut.PRECISION)); 22 | } 23 | 24 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 25 | public static fp3 Cross(fp3 a, fp3 b) 26 | { 27 | fp3 r; 28 | 29 | r.x = a.y * b.z - a.z * b.y; 30 | r.y = a.z * b.x - a.x * b.z; 31 | r.z = a.x * b.y - a.y * b.x; 32 | 33 | return r; 34 | } 35 | 36 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 37 | public static fp Distance(fp3 p1, fp3 p2) 38 | { 39 | fp3 v; 40 | v.x.value = p1.x.value - p2.x.value; 41 | v.y.value = p1.y.value - p2.y.value; 42 | v.z.value = p1.z.value - p2.z.value; 43 | 44 | return Magnitude(v); 45 | } 46 | 47 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 48 | public static fp DistanceSqr(fp3 p1, fp3 p2) 49 | { 50 | fp3 v; 51 | v.x.value = p1.x.value - p2.x.value; 52 | v.y.value = p1.y.value - p2.y.value; 53 | v.z.value = p1.z.value - p2.z.value; 54 | 55 | return MagnitudeSqr(v); 56 | } 57 | 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | public static fp3 Lerp(fp3 from, fp3 to, fp t) 60 | { 61 | t = Clamp01(t); 62 | return new fp3(LerpUnclamped(from.x, to.x, t), LerpUnclamped(from.y, to.y, t), LerpUnclamped(from.z, to.z, t)); 63 | } 64 | 65 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 66 | public static fp3 LerpUnclamped(fp3 from, fp3 to, fp t) 67 | { 68 | return new fp3(LerpUnclamped(from.x, to.x, t), LerpUnclamped(from.y, to.y, t), LerpUnclamped(from.z, to.z, t)); 69 | } 70 | 71 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 72 | public static fp3 Reflect(fp3 vector, fp3 normal) 73 | { 74 | var num = -fp._2 * Dot(normal, vector); 75 | return new fp3(num * normal.x + vector.x, num * normal.y + vector.y, num * normal.z + vector.z); 76 | } 77 | 78 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 79 | public static fp3 Project(fp3 vector, fp3 normal) 80 | { 81 | var sqrMag = MagnitudeSqr(normal); 82 | if (sqrMag < fp.epsilon) 83 | return fp3.zero; 84 | 85 | var dot = Dot(vector, normal); 86 | return normal * dot / sqrMag; 87 | } 88 | 89 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 90 | public static fp3 ProjectOnPlane(fp3 vector, fp3 planeNormal) 91 | { 92 | var sqrMag = MagnitudeSqr(planeNormal); 93 | if (sqrMag < fp.epsilon) 94 | return vector; 95 | 96 | var dot = Dot(vector, planeNormal); 97 | return vector - planeNormal * dot / sqrMag; 98 | } 99 | 100 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 101 | public static fp3 MoveTowards(fp3 current, fp3 target, fp maxDelta) 102 | { 103 | var v = target - current; 104 | var sqrMagnitude = MagnitudeSqr(v); 105 | if (v == fp3.zero || maxDelta >= fp._0 && sqrMagnitude <= maxDelta * maxDelta) 106 | return target; 107 | 108 | var magnitude = Sqrt(sqrMagnitude); 109 | return current + v / magnitude * maxDelta; 110 | } 111 | 112 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 113 | public static fp Angle(fp3 a, fp3 b) 114 | { 115 | var sqr = MagnitudeSqr(a) * MagnitudeSqr(b); 116 | var n = Sqrt(sqr); 117 | 118 | if (n < fp.epsilon) 119 | { 120 | return fp._0; 121 | } 122 | 123 | return Acos(Clamp(Dot(a, b) / n, fp.minus_one, fp._1)) * fp.rad2deg; 124 | } 125 | 126 | public static fp AngleSigned(fp3 a, fp3 b, fp3 axis) 127 | { 128 | var angle = Angle(a, b); 129 | long num2 = ((a.y.value * b.z.value) >> fixlut.PRECISION) - ((a.z.value * b.y.value) >> fixlut.PRECISION); 130 | long num3 = ((a.z.value * b.x.value) >> fixlut.PRECISION) - ((a.x.value * b.z.value) >> fixlut.PRECISION); 131 | long num4 = ((a.x.value * b.y.value) >> fixlut.PRECISION) - ((a.y.value * b.x.value) >> fixlut.PRECISION); 132 | var sign = (((axis.x.value * num2) >> fixlut.PRECISION) + 133 | ((axis.y.value * num3) >> fixlut.PRECISION) + 134 | ((axis.z.value * num4) >> fixlut.PRECISION)) < 0 135 | ? fp.minus_one 136 | : fp._1; 137 | return angle * sign; 138 | } 139 | 140 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 141 | public static fp Radians(fp3 a, fp3 b) 142 | { 143 | return Acos(Clamp(Dot(Normalize(a), Normalize(b)), fp.minus_one, fp._1)); 144 | } 145 | 146 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 147 | public static fp RadiansSkipNormalize(fp3 a, fp3 b) 148 | { 149 | return Acos(Clamp(Dot(a, b), fp.minus_one, fp._1)); 150 | } 151 | 152 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 153 | public static fp MagnitudeSqr(fp3 v) 154 | { 155 | v.x.value = 156 | ((v.x.value * v.x.value) >> fixlut.PRECISION) + 157 | ((v.y.value * v.y.value) >> fixlut.PRECISION) + 158 | ((v.z.value * v.z.value) >> fixlut.PRECISION); 159 | 160 | return v.x; 161 | } 162 | 163 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 164 | public static fp Magnitude(fp3 v) 165 | { 166 | v.x.value = 167 | ((v.x.value * v.x.value) >> fixlut.PRECISION) + 168 | ((v.y.value * v.y.value) >> fixlut.PRECISION) + 169 | ((v.z.value * v.z.value) >> fixlut.PRECISION); 170 | 171 | return Sqrt(v.x); 172 | } 173 | 174 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 175 | public static fp3 MagnitudeClamp(fp3 v, fp length) 176 | { 177 | var sqrMagnitude = MagnitudeSqr(v); 178 | if (sqrMagnitude <= length * length) 179 | return v; 180 | 181 | var magnitude = Sqrt(sqrMagnitude); 182 | var normalized = v / magnitude; 183 | return normalized * length; 184 | } 185 | 186 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 187 | public static fp3 MagnitudeSet(fp3 v, fp length) 188 | { 189 | return Normalize(v) * length; 190 | } 191 | 192 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 193 | public static fp3 Min(fp3 a, fp3 b) 194 | { 195 | return new fp3(Min(a.x, b.x), Min(a.y, b.y), Min(a.z, b.z)); 196 | } 197 | 198 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 199 | public static fp3 Max(fp3 a, fp3 b) 200 | { 201 | return new fp3(Max(a.x, b.x), Max(a.y, b.y), Max(a.z, b.z)); 202 | } 203 | 204 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 205 | public static fp3 Normalize(fp3 v) 206 | { 207 | if (v == fp3.zero) 208 | return fp3.zero; 209 | 210 | return v / Magnitude(v); 211 | } 212 | 213 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 214 | public static fp3 Normalize(fp3 v, out fp magnitude) 215 | { 216 | if (v == fp3.zero) 217 | { 218 | magnitude = fp._0; 219 | return fp3.zero; 220 | } 221 | 222 | magnitude = Magnitude(v); 223 | return v / magnitude; 224 | } 225 | } 226 | } -------------------------------------------------------------------------------- /FixedPointSharp/fixmath4.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.CompilerServices; 2 | 3 | namespace Deterministic.FixedPoint { 4 | public partial struct fixmath 5 | { 6 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 7 | public static fp Sum(fp4 v) { 8 | return new fp(v.x.value + v.y.value + v.z.value + v.w.value); 9 | } 10 | 11 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 12 | public static fp4 Abs(fp4 v) 13 | { 14 | return new fp4(Abs(v.x), Abs(v.y), Abs(v.z), Abs(v.w)); 15 | } 16 | 17 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 18 | public static fp Dot(fp4 a, fp4 b) { 19 | return new fp(((a.x.value * b.x.value) >> fixlut.PRECISION) + ((a.y.value * b.y.value) >> fixlut.PRECISION) + ((a.z.value * b.z.value) >> fixlut.PRECISION) + 20 | ((a.w.value * b.w.value) >> fixlut.PRECISION)); 21 | } 22 | 23 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 24 | public static fp4 Normalize(fp4 v) 25 | { 26 | if (v == fp4.zero) 27 | return fp4.zero; 28 | 29 | return v / Magnitude(v); 30 | } 31 | 32 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 33 | public static fp4 Normalize(fp4 v, out fp magnitude) 34 | { 35 | if (v == fp4.zero) 36 | { 37 | magnitude = fp._0; 38 | return fp4.zero; 39 | } 40 | 41 | magnitude = Magnitude(v); 42 | return v / magnitude; 43 | } 44 | 45 | 46 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 47 | public static fp Distance(fp4 p1, fp4 p2) 48 | { 49 | fp4 v; 50 | v.x.value = p1.x.value - p2.x.value; 51 | v.y.value = p1.y.value - p2.y.value; 52 | v.z.value = p1.z.value - p2.z.value; 53 | v.w.value = p1.w.value - p2.w.value; 54 | 55 | return Magnitude(v); 56 | } 57 | 58 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 59 | public static fp DistanceSqr(fp4 p1, fp4 p2) 60 | { 61 | fp4 v; 62 | v.x.value = p1.x.value - p2.x.value; 63 | v.y.value = p1.y.value - p2.y.value; 64 | v.z.value = p1.z.value - p2.z.value; 65 | v.w.value = p1.w.value - p2.w.value; 66 | 67 | return MagnitudeSqr(v); 68 | } 69 | 70 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 71 | public static fp MagnitudeSqr(fp4 v) 72 | { 73 | v.x.value = 74 | ((v.x.value * v.x.value) >> fixlut.PRECISION) + 75 | ((v.y.value * v.y.value) >> fixlut.PRECISION) + 76 | ((v.z.value * v.z.value) >> fixlut.PRECISION) + 77 | ((v.w.value * v.w.value) >> fixlut.PRECISION); 78 | 79 | return v.x; 80 | } 81 | 82 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 83 | public static fp Magnitude(fp4 v) 84 | { 85 | v.x.value = 86 | ((v.x.value * v.x.value) >> fixlut.PRECISION) + 87 | ((v.y.value * v.y.value) >> fixlut.PRECISION) + 88 | ((v.z.value * v.z.value) >> fixlut.PRECISION) + 89 | ((v.w.value * v.w.value) >> fixlut.PRECISION); 90 | 91 | return Sqrt(v.x); 92 | } 93 | 94 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 95 | public static fp4 MagnitudeClamp(fp4 v, fp length) 96 | { 97 | var sqrMagnitude = MagnitudeSqr(v); 98 | if (sqrMagnitude <= length * length) 99 | return v; 100 | 101 | var magnitude = Sqrt(sqrMagnitude); 102 | var normalized = v / magnitude; 103 | return normalized * length; 104 | } 105 | 106 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 107 | public static fp4 MagnitudeSet(fp4 v, fp length) 108 | { 109 | return Normalize(v) * length; 110 | } 111 | 112 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 113 | public static fp4 Min(fp4 a, fp4 b) 114 | { 115 | return new fp4(Min(a.x, b.x), Min(a.y, b.y), Min(a.z, b.z), Min(a.w, b.w)); 116 | } 117 | 118 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 119 | public static fp4 Max(fp4 a, fp4 b) 120 | { 121 | return new fp4(Max(a.x, b.x), Max(a.y, b.y), Max(a.z, b.z), Max(a.w, b.w)); 122 | } 123 | } 124 | } -------------------------------------------------------------------------------- /FixedPointSharp/fp.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Globalization; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | 7 | namespace Deterministic.FixedPoint { 8 | [Serializable] 9 | [StructLayout(LayoutKind.Explicit, Size = SIZE)] 10 | public struct fp : IEquatable, IComparable { 11 | public const int SIZE = 8; 12 | 13 | public static readonly fp max = new fp(long.MaxValue); 14 | public static readonly fp min = new fp(long.MinValue); 15 | public static readonly fp usable_max = new fp(2147483648L); 16 | public static readonly fp usable_min = -usable_max; 17 | 18 | public static readonly fp _0 = 0; 19 | public static readonly fp _1 = 1; 20 | public static readonly fp _2 = 2; 21 | public static readonly fp _3 = 3; 22 | public static readonly fp _4 = 4; 23 | public static readonly fp _5 = 5; 24 | public static readonly fp _6 = 6; 25 | public static readonly fp _7 = 7; 26 | public static readonly fp _8 = 8; 27 | public static readonly fp _9 = 9; 28 | public static readonly fp _10 = 10; 29 | public static readonly fp _99 = 99; 30 | public static readonly fp _100 = 100; 31 | public static readonly fp _200 = 200; 32 | 33 | public static readonly fp _0_01 = _1 / _100; 34 | public static readonly fp _0_02 = _0_01 * 2; 35 | public static readonly fp _0_03 = _0_01 * 3; 36 | public static readonly fp _0_04 = _0_01 * 4; 37 | public static readonly fp _0_05 = _0_01 * 5; 38 | public static readonly fp _0_10 = _1 / 10; 39 | public static readonly fp _0_20 = _0_10 * 2; 40 | public static readonly fp _0_25 = _1 / 4; 41 | public static readonly fp _0_33 = _1 / 3; 42 | public static readonly fp _0_50 = _1 / 2; 43 | public static readonly fp _0_75 = _1 - _0_25; 44 | public static readonly fp _0_95 = _1 - _0_05; 45 | public static readonly fp _0_99 = _1 - _0_01; 46 | public static readonly fp _1_01 = _1 + _0_01; 47 | public static readonly fp _1_10 = _1 + _0_10; 48 | public static readonly fp _1_50 = _1 + _0_50; 49 | 50 | public static readonly fp minus_one = -1; 51 | public static readonly fp pi = new fp(205887L); 52 | public static readonly fp pi2 = pi * 2; 53 | public static readonly fp pi_quarter = pi * _0_25; 54 | public static readonly fp pi_half = pi * _0_50; 55 | public static readonly fp one_div_pi2 = 1 / pi2; 56 | public static readonly fp deg2rad = new fp(1143L); 57 | public static readonly fp rad2deg = new fp(3754936L); 58 | public static readonly fp epsilon = new fp(1); 59 | public static readonly fp e = new fp(178145L); 60 | 61 | [FieldOffset(0)] 62 | public long value; 63 | 64 | public long AsLong => value >> fixlut.PRECISION; 65 | public int AsInt => (int) (value >> fixlut.PRECISION); 66 | public float AsFloat => value / 65536f; 67 | public float AsFloatRounded => (float) Math.Round(value / 65536f, 5); 68 | public double AsDouble => value / 65536d; 69 | public double AsDoubleRounded => Math.Round(value / 65536d, 5); 70 | 71 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 72 | internal fp(long v) { 73 | value = v; 74 | } 75 | 76 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 77 | public static fp operator -(fp a) { 78 | a.value = -a.value; 79 | return a; 80 | } 81 | 82 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 83 | public static fp operator +(fp a) { 84 | a.value = +a.value; 85 | return a; 86 | } 87 | 88 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 89 | public static fp operator +(fp a, fp b) { 90 | a.value += b.value; 91 | return a; 92 | } 93 | 94 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 95 | public static fp operator +(fp a, int b) { 96 | a.value += (long) b << fixlut.PRECISION; 97 | return a; 98 | } 99 | 100 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 101 | public static fp operator +(int a, fp b) { 102 | b.value = ((long) a << fixlut.PRECISION) + b.value; 103 | return b; 104 | } 105 | 106 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 107 | public static fp operator -(fp a, fp b) { 108 | a.value -= b.value; 109 | return a; 110 | } 111 | 112 | 113 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 114 | public static fp operator -(fp a, int b) { 115 | a.value -= (long) b << fixlut.PRECISION; 116 | return a; 117 | } 118 | 119 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 120 | public static fp operator -(int a, fp b) { 121 | b.value = ((long) a << fixlut.PRECISION) - b.value; 122 | return b; 123 | } 124 | 125 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 126 | public static fp operator *(fp a, fp b) { 127 | a.value = a.value * b.value >> fixlut.PRECISION; 128 | return a; 129 | } 130 | 131 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 132 | public static fp operator *(fp a, int b) { 133 | a.value *= b; 134 | return a; 135 | } 136 | 137 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 138 | public static fp operator *(int a, fp b) { 139 | b.value *= a; 140 | return b; 141 | } 142 | 143 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 144 | public static fp operator /(fp a, fp b) { 145 | a.value = (a.value << fixlut.PRECISION) / b.value; 146 | return a; 147 | } 148 | 149 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 150 | public static fp operator /(fp a, int b) { 151 | a.value /= b; 152 | return a; 153 | } 154 | 155 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 156 | public static fp operator /(int a, fp b) { 157 | b.value = ((long) a << 32) / b.value; 158 | return b; 159 | } 160 | 161 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 162 | public static fp operator %(fp a, fp b) { 163 | a.value %= b.value; 164 | return a; 165 | } 166 | 167 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 168 | public static fp operator %(fp a, int b) { 169 | a.value %= (long) b << fixlut.PRECISION; 170 | return a; 171 | } 172 | 173 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 174 | public static fp operator %(int a, fp b) { 175 | b.value = ((long) a << fixlut.PRECISION) % b.value; 176 | return b; 177 | } 178 | 179 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 180 | public static bool operator <(fp a, fp b) { 181 | return a.value < b.value; 182 | } 183 | 184 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 185 | public static bool operator <(fp a, int b) { 186 | return a.value < (long) b << fixlut.PRECISION; 187 | } 188 | 189 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 190 | public static bool operator <(int a, fp b) { 191 | return (long) a << fixlut.PRECISION < b.value; 192 | } 193 | 194 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 195 | public static bool operator <=(fp a, fp b) { 196 | return a.value <= b.value; 197 | } 198 | 199 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 200 | public static bool operator <=(fp a, int b) { 201 | return a.value <= (long) b << fixlut.PRECISION; 202 | } 203 | 204 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 205 | public static bool operator <=(int a, fp b) { 206 | return (long) a << fixlut.PRECISION <= b.value; 207 | } 208 | 209 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 210 | public static bool operator >(fp a, fp b) { 211 | return a.value > b.value; 212 | } 213 | 214 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 215 | public static bool operator >(fp a, int b) { 216 | return a.value > (long) b << fixlut.PRECISION; 217 | } 218 | 219 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 220 | public static bool operator >(int a, fp b) { 221 | return (long) a << fixlut.PRECISION > b.value; 222 | } 223 | 224 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 225 | public static bool operator >=(fp a, fp b) { 226 | return a.value >= b.value; 227 | } 228 | 229 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 230 | public static bool operator >=(fp a, int b) { 231 | return a.value >= (long) b << fixlut.PRECISION; 232 | } 233 | 234 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 235 | public static bool operator >=(int a, fp b) { 236 | return (long) a << fixlut.PRECISION >= b.value; 237 | } 238 | 239 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 240 | public static bool operator ==(fp a, fp b) { 241 | return a.value == b.value; 242 | } 243 | 244 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 245 | public static bool operator ==(fp a, int b) { 246 | return a.value == (long) b << fixlut.PRECISION; 247 | } 248 | 249 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 250 | public static bool operator ==(int a, fp b) { 251 | return (long) a << fixlut.PRECISION == b.value; 252 | } 253 | 254 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 255 | public static bool operator !=(fp a, fp b) { 256 | return a.value != b.value; 257 | } 258 | 259 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 260 | public static bool operator !=(fp a, int b) { 261 | return a.value != (long) b << fixlut.PRECISION; 262 | } 263 | 264 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 265 | public static bool operator !=(int a, fp b) { 266 | return (long) a << fixlut.PRECISION != b.value; 267 | } 268 | 269 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 270 | public static implicit operator fp(int value) { 271 | fp f; 272 | f.value = (long) value << fixlut.PRECISION; 273 | return f; 274 | } 275 | 276 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 277 | public static explicit operator int(fp value) { 278 | return (int) (value.value >> fixlut.PRECISION); 279 | } 280 | 281 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 282 | public static explicit operator long(fp value) { 283 | return value.value >> fixlut.PRECISION; 284 | } 285 | 286 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 287 | public static explicit operator float(fp value) { 288 | return value.value / 65536f; 289 | } 290 | 291 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 292 | public static explicit operator double(fp value) { 293 | return value.value / 65536d; 294 | } 295 | 296 | public int CompareTo(fp other) { 297 | return value.CompareTo(other.value); 298 | } 299 | 300 | public bool Equals(fp other) { 301 | return value == other.value; 302 | } 303 | 304 | public override bool Equals(object obj) { 305 | return obj is fp other && this == other; 306 | } 307 | 308 | public override int GetHashCode() { 309 | return value.GetHashCode(); 310 | } 311 | 312 | public override string ToString() { 313 | var corrected = Math.Round(AsDouble, 5); 314 | return corrected.ToString("F5", CultureInfo.InvariantCulture); 315 | } 316 | 317 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 318 | public static fp ParseRaw(long value) { 319 | return new fp(value); 320 | } 321 | 322 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 323 | public static fp Parse(long value) { 324 | return new fp(value << fixlut.PRECISION); 325 | } 326 | 327 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 328 | public static fp ParseUnsafe(float value) { 329 | return new fp((long) (value * fixlut.ONE + 0.5f * (value < 0 ? -1 : 1))); 330 | } 331 | 332 | public static fp ParseUnsafe(string value) { 333 | var doubleValue = double.Parse(value, CultureInfo.InvariantCulture); 334 | var longValue = (long) (doubleValue * fixlut.ONE + 0.5d * (doubleValue < 0 ? -1 : 1)); 335 | return new fp(longValue); 336 | } 337 | 338 | /// 339 | /// Deterministically parses FP value out of a string 340 | /// 341 | /// Trimmed string to parse 342 | public static fp Parse(string value) { 343 | if (string.IsNullOrEmpty(value)) 344 | { 345 | return _0; 346 | } 347 | 348 | bool negative; 349 | 350 | var startIndex = 0; 351 | if (negative = (value[0] == '-')) { 352 | startIndex = 1; 353 | } 354 | 355 | var pointIndex = value.IndexOf('.'); 356 | if (pointIndex < startIndex) { 357 | if (startIndex == 0) { 358 | return ParseInteger(value); 359 | } 360 | 361 | return -ParseInteger(value.Substring(startIndex, value.Length - startIndex)); 362 | 363 | } 364 | 365 | var result = new fp(); 366 | 367 | if (pointIndex > startIndex) { 368 | var integerString = value.Substring(startIndex, pointIndex - startIndex); 369 | result += ParseInteger(integerString); 370 | } 371 | 372 | 373 | if (pointIndex == value.Length - 1) { 374 | return negative ? -result : result; 375 | } 376 | 377 | var fractionString = value.Substring(pointIndex + 1, value.Length - pointIndex - 1); 378 | if (fractionString.Length > 0) { 379 | result += ParseFractions(fractionString); 380 | } 381 | 382 | return negative ? -result : result; 383 | } 384 | 385 | private static fp ParseInteger(string format) { 386 | return Parse(long.Parse(format, CultureInfo.InvariantCulture)); 387 | } 388 | 389 | private static fp ParseFractions(string format) { 390 | format = format.Length < 5 ? format.PadRight(5, '0') : format.Substring(0, 5); 391 | return ParseRaw(long.Parse(format, CultureInfo.InvariantCulture) * 65536 / 100000); 392 | } 393 | 394 | public class Comparer : IComparer { 395 | public static readonly Comparer instance = new Comparer(); 396 | 397 | private Comparer() { } 398 | 399 | int IComparer.Compare(fp x, fp y) { 400 | return x.value.CompareTo(y.value); 401 | } 402 | } 403 | 404 | public class EqualityComparer : IEqualityComparer { 405 | public static readonly EqualityComparer instance = new EqualityComparer(); 406 | 407 | private EqualityComparer() { } 408 | 409 | bool IEqualityComparer.Equals(fp x, fp y) { 410 | return x.value == y.value; 411 | } 412 | 413 | int IEqualityComparer.GetHashCode(fp num) { 414 | return num.value.GetHashCode(); 415 | } 416 | } 417 | } 418 | } -------------------------------------------------------------------------------- /FixedPointSharp/fp2.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Deterministic.FixedPoint { 7 | [Serializable] 8 | [StructLayout(LayoutKind.Explicit, Size = SIZE)] 9 | public struct fp2 : IEquatable { 10 | public const int SIZE = 16; 11 | 12 | public static readonly fp2 left = new fp2(-fp._1, fp._0); 13 | public static readonly fp2 right = new fp2(fp._1, fp._0); 14 | public static readonly fp2 up = new fp2(fp._0, fp._1); 15 | public static readonly fp2 down = new fp2(fp._0, -fp._1); 16 | public static readonly fp2 one = new fp2(fp._1, fp._1); 17 | public static readonly fp2 minus_one = new fp2(fp.minus_one, fp.minus_one); 18 | public static readonly fp2 zero = new fp2(fp._0, fp._0); 19 | 20 | [FieldOffset(0)] 21 | public fp x; 22 | 23 | [FieldOffset(8)] 24 | public fp y; 25 | 26 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 27 | public fp2(fp x, fp y) { 28 | this.x.value = x.value; 29 | this.y.value = y.value; 30 | } 31 | 32 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 33 | public static fp2 operator +(fp2 a, fp2 b) { 34 | a.x.value += b.x.value; 35 | a.y.value += b.y.value; 36 | return a; 37 | } 38 | 39 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 40 | public static fp2 operator -(fp2 a, fp2 b) { 41 | a.x.value -= b.x.value; 42 | a.y.value -= b.y.value; 43 | 44 | return a; 45 | } 46 | 47 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 48 | public static fp2 operator -(fp2 a) { 49 | a.x.value = -a.x.value; 50 | a.y.value = -a.y.value; 51 | 52 | return a; 53 | } 54 | 55 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 56 | public static fp2 operator *(fp2 a, fp2 b) { 57 | a.x.value = (a.x.value * b.x.value) >> fixlut.PRECISION; 58 | a.y.value = (a.y.value * b.y.value) >> fixlut.PRECISION; 59 | 60 | return a; 61 | } 62 | 63 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 64 | public static fp2 operator *(fp2 a, fp b) { 65 | a.x.value = (a.x.value * b.value) >> fixlut.PRECISION; 66 | a.y.value = (a.y.value * b.value) >> fixlut.PRECISION; 67 | 68 | return a; 69 | } 70 | 71 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 72 | public static fp2 operator *(fp b, fp2 a) { 73 | a.x.value = (a.x.value * b.value) >> fixlut.PRECISION; 74 | a.y.value = (a.y.value * b.value) >> fixlut.PRECISION; 75 | 76 | return a; 77 | } 78 | 79 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 80 | public static fp2 operator /(fp2 a, fp2 b) { 81 | a.x.value = (a.x.value << fixlut.PRECISION) / b.x.value; 82 | a.y.value = (a.y.value << fixlut.PRECISION) / b.y.value; 83 | 84 | return a; 85 | } 86 | 87 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 88 | public static fp2 operator /(fp2 a, fp b) { 89 | a.x.value = (a.x.value << fixlut.PRECISION) / b.value; 90 | a.y.value = (a.y.value << fixlut.PRECISION) / b.value; 91 | 92 | return a; 93 | } 94 | 95 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 96 | public static fp2 operator /(fp b, fp2 a) { 97 | a.x.value = (a.x.value << fixlut.PRECISION) / b.value; 98 | a.y.value = (a.y.value << fixlut.PRECISION) / b.value; 99 | 100 | return a; 101 | } 102 | 103 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 104 | public static bool operator ==(fp2 a, fp2 b) { 105 | return a.x.value == b.x.value && a.y.value == b.y.value; 106 | } 107 | 108 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 109 | public static bool operator !=(fp2 a, fp2 b) { 110 | return a.x.value != b.x.value || a.y.value != b.y.value; 111 | } 112 | 113 | public override bool Equals(object obj) { 114 | return obj is fp2 other && this == other; 115 | } 116 | 117 | public bool Equals(fp2 other) { 118 | return this == other; 119 | } 120 | 121 | public override int GetHashCode() { 122 | unchecked { 123 | return (x.GetHashCode() * 397) ^ y.GetHashCode(); 124 | } 125 | } 126 | 127 | public override string ToString() { 128 | return $"({x}, {y})"; 129 | } 130 | 131 | public class EqualityComparer : IEqualityComparer { 132 | public static readonly EqualityComparer instance = new EqualityComparer(); 133 | 134 | private EqualityComparer() { } 135 | 136 | bool IEqualityComparer.Equals(fp2 x, fp2 y) { 137 | return x == y; 138 | } 139 | 140 | int IEqualityComparer.GetHashCode(fp2 obj) { 141 | return obj.GetHashCode(); 142 | } 143 | } 144 | 145 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 146 | public fp2 Normalize() { 147 | return fixmath.Normalize(this); 148 | } 149 | 150 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 151 | public fp Magnitude() { 152 | return fixmath.Magnitude(this); 153 | } 154 | 155 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 156 | public fp MagnitudeSqr() { 157 | return fixmath.MagnitudeSqr(this); 158 | } 159 | } 160 | } -------------------------------------------------------------------------------- /FixedPointSharp/fp3.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Runtime.InteropServices; 5 | 6 | namespace Deterministic.FixedPoint { 7 | [Serializable] 8 | [StructLayout(LayoutKind.Explicit, Size = SIZE)] 9 | public struct fp3 : IEquatable { 10 | public const int SIZE = 24; 11 | 12 | public static readonly fp3 left = new fp3(-fp._1, fp._0, fp._0); 13 | public static readonly fp3 right = new fp3(fp._1, fp._0, fp._0); 14 | public static readonly fp3 up = new fp3(fp._0, fp._1, fp._0); 15 | public static readonly fp3 down = new fp3(fp._0, fp.minus_one, fp._0); 16 | public static readonly fp3 forward = new fp3(fp._0, fp._0, fp._1); 17 | public static readonly fp3 backward = new fp3(fp._0, fp._0, fp.minus_one); 18 | public static readonly fp3 one = new fp3(fp._1, fp._1, fp._1); 19 | public static readonly fp3 minus_one = new fp3(fp.minus_one, fp.minus_one, fp.minus_one); 20 | public static readonly fp3 zero = new fp3(fp._0, fp._0, fp._0); 21 | 22 | [FieldOffset(0)] 23 | public fp x; 24 | 25 | [FieldOffset(8)] 26 | public fp y; 27 | 28 | [FieldOffset(16)] 29 | public fp z; 30 | 31 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 32 | public fp3(fp x, fp y, fp z) { 33 | this.x.value = x.value; 34 | this.y.value = y.value; 35 | this.z.value = z.value; 36 | } 37 | 38 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 39 | public fp3(fp2 xy, fp z) { 40 | x.value = xy.x.value; 41 | y.value = xy.y.value; 42 | this.z.value = z.value; 43 | } 44 | 45 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 46 | public static fp3 operator +(fp3 a, fp3 b) { 47 | a.x.value += b.x.value; 48 | a.y.value += b.y.value; 49 | a.z.value += b.z.value; 50 | 51 | return a; 52 | } 53 | 54 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 55 | public static fp3 operator -(fp3 a, fp3 b) { 56 | a.x.value -= b.x.value; 57 | a.y.value -= b.y.value; 58 | a.z.value -= b.z.value; 59 | 60 | return a; 61 | } 62 | 63 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 64 | public static fp3 operator -(fp3 a) { 65 | a.x.value = -a.x.value; 66 | a.y.value = -a.y.value; 67 | a.z.value = -a.z.value; 68 | 69 | return a; 70 | } 71 | 72 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 73 | public static fp3 operator *(fp3 a, fp3 b) { 74 | a.x.value = (a.x.value * b.x.value) >> fixlut.PRECISION; 75 | a.y.value = (a.y.value * b.y.value) >> fixlut.PRECISION; 76 | a.z.value = (a.z.value * b.z.value) >> fixlut.PRECISION; 77 | 78 | return a; 79 | } 80 | 81 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 82 | public static fp3 operator *(fp3 a, fp b) { 83 | a.x.value = (a.x.value * b.value) >> fixlut.PRECISION; 84 | a.y.value = (a.y.value * b.value) >> fixlut.PRECISION; 85 | a.z.value = (a.z.value * b.value) >> fixlut.PRECISION; 86 | 87 | return a; 88 | } 89 | 90 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 91 | public static fp3 operator *(fp b, fp3 a) { 92 | a.x.value = (a.x.value * b.value) >> fixlut.PRECISION; 93 | a.y.value = (a.y.value * b.value) >> fixlut.PRECISION; 94 | a.z.value = (a.z.value * b.value) >> fixlut.PRECISION; 95 | 96 | return a; 97 | } 98 | 99 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 100 | public static fp3 operator /(fp3 a, fp3 b) { 101 | a.x.value = (a.x.value << fixlut.PRECISION) / b.x.value; 102 | a.y.value = (a.y.value << fixlut.PRECISION) / b.y.value; 103 | a.z.value = (a.z.value << fixlut.PRECISION) / b.z.value; 104 | 105 | return a; 106 | } 107 | 108 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 109 | public static fp3 operator /(fp3 a, fp b) { 110 | a.x.value = (a.x.value << fixlut.PRECISION) / b.value; 111 | a.y.value = (a.y.value << fixlut.PRECISION) / b.value; 112 | a.z.value = (a.z.value << fixlut.PRECISION) / b.value; 113 | 114 | return a; 115 | } 116 | 117 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 118 | public static fp3 operator /(fp b, fp3 a) { 119 | a.x.value = (a.x.value << fixlut.PRECISION) / b.value; 120 | a.y.value = (a.y.value << fixlut.PRECISION) / b.value; 121 | a.z.value = (a.z.value << fixlut.PRECISION) / b.value; 122 | 123 | return a; 124 | } 125 | 126 | 127 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 128 | public static bool operator ==(fp3 a, fp3 b) { 129 | return a.x.value == b.x.value && a.y.value == b.y.value && a.z.value == b.z.value; 130 | } 131 | 132 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 133 | public static bool operator !=(fp3 a, fp3 b) { 134 | return a.x.value != b.x.value || a.y.value != b.y.value || a.z.value != b.z.value; 135 | } 136 | 137 | public bool Equals(fp3 other) { 138 | return x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z); 139 | } 140 | 141 | public override bool Equals(object obj) { 142 | return obj is fp3 other && this == other; 143 | } 144 | 145 | public override int GetHashCode() { 146 | unchecked { 147 | var hashCode = x.GetHashCode(); 148 | hashCode = (hashCode * 397) ^ y.GetHashCode(); 149 | hashCode = (hashCode * 397) ^ z.GetHashCode(); 150 | return hashCode; 151 | } 152 | } 153 | 154 | public override string ToString() { 155 | return $"({x}, {y}, {z})"; 156 | } 157 | 158 | public class EqualityComparer : IEqualityComparer { 159 | public static readonly EqualityComparer instance = new EqualityComparer(); 160 | 161 | private EqualityComparer() { } 162 | 163 | bool IEqualityComparer.Equals(fp3 x, fp3 y) { 164 | return x == y; 165 | } 166 | 167 | int IEqualityComparer.GetHashCode(fp3 obj) { 168 | return obj.GetHashCode(); 169 | } 170 | } 171 | 172 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 173 | public fp3 Normalize() { 174 | return fixmath.Normalize(this); 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /FixedPointSharp/fp4.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | namespace Deterministic.FixedPoint { 6 | [StructLayout(LayoutKind.Explicit, Size = SIZE)] 7 | public struct fp4 : IEquatable { 8 | public const int SIZE = 32; 9 | 10 | [FieldOffset(0)] 11 | public fp x; 12 | 13 | [FieldOffset(8)] 14 | public fp y; 15 | 16 | [FieldOffset(16)] 17 | public fp z; 18 | 19 | [FieldOffset(24)] 20 | public fp w; 21 | 22 | public static readonly fp4 zero; 23 | public static readonly fp4 one = new fp4 {x = fp._1, y = fp._1, z = fp._1, w = fp._1}; 24 | public static readonly fp4 minus_one = new fp4 {x = fp.minus_one, y = fp.minus_one, z = fp.minus_one, w = fp.minus_one}; 25 | 26 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 27 | public fp4(fp x, fp y, fp z, fp w) { 28 | this.x = x; 29 | this.y = y; 30 | this.z = z; 31 | this.w = w; 32 | } 33 | 34 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 35 | public fp4(fp2 xy, fp2 zw) { 36 | x = xy.x; 37 | y = xy.y; 38 | z = zw.x; 39 | w = zw.y; 40 | } 41 | 42 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 43 | public fp4(fp3 v, fp w) { 44 | x = v.x; 45 | y = v.y; 46 | z = v.z; 47 | this.w = w; 48 | } 49 | 50 | /// 51 | /// Initializes fp4 vector with 48.16 fp format long values 52 | /// 53 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 54 | internal fp4(long x, long y, long z, long w) { 55 | this.x.value = x; 56 | this.y.value = y; 57 | this.z.value = z; 58 | this.w.value = w; 59 | } 60 | 61 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 62 | public static fp4 operator *(fp4 lhs, fp4 rhs) { 63 | return new fp4((lhs.x.value * rhs.x.value) >> fixlut.PRECISION, (lhs.y.value * rhs.y.value) >> fixlut.PRECISION, 64 | (lhs.z.value * rhs.z.value) >> fixlut.PRECISION, (lhs.w.value * rhs.w.value) >> fixlut.PRECISION); 65 | } 66 | 67 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 68 | public static fp4 operator *(fp4 lhs, fp rhs) { 69 | return new fp4(lhs.x * rhs, lhs.y * rhs, lhs.z * rhs, lhs.w * rhs); 70 | } 71 | 72 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 73 | public static fp4 operator *(fp lhs, fp4 rhs) { 74 | return new fp4(lhs * rhs.x, lhs * rhs.y, lhs * rhs.z, lhs * rhs.w); 75 | } 76 | 77 | 78 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 79 | public static fp4 operator +(fp4 lhs, fp4 rhs) { 80 | return new fp4(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z, lhs.w + rhs.w); 81 | } 82 | 83 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 84 | public static fp4 operator +(fp4 lhs, fp rhs) { 85 | return new fp4(lhs.x + rhs, lhs.y + rhs, lhs.z + rhs, lhs.w + rhs); 86 | } 87 | 88 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 89 | public static fp4 operator +(fp lhs, fp4 rhs) { 90 | return new fp4(lhs + rhs.x, lhs + rhs.y, lhs + rhs.z, lhs + rhs.w); 91 | } 92 | 93 | 94 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 95 | public static fp4 operator -(fp4 lhs, fp4 rhs) { 96 | return new fp4(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z, lhs.w - rhs.w); 97 | } 98 | 99 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 100 | public static fp4 operator -(fp4 lhs, fp rhs) { 101 | return new fp4(lhs.x - rhs, lhs.y - rhs, lhs.z - rhs, lhs.w - rhs); 102 | } 103 | 104 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 105 | public static fp4 operator -(fp lhs, fp4 rhs) { 106 | return new fp4(lhs - rhs.x, lhs - rhs.y, lhs - rhs.z, lhs - rhs.w); 107 | } 108 | 109 | 110 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 111 | public static fp4 operator /(fp4 lhs, fp4 rhs) { 112 | return new fp4(lhs.x / rhs.x, lhs.y / rhs.y, lhs.z / rhs.z, lhs.w / rhs.w); 113 | } 114 | 115 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 116 | public static fp4 operator /(fp4 lhs, fp rhs) { 117 | return new fp4(lhs.x / rhs, lhs.y / rhs, lhs.z / rhs, lhs.w / rhs); 118 | } 119 | 120 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 121 | public static fp4 operator /(fp lhs, fp4 rhs) { 122 | return new fp4(lhs / rhs.x, lhs / rhs.y, lhs / rhs.z, lhs / rhs.w); 123 | } 124 | 125 | 126 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 127 | public static fp4 operator %(fp4 lhs, fp4 rhs) { 128 | return new fp4(lhs.x % rhs.x, lhs.y % rhs.y, lhs.z % rhs.z, lhs.w % rhs.w); 129 | } 130 | 131 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 132 | public static fp4 operator %(fp4 lhs, fp rhs) { 133 | return new fp4(lhs.x % rhs, lhs.y % rhs, lhs.z % rhs, lhs.w % rhs); 134 | } 135 | 136 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 137 | public static fp4 operator %(fp lhs, fp4 rhs) { 138 | return new fp4(lhs % rhs.x, lhs % rhs.y, lhs % rhs.z, lhs % rhs.w); 139 | } 140 | 141 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 142 | public static bool operator ==(fp4 a, fp4 b) { 143 | return a.x.value == b.x.value && a.y.value == b.y.value && a.z.value == b.z.value && a.w.value == b.w.value; 144 | } 145 | 146 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 147 | public static bool operator !=(fp4 a, fp4 b) { 148 | return a.x.value != b.x.value || a.y.value != b.y.value || a.z.value != b.z.value || a.w.value != b.w.value; 149 | } 150 | 151 | public bool Equals(fp4 other) { 152 | return x.Equals(other.x) && y.Equals(other.y) && z.Equals(other.z) && w.Equals(other.w); 153 | } 154 | 155 | public override bool Equals(object obj) { 156 | return obj is fp4 other && Equals(other); 157 | } 158 | 159 | public override int GetHashCode() { 160 | unchecked { 161 | var hashCode = x.GetHashCode(); 162 | hashCode = (hashCode * 397) ^ y.GetHashCode(); 163 | hashCode = (hashCode * 397) ^ z.GetHashCode(); 164 | hashCode = (hashCode * 397) ^ w.GetHashCode(); 165 | return hashCode; 166 | } 167 | } 168 | } 169 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Roman Zhuravlev 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LUTGenerator/Data.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace LUTGenerator { 5 | public static class Data { 6 | public const int PRECISION = 16; 7 | public const long ONE = 1 << PRECISION; 8 | 9 | public const int SIN_VALUE_COUNT = 512; 10 | public const int SIN_COS_VALUE_COUNT = 512; 11 | public const int TAN_VALUE_COUNT = 512; 12 | public const int ACOS_VALUE_COUNT = 512; 13 | public const int ASIN_VALUE_COUNT = 512; 14 | 15 | public static readonly List SinLut = new List(SIN_VALUE_COUNT + 1); 16 | public static readonly List SinCosLut = new List(SIN_COS_VALUE_COUNT * 2 + 2); 17 | public static readonly List TanLut = new List(TAN_VALUE_COUNT + 1); 18 | public static readonly List AcosLut = new List(ACOS_VALUE_COUNT + 2); 19 | public static readonly List AsinLut = new List(ASIN_VALUE_COUNT + 2); 20 | 21 | static Data() { 22 | for (var i = 0; i < SIN_COS_VALUE_COUNT; i++) { 23 | var angle = 2 * Math.PI * i / SIN_COS_VALUE_COUNT; 24 | 25 | var sinValue = Math.Sin(angle); 26 | var movedSin = sinValue * ONE; 27 | var roundedSin = movedSin > 0 ? movedSin + 0.5f : movedSin - 0.5f; 28 | SinLut.Add((int) roundedSin); 29 | } 30 | 31 | SinLut.Add(SinLut[0]); 32 | 33 | for (var i = 0; i < SIN_COS_VALUE_COUNT; i++) { 34 | var angle = 2 * Math.PI * i / SIN_COS_VALUE_COUNT; 35 | 36 | var sinValue = Math.Sin(angle); 37 | var movedSin = sinValue * ONE; 38 | var roundedSin = movedSin > 0 ? movedSin + 0.5f : movedSin - 0.5f; 39 | SinCosLut.Add((int) roundedSin); 40 | 41 | var cosValue = Math.Cos(angle); 42 | var movedCos = cosValue * ONE; 43 | var roundedCos = movedCos > 0 ? movedCos + 0.5f : movedCos - 0.5f; 44 | SinCosLut.Add((int) roundedCos); 45 | } 46 | 47 | SinCosLut.Add(SinCosLut[0]); 48 | SinCosLut.Add(SinCosLut[1]); 49 | 50 | for (var i = 0; i < TAN_VALUE_COUNT; i++) { 51 | var angle = 2 * Math.PI * i / TAN_VALUE_COUNT; 52 | 53 | var value = Math.Tan(angle); 54 | var moved = value * ONE; 55 | var rounded = moved > 0 ? moved + 0.5f : moved - 0.5f; 56 | TanLut.Add((int) rounded); 57 | } 58 | 59 | TanLut.Add(TanLut[0]); 60 | 61 | for (var i = 0; i < ASIN_VALUE_COUNT; i++) { 62 | var angle = 2f * i / ASIN_VALUE_COUNT; 63 | angle -= 1; 64 | 65 | if (i == ASIN_VALUE_COUNT - 1) 66 | angle = 1; 67 | 68 | var value = Math.Asin(angle); 69 | var moved = value * ONE; 70 | var rounded = moved > 0 ? moved + 0.5f : moved - 0.5f; 71 | AsinLut.Add((int) rounded); 72 | } 73 | 74 | //Special handling for value of 1, as graph is not symmetric 75 | AsinLut.Add(AsinLut[ASIN_VALUE_COUNT - 1]); 76 | AsinLut.Add(AsinLut[ASIN_VALUE_COUNT - 1]); 77 | 78 | for (var i = 0; i < ACOS_VALUE_COUNT; i++) { 79 | var angle = 2f * i / ACOS_VALUE_COUNT; 80 | angle -= 1; 81 | 82 | if (i == ACOS_VALUE_COUNT - 1) 83 | angle = 1; 84 | 85 | var value = Math.Acos(angle); 86 | var moved = value * ONE; 87 | var rounded = moved > 0 ? moved + 0.5f : moved - 0.5f; 88 | AcosLut.Add((int) rounded); 89 | } 90 | 91 | //Special handling for value of 1, as graph is not symmetric 92 | AcosLut.Add(AcosLut[ACOS_VALUE_COUNT - 1]); 93 | AcosLut.Add(AcosLut[ACOS_VALUE_COUNT - 1]); 94 | } 95 | } 96 | } -------------------------------------------------------------------------------- /LUTGenerator/Generator.cs: -------------------------------------------------------------------------------- 1 | namespace LUTGenerator { 2 | internal class Generator { 3 | public static void Main(string[] args) { 4 | Writer.Write(Data.SinLut, "SinLut", "Sin"); 5 | Writer.Write(Data.SinCosLut, "SinCosLut", "Sin"); 6 | Writer.Write(Data.TanLut, "TanLut", "Sin"); 7 | Writer.Write(Data.AcosLut, "AcosLut", "Sin"); 8 | Writer.Write(Data.AsinLut, "AsinLut", "Sin"); 9 | } 10 | } 11 | } -------------------------------------------------------------------------------- /LUTGenerator/LUTGenerator.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {49D328B2-A15B-42A7-9C16-B04E160FE7B3} 8 | Exe 9 | Properties 10 | LUTGenerator 11 | LUTGenerator 12 | v4.7.1 13 | 512 14 | 15 | 16 | AnyCPU 17 | true 18 | full 19 | false 20 | bin\Debug\ 21 | DEBUG;TRACE 22 | prompt 23 | 4 24 | 25 | 26 | AnyCPU 27 | pdbonly 28 | true 29 | bin\Release\ 30 | TRACE 31 | prompt 32 | 4 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 54 | -------------------------------------------------------------------------------- /LUTGenerator/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.InteropServices; 3 | 4 | // General Information about an assembly is controlled through the following 5 | // set of attributes. Change these attribute values to modify the information 6 | // associated with an assembly. 7 | [assembly: AssemblyTitle("LUTGenerator")] 8 | [assembly: AssemblyDescription("")] 9 | [assembly: AssemblyConfiguration("")] 10 | [assembly: AssemblyCompany("")] 11 | [assembly: AssemblyProduct("LUTGenerator")] 12 | [assembly: AssemblyCopyright("Copyright © 2020")] 13 | [assembly: AssemblyTrademark("")] 14 | [assembly: AssemblyCulture("")] 15 | 16 | // Setting ComVisible to false makes the types in this assembly not visible 17 | // to COM components. If you need to access a type in this assembly from 18 | // COM, set the ComVisible attribute to true on that type. 19 | [assembly: ComVisible(false)] 20 | 21 | // The following GUID is for the ID of the typelib if this project is exposed to COM 22 | [assembly: Guid("49D328B2-A15B-42A7-9C16-B04E160FE7B3")] 23 | 24 | // Version information for an assembly consists of the following four values: 25 | // 26 | // Major Version 27 | // Minor Version 28 | // Build Number 29 | // Revision 30 | // 31 | // You can specify all the values or you can default the Build and Revision Numbers 32 | // by using the '*' as shown below: 33 | // [assembly: AssemblyVersion("1.0.*")] 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /LUTGenerator/Writer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | 6 | namespace LUTGenerator { 7 | public static class Writer { 8 | public const string HEADER = "namespace FixedPoint {\n public static partial class fixlut {\n public static readonly"; 9 | public const string FOOTER = "\n };\n }\n}"; 10 | public const int ENTRIES_PER_LINE = 10; 11 | 12 | public static string Output = string.Empty; 13 | public static int EntryIndex = 0; 14 | public static int MaxEntryIndex; 15 | 16 | private static readonly Dictionary _friendlyNames = new Dictionary { 17 | {typeof(int), "int"}, 18 | {typeof(uint), "uint"}, 19 | {typeof(long), "long"}, 20 | {typeof(ulong), "ulong"}, 21 | {typeof(short), "short"}, 22 | {typeof(ushort), "ushort"}, 23 | {typeof(byte), "byte"}, 24 | {typeof(sbyte), "sbyte"}, 25 | }; 26 | 27 | public static void Write(IList list, string fieldName, string fileName) { 28 | EntryIndex = 0; 29 | MaxEntryIndex = list.Count - 1; 30 | Output = $"{HEADER} {_friendlyNames[list[0].GetType()]}[] {fieldName} = {{"; 31 | 32 | foreach (var i in list) { 33 | AddEntry(i.ToString()); 34 | } 35 | 36 | Output += FOOTER; 37 | 38 | File.WriteAllText($"{Directory.GetCurrentDirectory()}/{fieldName}.cs", Output); 39 | } 40 | 41 | public static void AddEntry(string entry) { 42 | if (EntryIndex % ENTRIES_PER_LINE == 0) 43 | Output += "\n "; 44 | else 45 | Output += " "; 46 | 47 | Output += entry; 48 | 49 | if (EntryIndex != MaxEntryIndex) 50 | Output += ","; 51 | 52 | EntryIndex++; 53 | } 54 | } 55 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FixedPoint-Sharp 2 | 3 | ## Features 4 | - LUT-based trigonometry with lerping 5 | - 48.16 precision 6 | - 2D, 3D, 4D vector types 7 | - 1D, 2D, 3D, 4D math 8 | - Random generator for fp, int, bool, and vector types 9 | - A huge pile of tests 10 | -------------------------------------------------------------------------------- /UnitTests/FP_Tests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using Deterministic.FixedPoint; 4 | using FluentAssertions; 5 | using NUnit.Framework; 6 | 7 | namespace UnitTests 8 | { 9 | public class FP_Tests { 10 | private const string PRECISION_FORMAT = "F4"; 11 | private const int ROUNDING = 5; 12 | 13 | [Test] 14 | public void ToStringTest() 15 | { 16 | var originalFp = fp._1 - fp._0_01; 17 | Math.Round(originalFp.AsDouble, ROUNDING).ToString(PRECISION_FORMAT, CultureInfo.InvariantCulture).Should().Be("0.9900"); 18 | 19 | originalFp = fp._1 - fp._0_01 *fp._0_01; 20 | Math.Round(originalFp.AsDouble, ROUNDING).ToString(PRECISION_FORMAT, CultureInfo.InvariantCulture).Should().Be("0.9999"); 21 | 22 | originalFp = fp._1; 23 | Math.Round(originalFp.AsDouble, ROUNDING).ToString(PRECISION_FORMAT, CultureInfo.InvariantCulture).Should().Be("1.0000"); 24 | 25 | originalFp = fp._1 + fp._0_01; 26 | Math.Round(originalFp.AsDouble, ROUNDING).ToString(PRECISION_FORMAT, CultureInfo.InvariantCulture).Should().Be("1.0100"); 27 | 28 | originalFp = fp._1 + fp._0_01 * fp._0_01; 29 | Math.Round(originalFp.AsDouble, ROUNDING).ToString(PRECISION_FORMAT, CultureInfo.InvariantCulture).Should().Be("1.0001"); 30 | 31 | originalFp = fp._0_01; 32 | Math.Round(originalFp.AsDouble, ROUNDING).ToString(PRECISION_FORMAT, CultureInfo.InvariantCulture).Should().Be("0.0100"); 33 | 34 | originalFp = fp._0_50; 35 | Math.Round(originalFp.AsDouble, ROUNDING).ToString(PRECISION_FORMAT, CultureInfo.InvariantCulture).Should().Be("0.5000"); 36 | } 37 | 38 | [Test] 39 | public void SlowStringParsingTest() { 40 | fp.Parse("5").AsFloatRounded.Should().BeApproximately(5, 0.0001f); 41 | fp.Parse("5.").AsFloatRounded.Should().BeApproximately(5, 0.0001f); 42 | fp.Parse(".1").AsFloatRounded.Should().BeApproximately(0.1f, 0.0001f); 43 | fp.Parse("5.1").AsFloatRounded.Should().BeApproximately(5.1f, 0.0001f); 44 | fp.Parse("5.45111111").AsFloatRounded.Should().BeApproximately(5.4511f, 0.0001f); 45 | 46 | fp.Parse("-5").AsFloatRounded.Should().BeApproximately(-5, 0.0001f); 47 | fp.Parse("-5.").AsFloatRounded.Should().BeApproximately(-5, 0.0001f); 48 | fp.Parse("-.1").AsFloatRounded.Should().BeApproximately(-0.1f, 0.0001f); 49 | fp.Parse("-5.1").AsFloatRounded.Should().BeApproximately(-5.1f, 0.0001f); 50 | } 51 | 52 | [Test] 53 | public void SlowFromToStringTest() { 54 | var from = -100.0; 55 | var to = 100.0; 56 | var delta = 0.0001; 57 | 58 | for (var v = from; v < to; v += delta) { 59 | var parsedString = Math.Round(v, ROUNDING).ToString(PRECISION_FORMAT, CultureInfo.InvariantCulture); 60 | var parsedFp = fp.Parse(parsedString); 61 | var convertedBackFloat = parsedFp.AsDouble; 62 | convertedBackFloat.Should().BeApproximately(v, 0.0001); 63 | } 64 | } 65 | 66 | [Test] 67 | public void FromToStringTest() { 68 | var from = -100.0; 69 | var to = 100.0; 70 | var delta = 0.0001; 71 | 72 | for (var v = from; v < to; v += delta) { 73 | var parsedString = Math.Round(v, ROUNDING).ToString(PRECISION_FORMAT, CultureInfo.InvariantCulture); 74 | var parsedFp = fp.ParseUnsafe(parsedString); 75 | var convertedBackFloat = parsedFp.AsDouble; 76 | convertedBackFloat.Should().BeApproximately(v, 0.0001); 77 | } 78 | } 79 | 80 | [Test] 81 | public void FromFloatTest() { 82 | var from = -100.0; 83 | var to = 100.0; 84 | var delta = 0.0001; 85 | 86 | for (var v = from; v < to; v += delta) { 87 | var parsedFp = fp.ParseUnsafe((float)v); 88 | var convertedBackFloat = parsedFp.AsDouble; 89 | convertedBackFloat.Should().BeApproximately(v, 0.0001); 90 | } 91 | } 92 | 93 | [Test] 94 | public void AsIntTest() { 95 | var from = -65000f; 96 | var to = 65000; 97 | var delta = 0.1f; 98 | 99 | for (float v = from; v < to; v += delta) { 100 | var originalInt = (int) Math.Floor(v); 101 | var parsedFp = fp.ParseUnsafe(v); 102 | var convertedBack = parsedFp.AsInt; 103 | convertedBack.Should().Be(originalInt); 104 | } 105 | } 106 | } 107 | } -------------------------------------------------------------------------------- /UnitTests/FP_fixmath3Tests.cs: -------------------------------------------------------------------------------- 1 | using Deterministic.FixedPoint; 2 | using FluentAssertions; 3 | using NUnit.Framework; 4 | 5 | namespace UnitTests 6 | { 7 | public class FP_fixmath3Tests 8 | { 9 | [Test] 10 | public void NormalizationTest() 11 | { 12 | var originalVector = new fp3(fp._5, fp._0, fp._0); 13 | var modifiedVector = fixmath.Normalize(originalVector); 14 | 15 | modifiedVector.Should().Be(new fp3(fp._1, fp._0, fp._0)); 16 | } 17 | 18 | [Test] 19 | public void MagnitudeTest() 20 | { 21 | var originalVector = new fp3(fp._5, fp._0, fp._0); 22 | var magnitude = fixmath.Magnitude(originalVector); 23 | 24 | magnitude.Should().Be(fp._5); 25 | } 26 | 27 | [Test] 28 | public void MagnitudeSqrTest() 29 | { 30 | var originalVector = new fp3(fp._5, fp._0, fp._0); 31 | var magnitude = fixmath.MagnitudeSqr(originalVector); 32 | 33 | magnitude.Should().Be(fp._5 * fp._5); 34 | } 35 | 36 | [Test] 37 | public void MagnitudeClampTest() 38 | { 39 | var originalVector = new fp3(fp._5, fp._0, fp._0); 40 | var clampedVector = fixmath.MagnitudeClamp(originalVector, fp._1_10); 41 | 42 | clampedVector.x.AsFloat.Should().BeApproximately(1.10f, 0.01f); 43 | clampedVector.y.AsFloat.Should().Be(0f); 44 | clampedVector.z.AsFloat.Should().Be(0f); 45 | } 46 | 47 | [Test] 48 | public void DotTest() 49 | { 50 | var vector1 = new fp3(fp._5, fp._0, fp._0); 51 | var vector2 = new fp3(fp._5, fp._0, fp._0); 52 | var dot = fixmath.Dot(vector1, vector2); 53 | 54 | dot.Should().Be(fp._5 * fp._5); 55 | 56 | vector1 = new fp3(fp._1, fp._5, fp._4); 57 | vector2 = new fp3(fp._2, fp._0, fp._1); 58 | dot = fixmath.Dot(vector1, vector2); 59 | 60 | dot.Should().Be(fp._6); 61 | 62 | vector1 = new fp3(fp._0_10, fp._0_75, fp._0_10); 63 | vector2 = new fp3(fp._0_50+fp._0_10, fp._0_20, fp._0_33); 64 | dot = fixmath.Dot(vector1, vector2); 65 | 66 | var str = $"{vector1.x.AsFloat},{vector1.y.value},{vector1.z.value} | {vector2} {dot}"; 67 | 68 | dot.AsFloat.Should().BeApproximately(0.243f, 0.01f); 69 | } 70 | 71 | [Test] 72 | public void AngleTest() 73 | { 74 | var vector1 = new fp3(fp._1, fp._5, fp._4); 75 | var vector2 = new fp3(fp._2, fp._0, fp._1); 76 | var angle = fixmath.Angle(vector1, vector2); 77 | 78 | angle.AsInt.Should().Be(65); 79 | 80 | vector1 = new fp3(fp._2, fp._1, fp._1); 81 | vector2 = new fp3(fp._2, fp._0, fp._1); 82 | angle = fixmath.Angle(vector1, vector2); 83 | 84 | angle.AsInt.Should().Be(24); 85 | } 86 | 87 | [Test] 88 | public void AngleSignedTest() 89 | { 90 | var vector1 = new fp3(fp._1, fp._5, fp._4); 91 | var vector2 = new fp3(fp._2, fp._0, fp._1); 92 | var angle = fixmath.AngleSigned(vector1, vector2, fp3.up); 93 | 94 | angle.AsInt.Should().Be(65); 95 | 96 | vector1 = new fp3(-fp._2, fp._1, fp._1); 97 | vector2 = new fp3(fp._2, fp._1, fp._1); 98 | angle = fixmath.AngleSigned(vector1, vector2, fp3.up); 99 | 100 | angle.AsFloat.Should().BeApproximately(109.47f, 0.1f); 101 | } 102 | 103 | [Test] 104 | public void RadiansTest() 105 | { 106 | var vector1 = new fp3(fp._1, fp._5, fp._4); 107 | var vector2 = new fp3(fp._2, fp._0, fp._1); 108 | var angle = fixmath.Radians(vector1, vector2); 109 | 110 | angle.AsInt.Should().Be(1); 111 | 112 | vector1 = new fp3(fp._2, fp._1, fp._1); 113 | vector2 = new fp3(fp._2, fp._0, fp._1); 114 | angle = fixmath.Radians(vector1, vector2); 115 | 116 | angle.AsFloat.Should().BeApproximately(0.42f, 0.01f); 117 | } 118 | 119 | [Test] 120 | public void CrossTest() 121 | { 122 | var vector1 = new fp3(fp._1, fp._5, fp._4); 123 | var vector2 = new fp3(fp._2, fp._0, fp._1); 124 | var cross = fixmath.Cross(vector1, vector2); 125 | 126 | cross.Should().Be(new fp3(fp._5, fp._7, -fp._10)); 127 | } 128 | 129 | [Test] 130 | public void ReflectTest() 131 | { 132 | var vector = new fp3(fp._5, fp._0, fp._5); 133 | var normal = new fp3(-fp._1, fp._0, fp._0); 134 | var reflection = fixmath.Reflect(vector, normal); 135 | 136 | reflection.Should().Be(new fp3(-fp._5, fp._0, fp._5)); 137 | } 138 | 139 | [Test] 140 | public void ProjectTest() 141 | { 142 | var vector = new fp3(fp._5, fp._0, fp._5); 143 | var normal = new fp3(-fp._1, fp._0, fp._0); 144 | var projection = fixmath.Project(vector, normal); 145 | 146 | projection.Should().Be(new fp3(fp._5, fp._0, fp._0)); 147 | } 148 | 149 | [Test] 150 | public void ProjectOnPlaneTest() 151 | { 152 | var vector = new fp3(fp._5, fp._1, fp._5); 153 | var normal = new fp3(-fp._1, fp._0, fp._0); 154 | var projection = fixmath.ProjectOnPlane(vector, normal); 155 | 156 | projection.Should().Be(new fp3(fp._0, fp._1, fp._5)); 157 | } 158 | 159 | [Test] 160 | public void LerpTest() 161 | { 162 | var @from = new fp3(fp._5, fp._0, fp._5); 163 | var to = new fp3(fp._0, fp._0, fp._0); 164 | var lerped = fixmath.Lerp(@from, to, fp._0_50); 165 | 166 | lerped.Should().Be(new fp3(fp._2+fp._0_50, fp._0, fp._2 +fp._0_50)); 167 | } 168 | 169 | [Test] 170 | public void MoveTowardsTest() 171 | { 172 | var current = fp3.one; 173 | var target = new fp3(fp._5, fp._1, fp._1); 174 | 175 | var step1 = fixmath.MoveTowards(current, target, fp._1); 176 | step1.Should().Be(new fp3(fp._2, fp._1, fp._1)); 177 | 178 | var step2 = fixmath.MoveTowards(current, target, fp._10); 179 | step2.Should().Be(new fp3(fp._5, fp._1, fp._1)); 180 | } 181 | } 182 | } -------------------------------------------------------------------------------- /UnitTests/FP_fixmathTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Deterministic; 3 | using Deterministic.FixedPoint; 4 | using FluentAssertions; 5 | using NUnit.Framework; 6 | 7 | namespace UnitTests { 8 | public class FP_fixmathTests { 9 | 10 | [Test] 11 | public void CountLeadingZerosTest() { 12 | fixmath.CountLeadingZeroes(5435345).Should().Be(9); 13 | fixmath.CountLeadingZeroes(4).Should().Be(29); 14 | } 15 | 16 | [Test] 17 | public void ExpTest() { 18 | var from = -5f; 19 | var to = 5f; 20 | var delta = 0.001f; 21 | 22 | for (float v = from; v < to; v += delta) { 23 | var correctAnswer = (float)Math.Exp(v); 24 | var parsedFp = fp.ParseUnsafe(v); 25 | var answer = fixmath.Exp(parsedFp); 26 | answer.AsFloat.Should().BeApproximately(correctAnswer, 0.01f); 27 | } 28 | 29 | from = 5f; 30 | to = 5.33f; 31 | delta = 0.001f; 32 | 33 | for (float v = from; v < to; v += delta) { 34 | var correctAnswer = (float)Math.Exp(v); 35 | var parsedFp = fp.ParseUnsafe(v); 36 | var answer = fixmath.Exp(parsedFp); 37 | answer.AsFloat.Should().BeApproximately(correctAnswer, 1f); 38 | } 39 | } 40 | 41 | [Test] 42 | public void Atan_2Test() { 43 | var from = -1f; 44 | var to = 1f; 45 | var delta = 0.001f; 46 | 47 | for (float v = from; v < to; v += delta) { 48 | var correctAnswer = (float)Math.Atan(v); 49 | var parsedFp = fp.ParseUnsafe(v); 50 | var answer = fixmath.AtanApproximated(parsedFp); 51 | answer.AsFloat.Should().BeApproximately(correctAnswer, 0.01f); 52 | } 53 | } 54 | 55 | [Test] 56 | public void AtanTest() { 57 | var from = -1f; 58 | var to = 1f; 59 | var delta = 0.001f; 60 | 61 | for (float v = from; v < to; v += delta) { 62 | var correctAnswer = (float)Math.Atan(v); 63 | var parsedFp = fp.ParseUnsafe(v); 64 | var answer = fixmath.Atan(parsedFp); 65 | answer.AsFloat.Should().BeApproximately(correctAnswer, 0.001f); 66 | } 67 | } 68 | 69 | [Test] 70 | public void Atan2Test() { 71 | var from1 = 0.1f; 72 | var to1 = 10f; 73 | var from2 = 0.1f; 74 | var to2 = 10f; 75 | var delta = 0.01f; 76 | 77 | for (float x = from1; x < to1; x += delta) { 78 | for (float y = from2; y < to2; y += delta) { 79 | var correctAnswer = (float) Math.Atan2(x, y); 80 | var parsedFp1 = fp.ParseUnsafe(x); 81 | var parsedFp2 = fp.ParseUnsafe(y); 82 | var answer = fixmath.Atan2(parsedFp1, parsedFp2); 83 | answer.AsFloat.Should().BeApproximately(correctAnswer, 0.01f); 84 | } 85 | } 86 | } 87 | 88 | [Test] 89 | public void TanTest() { 90 | for (long i = fp.minus_one.value; i <= fp._1.value; i++) { 91 | fp val; 92 | val.value = i; 93 | var dValue = val.AsDouble; 94 | var answer = fixmath.Tan(val); 95 | answer.AsDouble.Should().BeApproximately(Math.Tan(dValue), 0.001d); 96 | } 97 | } 98 | 99 | [Test] 100 | public void AcosTest() { 101 | for (long i = fp.minus_one.value; i <= fp._1.value; i++) { 102 | fp val; 103 | val.value = i; 104 | var dValue = val.AsDouble; 105 | var answer = fixmath.Acos(val); 106 | answer.AsDouble.Should().BeApproximately(Math.Acos(dValue), 0.0001d); 107 | } 108 | } 109 | 110 | [Test] 111 | public void AsinTest() { 112 | for (long i = fp.minus_one.value; i <= fp._1.value; i++) { 113 | fp val; 114 | val.value = i; 115 | var dValue = val.AsDouble; 116 | var answer = fixmath.Asin(val); 117 | answer.AsDouble.Should().BeApproximately(Math.Asin(dValue), 0.0001d); 118 | } 119 | } 120 | 121 | [Test] 122 | public void CosTest() { 123 | for (long i = -fp.pi.value*2; i <= fp.pi.value*2; i++) { 124 | fp val; 125 | val.value = i; 126 | var dValue = val.AsDouble; 127 | var answer = fixmath.Cos(val); 128 | answer.AsDouble.Should().BeApproximately(Math.Cos(dValue), 0.001d); 129 | } 130 | } 131 | 132 | [Test] 133 | public void SinTest() { 134 | for (long i = -fp.pi.value*2; i <= fp.pi.value*2; i++) { 135 | fp val; 136 | val.value = i; 137 | var dValue = val.AsDouble; 138 | var answer = fixmath.Sin(val); 139 | answer.AsDouble.Should().BeApproximately(Math.Sin(dValue), 0.001d); 140 | } 141 | } 142 | 143 | [Test] 144 | public void SinCosTest() { 145 | for (long i = -fp.pi.value*2; i <= fp.pi.value*2; i++) { 146 | fp val; 147 | val.value = i; 148 | var dValue = val.AsDouble; 149 | 150 | fixmath.SinCos(val, out var sin, out var cos); 151 | sin.AsDouble.Should().BeApproximately(Math.Sin(dValue), 0.001d); 152 | cos.AsDouble.Should().BeApproximately(Math.Cos(dValue), 0.001d); 153 | } 154 | } 155 | 156 | [Test] 157 | public void RcpTest() { 158 | var value = fp._0_25; 159 | var result = fixmath.Rcp(value); 160 | result.Should().Be(fp._4); 161 | 162 | value = fp._4; 163 | result = fixmath.Rcp(value); 164 | result.Should().Be(fp._0_25); 165 | } 166 | 167 | [Test] 168 | public void SqrtTest() { 169 | var from = 0.1f; 170 | var to = 65000; 171 | var delta = 0.1f; 172 | 173 | for (float v = from; v < to; v += delta) { 174 | var correct = (float)Math.Sqrt(v); 175 | var parsedFp = fp.ParseUnsafe(v); 176 | var answer = fixmath.Sqrt(parsedFp); 177 | answer.AsFloat.Should().BeApproximately(correct, 0.01f); 178 | } 179 | } 180 | 181 | [Test] 182 | public void FloorTest() { 183 | var value = fp._0_25; 184 | var result = fixmath.Floor(value); 185 | result.Should().Be(fp._0); 186 | 187 | result = fixmath.Floor(-value); 188 | result.Should().Be(-fp._1); 189 | } 190 | 191 | [Test] 192 | public void CeilTest() { 193 | var value = fp._0_25; 194 | var result = fixmath.Ceil(value); 195 | result.Should().Be(fp._1); 196 | 197 | result = fixmath.Ceil(-fp._4 - fp._0_25); 198 | result.Should().Be(-fp._4); 199 | } 200 | 201 | [Test] 202 | public void RoundToIntTest() { 203 | var value = fp._5 + fp._0_25; 204 | var result = fixmath.RoundToInt(value); 205 | result.Should().Be(5); 206 | 207 | result = fixmath.RoundToInt(value + fp._0_33); 208 | result.Should().Be(6); 209 | 210 | result = fixmath.RoundToInt(value + fp._0_25); 211 | result.Should().Be(6); 212 | } 213 | 214 | [Test] 215 | public void MinTest() { 216 | var value1 = fp._0_25; 217 | var value2 = fp._0_33; 218 | var result = fixmath.Min(value1, value2); 219 | result.Should().Be(value1); 220 | 221 | result = fixmath.Min(-value1, -value2); 222 | result.Should().Be(-value2); 223 | } 224 | 225 | [Test] 226 | public void MaxTest() { 227 | var value1 = fp._0_25; 228 | var value2 = fp._0_33; 229 | var result = fixmath.Max(value1, value2); 230 | result.Should().Be(value2); 231 | 232 | result = fixmath.Max(-value1, -value2); 233 | result.Should().Be(-value1); 234 | } 235 | 236 | [Test] 237 | public void AbsTest() { 238 | var from = -5; 239 | var to = 5; 240 | var delta = 0.1f; 241 | 242 | for (float v = from; v < to; v += delta) { 243 | var correctAnswer = Math.Abs(v); 244 | var parsedFp = fp.ParseUnsafe(v); 245 | var answer = fixmath.Abs(parsedFp); 246 | 247 | answer.AsFloat.Should().BeApproximately(correctAnswer, 0.1f); 248 | } 249 | } 250 | 251 | [Test] 252 | public void ClampTest() { 253 | var from = -5; 254 | var to = 5; 255 | var delta = 0.1f; 256 | 257 | for (float v = from; v < to; v += delta) { 258 | var correctAnswer = Math.Clamp(v, -3, 3); 259 | var parsedFp = fp.ParseUnsafe(v); 260 | var answer = fixmath.Clamp(parsedFp, -fp._3, fp._3); 261 | answer.AsFloat.Should().BeApproximately(correctAnswer, 0.1f); 262 | } 263 | } 264 | 265 | 266 | [Test] 267 | public void LerpTest() { 268 | var result = fixmath.Lerp(fp._2, fp._4, fp._0_25); 269 | result.Should().Be(fp._2 + fp._0_50); 270 | 271 | result = fixmath.Lerp(fp._2, fp._4, fp._0); 272 | result.Should().Be(fp._2); 273 | 274 | result = fixmath.Lerp(fp._2, fp._4, fp._1); 275 | result.Should().Be(fp._4); 276 | 277 | result = fixmath.Lerp(fp._2, fp._4, fp._0_50); 278 | result.Should().Be(fp._3); 279 | } 280 | 281 | [Test] 282 | public void MulTest() { 283 | fp a = 5; 284 | 285 | var result1 = a * fp._0_01; 286 | result1.AsFloat.Should().BeApproximately(0.05f, 0.01f); 287 | 288 | var result2 = fp._0_01 * a; 289 | result2.AsFloat.Should().BeApproximately(0.05f, 0.01f); 290 | 291 | var result3 = fp._0_01 * fp._0_01; 292 | result3.AsFloat.Should().BeApproximately(0.001f, 0.002f); 293 | } 294 | 295 | [Test] 296 | public void SignTest() { 297 | var from = -5; 298 | var to = 5; 299 | var delta = 0.12f; 300 | 301 | for (float v = from; v < to; v += delta) { 302 | var correctAnswer = Math.Sign(v); 303 | var parsedFp = fp.ParseUnsafe(v); 304 | var answer = fixmath.Sign(parsedFp); 305 | answer.AsFloat.Should().BeApproximately(correctAnswer, 0.1f); 306 | } 307 | } 308 | 309 | [Test] 310 | public void IsOppositeSignTest() { 311 | var result = fixmath.IsOppositeSign(fp._0_25, -fp._0_20); 312 | result.Should().Be(true); 313 | 314 | result = fixmath.IsOppositeSign(fp._0_25, fp._0_20); 315 | result.Should().Be(false); 316 | 317 | result = fixmath.IsOppositeSign(-fp._0_25, -fp._0_20); 318 | result.Should().Be(false); 319 | } 320 | } 321 | } -------------------------------------------------------------------------------- /UnitTests/FP_randomTests.cs: -------------------------------------------------------------------------------- 1 | using Deterministic.FixedPoint; 2 | using FluentAssertions; 3 | using NUnit.Framework; 4 | 5 | namespace UnitTests { 6 | public class FP_randomTests { 7 | [Test] 8 | public void BoolTest() 9 | { 10 | var random = new Random(645); 11 | random.NextBool().Should().Be(true); 12 | 13 | random.SetState(435); 14 | random.NextBool().Should().Be(false); 15 | } 16 | 17 | [Test] 18 | public void IntTest() 19 | { 20 | var random = new Random(645); 21 | random.NextInt().Should().Be(-1975191795); 22 | 23 | random.SetState(435); 24 | random.NextInt().Should().Be(-2030414680); 25 | } 26 | 27 | [Test] 28 | public void IntMaxTest() 29 | { 30 | var random = new Random(345345346); 31 | for (uint i = 5; i < 100; i++) { 32 | random.NextInt(30).Should().BeLessThan(31); 33 | } 34 | } 35 | 36 | [Test] 37 | public void IntMinMaxTest() 38 | { 39 | var random = new Random(345345346); 40 | for (var i = 0; i < 100; i++) { 41 | random.NextInt(-30, 30).Should().BeInRange(-30, 30); 42 | } 43 | } 44 | 45 | [Test] 46 | public void FpTest() 47 | { 48 | var random = new Random(645); 49 | random.NextFp().value.Should().Be(2628L); 50 | 51 | random.SetState(435); 52 | random.NextFp().value.Should().Be(1786L); 53 | } 54 | 55 | [Test] 56 | public void FpMaxTest() 57 | { 58 | var random = new Random(345345346); 59 | for (uint i = 5; i < 100; i++) { 60 | var val = random.NextFp(fp._100); 61 | val.Should().BeLessThan(fp._100); 62 | } 63 | } 64 | 65 | [Test] 66 | public void FpMinMaxTest() 67 | { 68 | var random = new Random(345345346); 69 | for (uint i = 5; i < 100; i++) { 70 | var val = random.NextFp(fp._99, fp._100); 71 | val.Should().BeInRange(fp._99, fp._100); 72 | } 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /UnitTests/UnitTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Library 5 | netcoreapp3.1 6 | FPTesting 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /UnitTests/plotting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Drawing; 4 | using Deterministic.FixedPoint; 5 | using NUnit.Framework; 6 | 7 | namespace UnitTests { 8 | public class plotting { 9 | [Test] 10 | public void Atan_Plottting() { 11 | var tans = new List(); 12 | var correctValue = new List(); 13 | var approxValue1 = new List(); 14 | var approxValue2 = new List(); 15 | var approxValueError1 = new List(); 16 | var approxValueError2 = new List(); 17 | for (var i = 0; i < 2000; i++) { 18 | var tan = (2 * i / 2000f) - 1; 19 | tans.Add(tan); 20 | } 21 | 22 | foreach (var tan in tans) { 23 | var correctTan = Math.Atan(tan); 24 | correctValue.Add(correctTan); 25 | var aprox1Tan = fixmath.Atan(fp.ParseUnsafe((float) tan)).AsDouble; 26 | approxValue1.Add(aprox1Tan); 27 | approxValueError1.Add(correctTan - aprox1Tan); 28 | var aprox2Tan = fixmath.AtanApproximated(fp.ParseUnsafe((float) tan)).AsDouble; 29 | approxValue2.Add(aprox2Tan); 30 | approxValueError2.Add(correctTan - aprox2Tan); 31 | } 32 | 33 | var plt = new ScottPlot.Plot(2048, 2048); 34 | plt.PlotScatter(tans.ToArray(), correctValue.ToArray(), Color.CadetBlue, 0.01f, 1, "Math.Atan"); 35 | plt.PlotScatter(tans.ToArray(), approxValue1.ToArray(), Color.Firebrick, 0.01f, 1f, "fixmath.Atan"); 36 | plt.PlotScatter(tans.ToArray(), approxValue2.ToArray(), Color.FromArgb(74, 178, 67), 0.01f, 1, "fixmath.Atan_2"); 37 | plt.AxisAuto(); 38 | plt.SaveFig("Atan.png"); 39 | 40 | plt = new ScottPlot.Plot(2048, 2048); 41 | plt.PlotScatter(tans.ToArray(), approxValueError1.ToArray(), Color.Firebrick, 0.01f, 1f, "fixmath.Atan"); 42 | plt.PlotScatter(tans.ToArray(), approxValueError2.ToArray(), Color.FromArgb(74, 178, 67), 0.01f, 1, "fixmath.Atan_2"); 43 | plt.AxisAuto(); 44 | plt.SaveFig("Atan_error.png"); 45 | } 46 | 47 | [Test] 48 | public void Atan2_Plottting() { 49 | var xValues = new List(); 50 | var correctValue = new List(); 51 | var approxValue1 = new List(); 52 | var approxValueError1 = new List(); 53 | for (var i = 0; i < 2000; i++) { 54 | var tan = (2 * i / 2000f) - 1; 55 | xValues.Add(tan); 56 | } 57 | 58 | foreach (var tan in xValues) { 59 | var correctTan = Math.Atan2(tan, 1); 60 | correctValue.Add(correctTan); 61 | var aprox1Tan = fixmath.Atan2(fp.ParseUnsafe((float) tan), fp._1).AsDouble; 62 | approxValue1.Add(aprox1Tan); 63 | approxValueError1.Add(correctTan - aprox1Tan); 64 | } 65 | 66 | var plt = new ScottPlot.Plot(2048, 2048); 67 | plt.PlotScatter(xValues.ToArray(), correctValue.ToArray(), Color.CadetBlue, 0.01f, 1, "Math.Atan2"); 68 | plt.PlotScatter(xValues.ToArray(), approxValue1.ToArray(), Color.Firebrick, 0.01f, 1f, "fixmath.Atan2"); 69 | plt.AxisAuto(); 70 | plt.SaveFig("Atan2.png"); 71 | 72 | plt = new ScottPlot.Plot(2048, 2048); 73 | plt.PlotScatter(xValues.ToArray(), approxValueError1.ToArray(), Color.Firebrick, 0.01f, 1f, "fixmath.Atan2"); 74 | plt.AxisAuto(); 75 | plt.SaveFig("Atan2_error.png"); 76 | } 77 | } 78 | } --------------------------------------------------------------------------------