Browse Source

use struct for DiyFp

Marko Lahma 7 years ago
parent
commit
0e6b4dc384

+ 6 - 10
Jint/Native/Number/Dtoa/CachePowers.cs

@@ -32,8 +32,7 @@ using System.Diagnostics;
 
 namespace Jint.Native.Number.Dtoa
 {
-
-    public class CachedPowers
+    internal class CachedPowers
     {
         private const double Kd1Log210 = 0.30102999566398114; //  1 / lg(10)
 
@@ -52,17 +51,16 @@ namespace Jint.Native.Number.Dtoa
         }
 
 
-        internal static int GetCachedPower(int e, int alpha, int gamma, DiyFp cMk)
+        internal static (short, DiyFp) GetCachedPower(int e, int alpha, int gamma)
         {
             const int kQ = DiyFp.KSignificandSize;
-            double k = System.Math.Ceiling((alpha - e + kQ - 1)*Kd1Log210);
-            int index = (GrisuCacheOffset + (int) k - 1)/CachedPowersSpacing + 1;
+            double k = System.Math.Ceiling((alpha - e + kQ - 1) * Kd1Log210);
+            int index = (GrisuCacheOffset + (int) k - 1) / CachedPowersSpacing + 1;
             CachedPower cachedPower = CACHED_POWERS[index];
 
-            cMk.F = cachedPower.Significand;
-            cMk.E = cachedPower.BinaryExponent;
+            var cMk = new DiyFp(cachedPower.Significand, cachedPower.BinaryExponent);
             Debug.Assert((alpha <= cMk.E + e) && (cMk.E + e <= gamma));
-            return cachedPower.DecimalExponent;
+            return (cachedPower.DecimalExponent, cMk);
         }
 
         // Code below is converted from GRISU_CACHE_NAME(8) in file "powers-ten.h"
@@ -159,7 +157,5 @@ namespace Jint.Native.Number.Dtoa
         };
 
         private const int GrisuCacheOffset = 308;
-
-
     }
 }

+ 22 - 49
Jint/Native/Number/Dtoa/DiyFp.cs

@@ -38,25 +38,19 @@ namespace Jint.Native.Number.Dtoa
 // have the most significant bit of the significand set.
 // Multiplication and Subtraction do not normalize their results.
 // DiyFp are not designed to contain special doubles (NaN and Infinity).
-    internal class DiyFp
+    internal struct DiyFp
     {
         internal const int KSignificandSize = 64;
         private const ulong KUint64MSB = 0x8000000000000000L;
 
-        internal DiyFp()
-        {
-            F = 0;
-            E = 0;
-        }
-
         internal DiyFp(long f, int e)
         {
             F = f;
             E = e;
         }
 
-        public long F { get; set; }
-        public int E { get; set; }
+        public long F { get; }
+        public int E { get; }
 
         private static bool Uint64Gte(long a, long b)
         {
@@ -64,66 +58,46 @@ namespace Jint.Native.Number.Dtoa
             return (a == b) || ((a > b) ^ (a < 0) ^ (b < 0));
         }
 
-        // this = this - other.
-        // The exponents of both numbers must be the same and the significand of this
-        // must be bigger than the significand of other.
-        // The result will not be normalized.
-        private void Subtract(DiyFp other)
-        {
-            Debug.Assert(E == other.E);
-            Debug.Assert(Uint64Gte(F, other.F));
-
-            F -= other.F;
-        }
-
         // Returns a - b.
         // The exponents of both numbers must be the same and this must be bigger
         // than other. The result will not be normalized.
         internal static DiyFp Minus(DiyFp a, DiyFp b)
         {
-            DiyFp result = new DiyFp(a.F, a.E);
-            result.Subtract(b);
-            return result;
+            Debug.Assert(a.E == b.E);
+            Debug.Assert(Uint64Gte(a.F, b.F));
+
+            return new DiyFp(a.F - b.F, a.E);
         }
 
         // this = this * other.
-        private void Multiply(DiyFp other)
+
+        // returns a * b;
+        internal static DiyFp Times(DiyFp a, DiyFp b)
         {
+            DiyFp result = new DiyFp(a.F, a.E);
             // Simply "emulates" a 128 bit multiplication.
             // However: the resulting number only contains 64 bits. The least
             // significant 64 bits are only used for rounding the most significant 64
             // bits.
             const long kM32 = 0xFFFFFFFFL;
-            long a = F.UnsignedShift(32);
-            long b = F & kM32;
-            long c = other.F.UnsignedShift(32);
-            long d = other.F & kM32;
-            long ac = a*c;
-            long bc = b*c;
-            long ad = a*d;
-            long bd = b*d;
+            long a1 = result.F.UnsignedShift(32);
+            long b1 = result.F & kM32;
+            long c = b.F.UnsignedShift(32);
+            long d = b.F & kM32;
+            long ac = a1*c;
+            long bc = b1*c;
+            long ad = a1*d;
+            long bd = b1*d;
             long tmp = bd.UnsignedShift(32) + (ad & kM32) + (bc & kM32);
             // By adding 1U << 31 to tmp we round the final result.
             // Halfway cases will be round up.
             tmp += 1L << 31;
             long resultF = ac + ad.UnsignedShift(32) + bc.UnsignedShift(32) + tmp.UnsignedShift(32);
-            E += other.E + 64;
-            F = resultF;
+            return new DiyFp(resultF, result.E + b.E + 64);
         }
 
-        // returns a * b;
-        internal static DiyFp Times(DiyFp a, DiyFp b)
+        internal static DiyFp Normalize(long f, int e)
         {
-            DiyFp result = new DiyFp(a.F, a.E);
-            result.Multiply(b);
-            return result;
-        }
-
-        internal void Normalize()
-        {
-            long f = F;
-            int e = E;
-
             // This method is mainly called for normalizing boundaries. In general
             // boundaries need to be shifted by 10 bits. We thus optimize for this case.
             const long k10MsBits = 0xFFC00000L << 32;
@@ -138,8 +112,7 @@ namespace Jint.Native.Number.Dtoa
                 e--;
             }
 
-            F = f;
-            E = e;
+            return new DiyFp(f, e);
         }
 
         public override string ToString()

