Interoperability Guide

May 27, 2026 · View on GitHub

This page covers the relationship between Deveel.Math types and .NET built-in types, including System.Numerics.BigInteger, primitive numeric types, and detailed parsing behavior.

Why a Separate BigInteger?

Deveel Math includes its own BigInteger implementation for several important reasons:

Historical Context

Deveel Math originated as a port of the Java java.math package from Apache Harmony. At the time of the original port, .NET did not have a mature System.Numerics namespace, and the library was developed to fill the gap for arbitrary-precision arithmetic in .NET applications.

BigDecimal is the Primary Driver

The main reason Deveel Math exists today is BigDecimal — a type that .NET still does not provide natively. BigDecimal requires an internal BigInteger for its unscaled value. Using a consistent, co-designed BigInteger ensures:

  • Java-compatible behavior: Both types follow the same semantics as Java's java.math.BigDecimal and java.math.BigInteger
  • Consistent internal representation: BigDecimal and BigInteger share implementation details for optimal performance
  • Cross-platform consistency: Results are identical whether running on .NET Framework, .NET Core, or .NET 8+

Internal Representation Differences

AspectDeveel.Math.BigIntegerSystem.Numerics.BigInteger
Internal formatSign + magnitude (separate sign field)Two's complement
Designed forIntegration with BigDecimalStandalone integer arithmetic
Java compatibilityYesNo
BigDecimal supportYes (native)No

When to Use Each Type

Use Deveel.Math.BigInteger When

ScenarioReason
Working with BigDecimalDeveel.Math.BigDecimal requires Deveel.Math.BigInteger internally
Java-compatible behavior neededResults must match Java's java.math package semantics
Using Deveel ecosystem librariesDeveelDB and other Deveel libraries expect Deveel.Math types
Primality testing requiredIsProbablePrime, NextProbablePrime, ProbablePrime are only available here
Modular inverse neededModInverse is only available here
Radix-based parsing (base 2-36)Parse(string, int radix) is only available here

Use System.Numerics.BigInteger When

ScenarioReason
Codebase already uses itAvoids unnecessary conversions
Platform-specific optimizations neededSystem.Numerics may have architecture-specific optimizations
Third-party library compatibilitySome libraries only accept System.Numerics.BigInteger

Converting Between BigInteger Types

Conversion Methods

DirectionMethodSyntaxNotes
Deveel → System.NumericsToSystemBigInteger()var sys = deveel.ToSystemBigInteger();Lossless
Deveel → System.NumericsImplicit operatorSystem.Numerics.BigInteger sys = deveel;Lossless
System.Numerics → DeveelFromSystemBigInteger()var deveel = BigInteger.FromSystemBigInteger(sys);Lossless
System.Numerics → DeveelExplicit castvar deveel = (BigInteger)sys;Lossless

Practical Example

// Working with a library that uses System.Numerics
var systemValue = SomeLibrary.ComputeLargeNumber(); // Returns System.Numerics.BigInteger

// Convert to Deveel.Math for BigDecimal operations
var deveelValue = BigInteger.FromSystemBigInteger(systemValue);
var decimalResult = new BigDecimal(deveelValue, 4); // Scale to 4 decimal places

// Convert back for the library
var systemResult = decimalResult.UnscaledValue.ToSystemBigInteger();
SomeLibrary.ProcessResult(systemResult);

Primitive Type Conversions

Primitives → BigInteger (Implicit)

Source TypeSyntaxAlways SucceedsExample
byteBigInteger x = (byte)255;Yes(byte)255255
sbyteBigInteger x = (sbyte)-128;Yes(sbyte)-128-128
shortBigInteger x = (short)-32768;Yes(short)-32768-32768
intBigInteger x = 42;Yes4242
longBigInteger x = 42L;Yes9223372036854775807L9223372036854775807

BigInteger → Primitives (Explicit)

Target TypeSyntaxOverflow BehaviorExample (BigInteger = 2642^{64} + 1)
short(short)bigIntTruncates to 16 bits (modulo 2162^{16})1
int(int)bigIntTruncates to 32 bits (modulo 2322^{32})1
long(long)bigIntTruncates to 64 bits (modulo 2642^{64})1

