{
[Serializable]
[TypeForwardedFrom("System.Numerics, Version=4.0.0.0, PublicKeyToken=b77a5c561934e089")]
+ [DebuggerDisplay("{DebuggerDisplay,nq}")]
public readonly struct BigInteger
: ISpanFormattable,
IComparable,
return BigNumber.FormatBigInteger(this, format, NumberFormatInfo.GetInstance(provider));
}
+ private string DebuggerDisplay
+ {
+ get
+ {
+ // For very big numbers, ToString can be too long or even timeout for Visual Studio to display
+ // Display a fast estimated value instead
+
+ // Use ToString for small values
+
+ if ((_bits is null) || (_bits.Length <= 4))
+ {
+ return ToString();
+ }
+
+ // Estimate the value x as `L * 2^n`, while L is the value of high bits, and n is the length of low bits
+ // Represent L as `k * 10^i`, then `x = L * 2^n = k * 10^(i + (n * log10(2)))`
+ // Let `m = n * log10(2)`, the final result would be `x = (k * 10^(m - [m])) * 10^(i+[m])`
+
+ const double log10Of2 = 0.3010299956639812; // Log10(2)
+ ulong highBits = ((ulong)_bits[^1] << kcbitUint) + _bits[^2];
+ double lowBitsCount32 = _bits.Length - 2; // if Length > int.MaxValue/32, counting in bits can cause overflow
+ double exponentLow = lowBitsCount32 * kcbitUint * log10Of2;
+
+ // Max possible length of _bits is int.MaxValue of bytes,
+ // thus max possible value of BigInteger is 2^(8*Array.MaxLength)-1 which is larger than 10^(2^33)
+ // Use long to avoid potential overflow
+ long exponent = (long)exponentLow;
+ double significand = (double)highBits * Math.Pow(10, exponentLow - exponent);
+
+ // scale significand to [1, 10)
+ double log10 = Math.Log10(significand);
+ if (log10 >= 1)
+ {
+ exponent += (long)log10;
+ significand /= Math.Pow(10, Math.Floor(log10));
+ }
+
+ // The digits can be incorrect because of floating point errors and estimation in Log and Exp
+ // Keep some digits in the significand. 8 is arbitrarily chosen, about half of the precision of double
+ significand = Math.Round(significand, 8);
+
+ if (significand >= 10.0)
+ {
+ // 9.9999999999999 can be rounded to 10, make the display to be more natural
+ significand /= 10.0;
+ exponent++;
+ }
+
+ string signStr = _sign < 0 ? NumberFormatInfo.CurrentInfo.NegativeSign : "";
+
+ // Use about a half of the precision of double
+ return $"{signStr}{significand:F8}e+{exponent}";
+ }
+ }
+
public bool TryFormat(Span<char> destination, out int charsWritten, [StringSyntax(StringSyntaxAttribute.NumericFormat)] ReadOnlySpan<char> format = default, IFormatProvider? provider = null)
{
return BigNumber.TryFormatBigInteger(this, format, NumberFormatInfo.GetInstance(provider), destination, out charsWritten);
--- /dev/null
+// Licensed to the .NET Foundation under one or more agreements.
+// The .NET Foundation licenses this file to you under the MIT license.
+
+using System.Diagnostics;
+using System.Globalization;
+using System.Tests;
+using Xunit;
+
+namespace System.Numerics.Tests
+{
+ public class DebuggerDisplayTests
+ {
+ [Theory]
+ [InlineData(new uint[] { 0, 0, 1 }, "18446744073709551616")]
+ [InlineData(new uint[] { 0, 0, 0, 0, 1 }, "3.40282367e+38")]
+ [InlineData(new uint[] { 0, 0x12345678, 0, 0xCC00CC00, 0x80808080 }, "7.33616508e+47")]
+ [SkipOnPlatform(TestPlatforms.Browser, "DebuggerDisplayAttribute is stripped on wasm")]
+ public void TestDebuggerDisplay(uint[] bits, string displayString)
+ {
+ using (new ThreadCultureChange(CultureInfo.InvariantCulture))
+ {
+ BigInteger positiveValue = new BigInteger(1, bits);
+ Assert.Equal(displayString, DebuggerAttributes.ValidateDebuggerDisplayReferences(positiveValue));
+
+ BigInteger negativeValue = new BigInteger(-1, bits);
+ Assert.Equal("-" + displayString, DebuggerAttributes.ValidateDebuggerDisplayReferences(negativeValue));
+ }
+ }
+ }
+}
-<Project Sdk="Microsoft.NET.Sdk">
+<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>$(NetCoreAppCurrent)</TargetFramework>
<IncludeRemoteExecutor>true</IncludeRemoteExecutor>
<Compile Include="BigInteger\cast_to.cs" />
<Compile Include="BigInteger\Comparison.cs" />
<Compile Include="BigInteger\ctor.cs" />
+ <Compile Include="BigInteger\DebuggerDisplayTests.cs" />
<Compile Include="BigInteger\divide.cs" />
<Compile Include="BigInteger\divrem.cs" />
<Compile Include="BigInteger\Driver.cs" />
</ItemGroup>
<ItemGroup>
<Compile Include="$(CommonTestPath)System\GenericMathHelpers.cs" Link="Common\System\GenericMathHelpers.cs" />
+ <Compile Include="$(CommonTestPath)System\Diagnostics\DebuggerAttributes.cs" Link="Common\System\Diagnostics\DebuggerAttributes.cs" />
<Compile Include="BigIntegerTests.GenericMath.cs" />
<Compile Include="ComplexTests.GenericMath.cs" />
</ItemGroup>