private string CreateExponentialRepresentation(
            DtoaBuilder buffer,
            int exponent,
            bool negative,
            int significantDigits)
        {
            bool negativeExponent = false;

            if (exponent < 0)
            {
                negativeExponent = true;
                exponent         = -exponent;
            }

            using (var builder = StringBuilderPool.Rent())
            {
                if (negative)
                {
                    builder.Builder.Append('-');
                }
                builder.Builder.Append(buffer._chars[0]);
                if (significantDigits != 1)
                {
                    builder.Builder.Append('.');
                    builder.Builder.Append(buffer._chars, 1, buffer.Length - 1);
                    int length = buffer.Length;
                    builder.Builder.Append('0', significantDigits - length);
                }

                builder.Builder.Append('e');
                builder.Builder.Append(negativeExponent ? '-' : '+');
                builder.Builder.Append(exponent);
                return(builder.ToString());
            }
        }
        internal static string NumberToString(
            double m,
            DtoaBuilder builder,
            StringBuilder stringBuilder)
        {
            if (double.IsNaN(m))
            {
                return("NaN");
            }

            if (m == 0)
            {
                return("0");
            }

            if (double.IsPositiveInfinity(m))
            {
                return("Infinity");
            }

            if (double.IsNegativeInfinity(m))
            {
                return("-Infinity");
            }

            DtoaNumberFormatter.DoubleToAscii(
                builder,
                m,
                DtoaMode.Shortest,
                0,
                out var negative,
                out var decimal_point);

            if (negative)
            {
                stringBuilder.Append('-');
            }

            if (builder.Length <= decimal_point && decimal_point <= 21)
            {
                // ECMA-262 section 9.8.1 step 6.
                stringBuilder.Append(builder._chars, 0, builder.Length);
                stringBuilder.Append('0', decimal_point - builder.Length);
            }
            else if (0 < decimal_point && decimal_point <= 21)
            {
                // ECMA-262 section 9.8.1 step 7.
                stringBuilder.Append(builder._chars, 0, decimal_point);
                stringBuilder.Append('.');
                stringBuilder.Append(builder._chars, decimal_point, builder.Length - decimal_point);
            }
            else if (decimal_point <= 0 && decimal_point > -6)
            {
                // ECMA-262 section 9.8.1 step 8.
                stringBuilder.Append("0.");
                stringBuilder.Append('0', -decimal_point);
                stringBuilder.Append(builder._chars, 0, builder.Length);
            }
            else
            {
                // ECMA-262 section 9.8.1 step 9 and 10 combined.
                stringBuilder.Append(builder._chars[0]);
                if (builder.Length != 1)
                {
                    stringBuilder.Append('.');
                    stringBuilder.Append(builder._chars, 1, builder.Length - 1);
                }

                stringBuilder.Append('e');
                stringBuilder.Append((decimal_point >= 0) ? '+' : '-');
                int exponent = decimal_point - 1;
                if (exponent < 0)
                {
                    exponent = -exponent;
                }

                stringBuilder.Append(exponent);
            }

            return(stringBuilder.ToString());
        }
        private JsValue ToPrecision(JsValue thisObj, JsValue[] arguments)
        {
            if (!thisObj.IsNumber() && ReferenceEquals(thisObj.TryCast <NumberInstance>(), null))
            {
                ExceptionHelper.ThrowTypeError(Engine);
            }

            var x = TypeConverter.ToNumber(thisObj);
            var precisionArgument = arguments.At(0);

            if (precisionArgument.IsUndefined())
            {
                return(TypeConverter.ToString(x));
            }

            var p = (int)TypeConverter.ToInteger(precisionArgument);

            if (double.IsNaN(x))
            {
                return("NaN");
            }

            if (double.IsInfinity(x))
            {
                return(thisObj.ToString());
            }

            if (p < 1 || p > 100)
            {
                ExceptionHelper.ThrowRangeError(_engine, "precision must be between 1 and 100");
            }

            var dtoaBuilder = new DtoaBuilder(101);

            DtoaNumberFormatter.DoubleToAscii(
                dtoaBuilder,
                x,
                DtoaMode.Precision,
                p,
                out var negative,
                out var decimalPoint);


            int exponent = decimalPoint - 1;

            if (exponent < -6 || exponent >= p)
            {
                return(CreateExponentialRepresentation(dtoaBuilder, exponent, negative, p));
            }

            using (var builder = StringBuilderPool.Rent())
            {
                // Use fixed notation.
                if (negative)
                {
                    builder.Builder.Append('-');
                }

                if (decimalPoint <= 0)
                {
                    builder.Builder.Append("0.");
                    builder.Builder.Append('0', -decimalPoint);
                    builder.Builder.Append(dtoaBuilder._chars, 0, dtoaBuilder.Length);
                    builder.Builder.Append('0', p - dtoaBuilder.Length);
                }
                else
                {
                    int m = System.Math.Min(dtoaBuilder.Length, decimalPoint);
                    builder.Builder.Append(dtoaBuilder._chars, 0, m);
                    builder.Builder.Append('0', System.Math.Max(0, decimalPoint - dtoaBuilder.Length));
                    if (decimalPoint < p)
                    {
                        builder.Builder.Append('.');
                        var extra = negative ? 2 : 1;
                        if (dtoaBuilder.Length > decimalPoint)
                        {
                            int len = dtoaBuilder.Length - decimalPoint;
                            int n   = System.Math.Min(len, p - (builder.Builder.Length - extra));
                            builder.Builder.Append(dtoaBuilder._chars, decimalPoint, n);
                        }

                        builder.Builder.Append('0', System.Math.Max(0, extra + (p - builder.Builder.Length)));
                    }
                }

                return(builder.ToString());
            }
        }
        /// <summary>
        /// https://www.ecma-international.org/ecma-262/6.0/#sec-number.prototype.toexponential
        /// </summary>
        private JsValue ToExponential(JsValue thisObj, JsValue[] arguments)
        {
            if (!thisObj.IsNumber() && ReferenceEquals(thisObj.TryCast <NumberInstance>(), null))
            {
                ExceptionHelper.ThrowTypeError(Engine);
            }

            var x = TypeConverter.ToNumber(thisObj);
            var fractionDigits = arguments.At(0);

            if (fractionDigits.IsUndefined())
            {
                fractionDigits = JsNumber.PositiveZero;
            }

            var f = (int)TypeConverter.ToInteger(fractionDigits);

            if (double.IsNaN(x))
            {
                return("NaN");
            }

            if (double.IsInfinity(x))
            {
                return(thisObj.ToString());
            }

            if (f < 0 || f > 100)
            {
                ExceptionHelper.ThrowRangeError(_engine, "fractionDigits argument must be between 0 and 100");
            }

            if (arguments.At(0).IsUndefined())
            {
                f = -1;
            }

            bool negative = false;

            if (x < 0)
            {
                x        = -x;
                negative = true;
            }

            int         decimalPoint;
            DtoaBuilder dtoaBuilder;

            if (f == -1)
            {
                dtoaBuilder = new DtoaBuilder();
                DtoaNumberFormatter.DoubleToAscii(
                    dtoaBuilder,
                    x,
                    DtoaMode.Shortest,
                    requested_digits: 0,
                    out _,
                    out decimalPoint);
                f = dtoaBuilder.Length - 1;
            }
            else
            {
                dtoaBuilder = new DtoaBuilder(101);
                DtoaNumberFormatter.DoubleToAscii(
                    dtoaBuilder,
                    x,
                    DtoaMode.Precision,
                    requested_digits: f + 1,
                    out _,
                    out decimalPoint);
            }

            Debug.Assert(dtoaBuilder.Length > 0);
            Debug.Assert(dtoaBuilder.Length <= f + 1);

            int exponent = decimalPoint - 1;
            var result   = CreateExponentialRepresentation(dtoaBuilder, exponent, negative, f + 1);

            return(result);
        }