BigInteger → Floating Point (Implicit)

Target TypeSyntaxPrecision LimitExample (BigInteger = 2602^{60} + 123456789)
floatfloat f = bigInt;~7 significant digits1.152922E+18 (loses precision)
doubledouble d = bigInt;~15 significant digits1.1529215046069691E+18 (loses precision)
decimaldecimal dec = bigInt;Via ToInt64(); throws if > long.MaxValueThrows if value exceeds 64-bit range

Primitives → BigDecimal

Source TypeSyntaxExact?Notes
intnew BigDecimal(42)YesScale = 0
longnew BigDecimal(42L)YesScale = 0
doublenew BigDecimal(0.1)NoSee "Double Constructor Caveat" below
BigIntegernew BigDecimal(bigInt)YesScale = 0
BigIntegernew BigDecimal(bigInt, scale)YesExplicit scale

BigDecimal → Primitives

Target TypeMethodPrecision NotesExample (BigDecimal = 12345.6789)
doublebd.ToDouble()~15 significant digits12345.6789 (exact for this value)
floatbd.ToSingle()~7 significant digits12345.68 (rounded)
int(int)bd.UnscaledValueTruncates to 32 bits; ignores scale123456789 (not 12345!)
long(long)bd.UnscaledValueTruncates to 64 bits; ignores scale123456789 (not 12345!)

Important: BigDecimal has no direct conversion to int/long that respects scale. To get the integral part:

var bd = BigDecimal.Parse("123.45");
var integral = BigMath.DivideToIntegral(bd, BigDecimal.One);
long value = integral.UnscaledValue.ToInt64(); // 123

Double Constructor Caveat

The BigDecimal(double) constructor does not produce exact decimal values for most inputs because the double itself is already an approximation:

Inputnew BigDecimal(double) ResultBigDecimal.Parse(string) Result
0.10.10000000000000000555111512312578270211815834045410156250.1
0.20.2000000000000000111022302462515654042363166809082031250.2
0.30.2999999999999999888977697537484345957636833190917968750.3
1.11.1000000000000000888178419700125232338905334472656251.1

Recommendation: Always use BigDecimal.Parse(string) for exact decimal values.

Parsing Deep Dive

BigDecimal Parsing

Supported String Formats

FormatExampleResultScale
Plain decimal"123.456"123.4563
Scientific (positive exponent)"1.23E+10"123000000000
Scientific (negative exponent)"1.23e-5"0.00001237
Negative value"-42.50"-42.502
Leading zeros"007.00"7.002
Trailing zeros preserved"100.00"100.002

Parse Signatures

MethodSignatureDescription
Parse()BigDecimal.Parse(string s)Parses with invariant culture (period as decimal separator)
Parse()BigDecimal.Parse(string s, MathContext mc)Parses with rounding applied
Parse()BigDecimal.Parse(string s, IFormatProvider provider)Parses with culture-specific format
Parse()BigDecimal.Parse(char[] chars, int offset, int length)Parses from char array slice
TryParse()BigDecimal.TryParse(string s, out BigDecimal value)Safe parsing; returns false on failure
TryParse()BigDecimal.TryParse(string s, MathContext mc, out BigDecimal value)Safe parsing with rounding
TryParse()BigDecimal.TryParse(char[] chars, out BigDecimal value)Safe parsing from char array

BigInteger Parsing

Supported Radices

RadixNameValid CharactersExampleResult
2Binary0-1"101010"42
8Octal0-7"52"42
10Decimal0-9"42"42
16Hexadecimal0-9, a-f"2a" or "2A"42
36Base-360-9, a-z"16"`42$ (1 \times 36 + 6)

\text{Parse} \text{Signatures}

\text{Method}\text{Signature}\text{Description}
$Parse()`BigInteger.Parse(string s)Parses decimal string
Parse()BigInteger.Parse(string s, int radix)Parses in given base (2-36)
TryParse()BigInteger.TryParse(string s, out BigInteger value)Safe decimal parsing
TryParse()BigInteger.TryParse(string s, int radix, out BigInteger value)Safe parsing with radix

