using System.Collections.Generic;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
const int Iterations = 5_000_000;
var bitSizes = new[] { 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536 };
Console.WriteLine($"Running {Iterations:N0} iterations per method for each BigInteger size...\n");
Console.WriteLine($"{"Bits",6} | {"Mask & Cast",12} | {"ToByteArray->BitConv",20} | {"Reflection",10} | {"Compile", 7}");
foreach (int bits in bitSizes)
BigInteger bigPos = BigInteger.One << (bits - 1);
BigInteger bigNeg = -bigPos;
long acc1 = 0, acc2 = 0, acc3 = 0, acc4 = 0;
var sw1 = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
acc1 += MaskToInt((i & 1) == 0 ? bigPos : bigNeg);
var sw2 = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
acc2 += ((i & 1) == 0 ? bigPos : bigNeg).ToInt();
var sw3 = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
acc3 += ((i & 1) == 0 ? bigPos : bigNeg).ToInt32Truncate();
var sw4 = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
acc4 += ((i & 1) == 0 ? bigPos : bigNeg).ToInt32TruncateCompiled();
if (acc1 != acc2 || acc1 != acc3 || acc1 != acc4)
Console.WriteLine($"Error: Results mismatch for {bits} bits!");
Console.WriteLine($"{bits,6} | {sw1.ElapsedMilliseconds,12:N0} | {sw2.ElapsedMilliseconds,20:N0} | {sw3.ElapsedMilliseconds,10:N0} | {sw4.ElapsedMilliseconds,7:N0}");
Console.WriteLine("\nDone.");
static void Warmup(BigInteger p, BigInteger n)
for (int i = 0; i < 1000; i++)
_ = p.ToInt32TruncateCompiled();
_ = n.ToInt32TruncateCompiled();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static int MaskToInt(BigInteger big) => unchecked((int)(uint)(big & uint.MaxValue));
public static class BigIntegerToByteArrayExtensions
public static int ToInt(this BigInteger big)
return BitConverter.ToInt32(big.ToByteArray().AsSpan(0, 4));
public static class BigIntegerExtensions
private static readonly FieldInfo s_signField;
private static readonly FieldInfo s_bitsField;
static BigIntegerExtensions()
s_signField = typeof(BigInteger)
.GetField("_sign", BindingFlags.NonPublic | BindingFlags.Instance)
?? throw new Exception("Could not find BigInteger._sign field");
s_bitsField = typeof(BigInteger)
.GetField("_bits", BindingFlags.NonPublic | BindingFlags.Instance)
?? throw new Exception("Could not find BigInteger._bits field");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ToUInt32Truncate(this BigInteger value)
if (value.GetBitLength() < 256)
return BitConverter.ToUInt32(value.ToByteArray().AsSpan(0, 4));
uint[]? magnitude = (uint[]?)s_bitsField.GetValue(value);
int sign = (int)s_signField.GetValue(value)!;
uint absSign = (uint)(-sign);
return (uint)(~absSign + 1);
uint lowWord = magnitude.Length > 0 ? magnitude[0] : 0u;
return sign > 0 ? lowWord : unchecked((uint)(~lowWord + 1));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ToInt32Truncate(this BigInteger value)
uint u = value.ToUInt32Truncate();
return unchecked((int)u);
public static class BigIntegerCompiledAccess
public static readonly Func<BigInteger, int> GetSign;
public static readonly Func<BigInteger, uint[]?> GetBits;
static BigIntegerCompiledAccess()
var param = Expression.Parameter(typeof(BigInteger), "x");
var signField = typeof(BigInteger)
.GetField("_sign", BindingFlags.NonPublic | BindingFlags.Instance)
?? throw new InvalidOperationException("Field '_sign' not found");
var bitsField = typeof(BigInteger)
.GetField("_bits", BindingFlags.NonPublic | BindingFlags.Instance)
?? throw new InvalidOperationException("Field '_bits' not found");
GetSign = Expression.Lambda<Func<BigInteger, int>>(
Expression.Field(param, signField), param).Compile();
GetBits = Expression.Lambda<Func<BigInteger, uint[]?>>(
Expression.Field(param, bitsField), param).Compile();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ToUInt32TruncateCompiled(this BigInteger value)
if (value.IsZero) return 0u;
int sign = BigIntegerCompiledAccess.GetSign(value);
uint[]? magnitude = BigIntegerCompiledAccess.GetBits(value);
if (sign >= 0) return (uint)sign;
uint absSign = (uint)(-sign);
return (uint)(~absSign + 1);
uint lowWord = magnitude.Length > 0 ? magnitude[0] : 0u;
return sign > 0 ? lowWord : unchecked((uint)(~lowWord + 1));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ToInt32TruncateCompiled(this BigInteger value)
return unchecked((int)value.ToUInt32TruncateCompiled());
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
const int Iterations = 5_000_000;
var bitSizes = new[] { 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384, 32768, 65536 };
Console.WriteLine($"Running {Iterations:N0} iterations per method for each BigInteger size...\n");
Console.WriteLine($"{"Bits",6} | {"Mask & Cast",12} | {"ToByteArray->BitConv",20} | {"Reflection",10} | {"Compile", 7}");
foreach (int bits in bitSizes)
BigInteger bigPos = BigInteger.One << (bits - 1);
BigInteger bigNeg = -bigPos;
long acc1 = 0, acc2 = 0, acc3 = 0, acc4 = 0;
var sw1 = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
acc1 += MaskToInt((i & 1) == 0 ? bigPos : bigNeg);
var sw2 = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
acc2 += ((i & 1) == 0 ? bigPos : bigNeg).ToInt();
var sw3 = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
acc3 += ((i & 1) == 0 ? bigPos : bigNeg).ToInt32Truncate();
var sw4 = Stopwatch.StartNew();
for (int i = 0; i < Iterations; i++)
acc4 += ((i & 1) == 0 ? bigPos : bigNeg).ToInt32TruncateCompiled();
if (acc1 != acc2 || acc1 != acc3 || acc1 != acc4)
Console.WriteLine($"Error: Results mismatch for {bits} bits!");
Console.WriteLine($"{bits,6} | {sw1.ElapsedMilliseconds,12:N0} | {sw2.ElapsedMilliseconds,20:N0} | {sw3.ElapsedMilliseconds,10:N0} | {sw4.ElapsedMilliseconds,7:N0}");
Console.WriteLine("\nDone.");
static void Warmup(BigInteger p, BigInteger n)
for (int i = 0; i < 1000; i++)
_ = p.ToInt32TruncateCompiled();
_ = n.ToInt32TruncateCompiled();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
static int MaskToInt(BigInteger big) => unchecked((int)(uint)(big & uint.MaxValue));
public static class BigIntegerToByteArrayExtensions
public static int ToInt(this BigInteger big)
return BitConverter.ToInt32(big.ToByteArray().AsSpan(0, 4));
public static class BigIntegerExtensions
private static readonly FieldInfo s_signField;
private static readonly FieldInfo s_bitsField;
static BigIntegerExtensions()
s_signField = typeof(BigInteger)
.GetField("_sign", BindingFlags.NonPublic | BindingFlags.Instance)
?? throw new Exception("Could not find BigInteger._sign field");
s_bitsField = typeof(BigInteger)
.GetField("_bits", BindingFlags.NonPublic | BindingFlags.Instance)
?? throw new Exception("Could not find BigInteger._bits field");
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ToUInt32Truncate(this BigInteger value)
if (value.GetBitLength() < 256)
return BitConverter.ToUInt32(value.ToByteArray().AsSpan(0, 4));
uint[]? magnitude = (uint[]?)s_bitsField.GetValue(value);
int sign = (int)s_signField.GetValue(value)!;
uint absSign = (uint)(-sign);
return (uint)(~absSign + 1);
uint lowWord = magnitude.Length > 0 ? magnitude[0] : 0u;
return sign > 0 ? lowWord : unchecked((uint)(~lowWord + 1));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ToInt32Truncate(this BigInteger value)
uint u = value.ToUInt32Truncate();
return unchecked((int)u);
public static class BigIntegerCompiledAccess
public static readonly Func<BigInteger, int> GetSign;
public static readonly Func<BigInteger, uint[]?> GetBits;
static BigIntegerCompiledAccess()
var param = Expression.Parameter(typeof(BigInteger), "x");
var signField = typeof(BigInteger)
.GetField("_sign", BindingFlags.NonPublic | BindingFlags.Instance)
?? throw new InvalidOperationException("Field '_sign' not found");
var bitsField = typeof(BigInteger)
.GetField("_bits", BindingFlags.NonPublic | BindingFlags.Instance)
?? throw new InvalidOperationException("Field '_bits' not found");
GetSign = Expression.Lambda<Func<BigInteger, int>>(
Expression.Field(param, signField), param).Compile();
GetBits = Expression.Lambda<Func<BigInteger, uint[]?>>(
Expression.Field(param, bitsField), param).Compile();
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static uint ToUInt32TruncateCompiled(this BigInteger value)
if (value.IsZero) return 0u;
int sign = BigIntegerCompiledAccess.GetSign(value);
uint[]? magnitude = BigIntegerCompiledAccess.GetBits(value);
if (sign >= 0) return (uint)sign;
uint absSign = (uint)(-sign);
return (uint)(~absSign + 1);
uint lowWord = magnitude.Length > 0 ? magnitude[0] : 0u;
return sign > 0 ? lowWord : unchecked((uint)(~lowWord + 1));
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public static int ToInt32TruncateCompiled(this BigInteger value)
return unchecked((int)value.ToUInt32TruncateCompiled());