|
1 | 1 |
|
2 | 2 | using System.Diagnostics; |
| 3 | +using System.Numerics; |
3 | 4 | using System.Runtime.CompilerServices; |
4 | 5 | using System.Runtime.Intrinsics; |
5 | 6 |
|
@@ -127,12 +128,75 @@ private double InternalPseudoHashSeed(int keyLength) |
127 | 128 | return num; |
128 | 129 | } |
129 | 130 |
|
| 131 | +#if !DEBUG |
| 132 | + [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| 133 | +#endif |
| 134 | + private static double Fract(double x) |
| 135 | + { |
| 136 | + ref ulong xInt = ref Unsafe.As<double, ulong>(ref x); |
| 137 | + |
| 138 | + const ulong DblExpo = 0x7FF0000000000000; |
| 139 | + const ulong DblMant = 0x000FFFFFFFFFFFFF; |
| 140 | + |
| 141 | + const int DblMantSZ = 52; |
| 142 | + |
| 143 | + const int DblExpoBias = 1023; |
| 144 | + |
| 145 | + ulong expo = (xInt & DblExpo) >> DblMantSZ; |
| 146 | + |
| 147 | + if (expo < DblExpoBias) return x; |
| 148 | + |
| 149 | + // We don't have to worry about this edge case |
| 150 | + |
| 151 | + // const int DblExpoSZ = 11; |
| 152 | + // if (expo == ((1 << DblExpoSZ) - 1)) return double.NaN; |
| 153 | + |
| 154 | + ulong expoBiased = expo - DblExpoBias; |
| 155 | + |
| 156 | + if (expoBiased > DblMantSZ) return 0; |
| 157 | + |
| 158 | + ulong mant = xInt & DblMant; |
| 159 | + ulong fractMant = mant & ((1ul << (int)(DblMantSZ - expoBiased)) - 1); |
| 160 | + |
| 161 | + if (fractMant == 0) return 0; |
| 162 | + |
| 163 | + int fractLzcnt = BitOperations.LeadingZeroCount(fractMant) - (64 - DblMantSZ); |
| 164 | + ulong resExpo = (expo - (ulong)fractLzcnt - 1) << DblMantSZ; |
| 165 | + ulong resMant = (fractMant << (fractLzcnt + 1)) & DblMant; |
| 166 | + |
| 167 | + ulong res = resExpo | resMant; |
| 168 | + |
| 169 | + return Unsafe.As<ulong, double>(ref res); |
| 170 | + } |
| 171 | + |
| 172 | + private static readonly double InvPrec = Math.Pow(10.0, 13); |
| 173 | + private static readonly double TwoInvPrec = Math.Pow(2.0, 13); |
| 174 | + private static readonly double FiveInvPrec = Math.Pow(5.0, 13); |
| 175 | + |
| 176 | +#if !DEBUG |
| 177 | + [MethodImpl(MethodImplOptions.AggressiveInlining)] |
| 178 | +#endif |
| 179 | + private static double Round13(double x) |
| 180 | + { |
| 181 | + double normalCase = Math.Round(x * InvPrec, MidpointRounding.AwayFromZero) / InvPrec; |
| 182 | + |
| 183 | + if (normalCase == Math.Round(Math.BitDecrement(x) * InvPrec, MidpointRounding.AwayFromZero) / InvPrec) |
| 184 | + return normalCase; |
| 185 | + |
| 186 | + double truncated = Fract(x * TwoInvPrec) * FiveInvPrec; |
| 187 | + |
| 188 | + if (Fract(truncated) >= 0.5) |
| 189 | + return (Math.Floor(x * InvPrec) + 1) / InvPrec; |
| 190 | + |
| 191 | + return Math.Floor(x * InvPrec) / InvPrec; |
| 192 | + } |
| 193 | + |
130 | 194 | #if !DEBUG |
131 | 195 | [MethodImpl(MethodImplOptions.AggressiveInlining)] |
132 | 196 | #endif |
133 | 197 | private static double IteratePRNG(double state) |
134 | 198 | { |
135 | | - return Math.Round((state * 1.72431234 + 2.134453429141) % 1, 13); |
| 199 | + return Round13(Fract(state * 1.72431234 + 2.134453429141)); |
136 | 200 | } |
137 | 201 |
|
138 | 202 | #if !DEBUG |
|
0 commit comments