+ 8 - 11
Jint/Native/Number/Dtoa/DoubleHelper.cs

@@ -34,7 +34,7 @@ namespace Jint.Native.Number.Dtoa
 {
 
 // Helper functions for doubles.
-    public class DoubleHelper
+    internal class DoubleHelper
     {
 
         private const long KExponentMask = 0x7FF0000000000000L;
@@ -104,13 +104,12 @@ namespace Jint.Native.Number.Dtoa
         // Returns the two boundaries of first argument.
         // The bigger boundary (m_plus) is normalized. The lower boundary has the same
         // exponent as m_plus.
-        internal static void NormalizedBoundaries(long d64, DiyFp mMinus, DiyFp mPlus)
+        internal static (DiyFp, DiyFp) NormalizedBoundaries(long d64)
         {
             DiyFp v = AsDiyFp(d64);
             bool significandIsZero = (v.F == KHiddenBit);
-            mPlus.F = (v.F << 1) + 1;
-            mPlus.E = v.E - 1;
-            mPlus.Normalize();
+            var mPlus = DiyFp.Normalize((v.F << 1) + 1, v.E - 1);
+            DiyFp mMinus;
             if (significandIsZero && v.E != KDenormalExponent)
             {
                 // The boundary is closer. Think of v = 1000e10 and v- = 9999e9.
@@ -119,16 +118,14 @@ namespace Jint.Native.Number.Dtoa
                 // The only exception is for the smallest normal: the largest denormal is
                 // at the same distance as its successor.
                 // Note: denormals have the same exponent as the smallest normals.
-                mMinus.F = (v.F << 2) - 1;
-                mMinus.E = v.E - 2;
+                mMinus = new DiyFp((v.F << 2) - 1, v.E - 2);
             }
             else
             {
-                mMinus.F = (v.F << 1) - 1;
-                mMinus.E = v.E - 1;
+                mMinus = new DiyFp((v.F << 1) - 1, v.E - 1);
             }
-            mMinus.F = mMinus.F << (mMinus.E - mPlus.E);
-            mMinus.E = mPlus.E;
+            mMinus = new DiyFp(mMinus.F << (mMinus.E - mPlus.E), mPlus.E);
+            return (mMinus, mPlus);
         }
 
         private const int KSignificandSize = 52; // Excludes the hidden bit.

+ 21 - 21
Jint/Native/Number/Dtoa/FastDtoa.cs

@@ -30,15 +30,19 @@
 
 using System;
 using System.Diagnostics;
+using System.Threading;
 
 namespace Jint.Native.Number.Dtoa
 {
-    public class FastDtoa
+    internal class FastDtoa
     {
+        // share buffer to reduce memory usage
+        private static readonly ThreadLocal<FastDtoaBuilder> cachedBuffer = new ThreadLocal<FastDtoaBuilder>(() => new FastDtoaBuilder());
+
 
         // FastDtoa will produce at most kFastDtoaMaximalLength digits.
         public const int KFastDtoaMaximalLength = 17;
-        
+
         // The minimal and maximal target exponent define the range of w's binary
         // exponent, where 'w' is the result of multiplying the input by a cached power
         // of ten.
@@ -167,7 +171,7 @@ namespace Jint.Native.Number.Dtoa
             //   Conceptually we have: rest ~= too_high - buffer
             return (2*unit <= rest) && (rest <= unsafeInterval - 4*unit);
         }
-        
+
         private const int KTen4 = 10000;
         private const int KTen5 = 100000;
         private const int KTen6 = 1000000;
@@ -430,10 +434,8 @@ namespace Jint.Native.Number.Dtoa
             {
                 fractionals *= 5;
                 unit *= 5;
-                unsafeInterval.F = unsafeInterval.F*5;
-                unsafeInterval.E = unsafeInterval.E + 1; // Will be optimized out.
-                one.F = one.F.UnsignedShift(1);
-                one.E = one.E + 1;
+                unsafeInterval = new DiyFp(unsafeInterval.F*5, unsafeInterval.E + 1); // Will be optimized out.
+                one = new DiyFp(one.F.UnsignedShift(1), one.E + 1);
                 // Integer division by one.
                 var digit = (int) ((fractionals.UnsignedShift(-one.E)) & 0xffffffffL);
                 buffer.Append((char) ('0' + digit));
@@ -467,12 +469,13 @@ namespace Jint.Native.Number.Dtoa
             // closest floating-point neighbors. Any number strictly between
             // boundary_minus and boundary_plus will round to v when convert to a double.
             // Grisu3 will never output representations that lie exactly on a boundary.
-            DiyFp boundaryMinus = new DiyFp(), boundaryPlus = new DiyFp();
-            DoubleHelper.NormalizedBoundaries(bits, boundaryMinus, boundaryPlus);
+            (DiyFp boundaryMinus, DiyFp boundaryPlus) = DoubleHelper.NormalizedBoundaries(bits);
             Debug.Assert(boundaryPlus.E == w.E);
-            var tenMk = new DiyFp(); // Cached power of ten: 10^-k
-            int mk = CachedPowers.GetCachedPower(w.E + DiyFp.KSignificandSize,
-                MinimalTargetExponent, MaximalTargetExponent, tenMk);
+
+            var (mk, tenMk) = CachedPowers.GetCachedPower(
+                w.E + DiyFp.KSignificandSize,
+                MinimalTargetExponent, MaximalTargetExponent);
+
             Debug.Assert(MinimalTargetExponent <= w.E + tenMk.E +
                          DiyFp.KSignificandSize &&
                          MaximalTargetExponent >= w.E + tenMk.E +
@@ -517,19 +520,16 @@ namespace Jint.Native.Number.Dtoa
 
         public static string NumberToString(double v)
         {
-            var buffer = new FastDtoaBuilder();
-            return NumberToString(v, buffer) ? buffer.Format() : null;
-        }
-
-        public static bool NumberToString(double v, FastDtoaBuilder buffer)
-        {
-            buffer.Reset();
+            cachedBuffer.Value.Reset();
             if (v < 0)
             {
-                buffer.Append('-');
+                cachedBuffer.Value.Append('-');
                 v = -v;
             }
-            return Dtoa(v, buffer);
+
+            var numberToString = Dtoa(v, cachedBuffer.Value);
+            var toString = numberToString ? cachedBuffer.Value.Format() : null;
+            return toString;
         }
     }
 }

+ 10 - 5
Jint/Native/Number/Dtoa/FastDtoaBuilder.cs

@@ -2,14 +2,16 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
+using System.Runtime.CompilerServices;
+
 namespace Jint.Native.Number.Dtoa
 {
-    public class FastDtoaBuilder
+    internal class FastDtoaBuilder
     {
 
         // allocate buffer for generated digits + extra notation + padding zeroes
         private readonly char[] _chars = new char[FastDtoa.KFastDtoaMaximalLength + 8];
-        internal int End = 0;
+        internal int End;
         internal int Point;
         private bool _formatted;
 
@@ -25,16 +27,18 @@ namespace Jint.Native.Number.Dtoa
 
         public void Reset()
         {
+            Point = 0;
             End = 0;
             _formatted = false;
+            System.Array.Clear(_chars, 0, _chars.Length);
         }
 
         public override string ToString()
         {
-            return "[chars:" + new System.String(_chars, 0, End) + ", point:" + Point + "]";
+            return "[chars:" + new string(_chars, 0, End) + ", point:" + Point + "]";
         }
 
-        public System.String Format()
+        public string Format()
         {
             if (!_formatted)
             {
@@ -51,7 +55,7 @@ namespace Jint.Native.Number.Dtoa
                 }
                 _formatted = true;
             }
-            return new System.String(_chars, 0, End);
+            return new string(_chars, 0, End);
 
         }
 
@@ -127,6 +131,7 @@ namespace Jint.Native.Number.Dtoa
             '0', '1', '2', '3', '4', '5', '6', '7', '8', '9'
         };
 
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         private void Fill<T>(T[] array, int fromIndex, int toIndex, T val)
         {
             for (int i = fromIndex; i < toIndex; i++)

+ 5 - 2
Jint/Native/Number/Dtoa/NumberExtensions.cs

@@ -1,7 +1,10 @@
-namespace Jint.Native.Number.Dtoa
+using System.Runtime.CompilerServices;
+
+namespace Jint.Native.Number.Dtoa
 {
-    public static class NumberExtensions
+    internal static class NumberExtensions
     {
+        [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static long UnsignedShift(this long l, int shift)
         {
             return (long) ((ulong) l >> shift);