MathContext Parsing

MethodSignatureFormat
Parse()MathContext.Parse(string s)"precision=<n> roundingMode=<mode>"
TryParse()MathContext.TryParse(string s, out MathContext context)Same format; returns false on failure
ComponentValid ValuesCase Sensitivity
<n>Non-negative integerN/A
<mode>Up, Down, Ceiling, Floor, HalfUp, HalfDown, HalfEven, UnnecessaryCase-insensitive

Quick Reference: Conversion Summary

FromToSyntaxLossless?
intBigIntegerBigInteger x = 42;Yes
longBigIntegerBigInteger x = 42L;Yes
BigIntegerint(int)bigIntNo (truncates)
BigIntegerlong(long)bigIntNo (truncates)
BigIntegerdoubledouble d = bigInt;No (precision loss for large values)
Deveel.Math.BigIntegerSystem.Numerics.BigIntegervar sys = deveel;Yes
System.Numerics.BigIntegerDeveel.Math.BigIntegervar deveel = (BigInteger)sys;Yes
intBigDecimalnew BigDecimal(42)Yes
longBigDecimalnew BigDecimal(42L)Yes
doubleBigDecimalnew BigDecimal(0.1)No (use Parse("0.1"))
stringBigDecimalBigDecimal.Parse("0.1")Yes
BigDecimaldoublebd.ToDouble()No (precision loss for large values)

Example Usages

Type Conversion Chain

// Start with a primitive
int original = 42;

// Convert to BigInteger
BigInteger bigInt = original;

// Convert to BigDecimal with scale
BigDecimal bd = new BigDecimal(bigInt, 2); // 0.42

// Convert back to double
double backToDouble = bd.ToDouble(); // 0.42

Console.WriteLine($"Original: {original}");
Console.WriteLine($"BigDecimal: {bd}");
Console.WriteLine($"Back to double: {backToDouble}");

Safe Parsing with TryParse

// BigDecimal
if (BigDecimal.TryParse("123.456", out var bd))
{
    Console.WriteLine($"Parsed: {bd}");
}

// BigInteger with radix
if (BigInteger.TryParse("FF", 16, out var bi))
{
    Console.WriteLine($"Hex FF = {bi}"); // 255
}

// MathContext
if (MathContext.TryParse("precision=5 roundingMode=HalfUp", out var mc))
{
    Console.WriteLine($"Precision: {mc.Precision}"); // 5
}

String Formatting Support

BigDecimal Formatting

BigDecimal implements IFormattable and supports format specifiers in string interpolation:

SyntaxOutput Example (1234567.89)
$"{bd}" or $"{bd:G}"1234567.89
$"{bd:P}"1234567.89 (plain, no scientific notation)
$"{bd:E}"1.23456789E+6 (engineering notation)

Unsupported: Standard numeric formats like F2, N2, C are not recognized and will throw ArgumentException.

BigInteger Formatting

BigInteger does not implement IFormattable. String interpolation always produces decimal output:

SyntaxOutput Example (255)
$"{bi}"255
$"{bi:X}"255 (format specifier ignored, not hex!)

To get non-decimal output, use ToString(radix):

MethodOutput Example (255)
bi.ToString()255
bi.ToString(2)11111111
bi.ToString(16)ff
bi.ToString(36)73

Formatting Comparison

var bd = BigDecimal.Parse("1234567.89");
var bi = BigInteger.Parse("12345678901234567890");

// BigDecimal: full format support
Console.WriteLine($"BigDecimal: {bd}");       // 1234567.89
Console.WriteLine($"BigDecimal: {bd:E}");     // 1.23456789E+6

// BigInteger: decimal only in interpolation
Console.WriteLine($"BigInteger: {bi}");       // 12345678901234567890

// BigInteger: use ToString(radix) for other bases
Console.WriteLine($"BigInteger hex: {bi.ToString(16)}"); // ab5bc919d2e89402