Requirements

Run through this short checklist before installing so you do not chase environment issues later.

📦

.NET 9 SDK

BigFloat targets net9.0 in the library project file.

⚙️

Recent C# compiler

Use the SDK-installed compiler (C# 13 as of .NET 9) for best results.

💾

System.Numerics

BigInteger ships with .NET and powers the mantissa.

Check your toolchain

Run dotnet --list-sdks and confirm a 9.x entry is present before building. If you see only 8.x, install the .NET 9 SDK first.

Installation

Method 1: NuGet Package (Recommended)

The easiest way to add BigFloat to your project is via NuGet (current release: 4.0.0):

Using Package Manager Console:

Install-Package BigFloatLibrary

Using .NET CLI:

dotnet add package BigFloatLibrary

Using PackageReference in .csproj:

<PackageReference Include="BigFloatLibrary" Version="*" />

Use the wildcard when you want to pick up patch updates automatically; pin to 4.0.0 for reproducible builds.

Method 2: Source Code

For maximum control or to contribute to development:

Clone the repository:

git clone https://github.com/SunsetQuest/BigFloat.git

Add to your project:

  1. Copy the BigFloatLibrary folder to your solution
  2. Add a project reference in your .csproj file
  3. Optional: Include Constants.cs for mathematical constants

🔍 Verify your clone

From the repo root, run dotnet restore followed by dotnet build BigFloat.sln. This ensures your environment can build the library and run the bundled tests.

Your First BigFloat Program

Let's create a simple program that demonstrates BigFloat's precision advantages and the updated rounding helpers:

Bootstrap a console app
dotnet new console -n BigFloatDemo
cd BigFloatDemo
dotnet add package BigFloatLibrary
dotnet run

The dotnet new template defaults to net9.0 when the .NET 9 SDK is installed. If it falls back to net8.0, install the latest SDK and rerun the commands.

Program.cs
using System;
using BigFloatLibrary;

class Program
{
    static void Main()
    {
        // Problem: 0.1 + 0.2 != 0.3 in standard floating point
        double d1 = 0.1;
        double d2 = 0.2;
        double d3 = d1 + d2;
        Console.WriteLine($"Double: {d1} + {d2} = {d3}");
        Console.WriteLine($"Is 0.1 + 0.2 == 0.3? {d3 == 0.3}"); // False!
        
        // Solution: BigFloat maintains precision
        BigFloat b1 = new("0.1");
        BigFloat b2 = new("0.2");
        BigFloat b3 = b1 + b2;
        Console.WriteLine($"\nBigFloat: {b1} + {b2} = {b3}");
        Console.WriteLine($"Is 0.1 + 0.2 == 0.3? {b3 == new BigFloat(\"0.3\")}"); // True!

        // High precision calculation
        BigFloat pi = Constants.Fundamental.Pi;
        BigFloat radius = new("10.123456789");
        BigFloat area = pi * radius * radius;
        Console.WriteLine($"\nCircle area with radius {radius}:");
        Console.WriteLine($"Area = {area}");

        // Guard-bit–aware rounding (new in 2025)
        Console.WriteLine($"Nearest integer: {BigFloat.ToNearestInt(area)}");
    }
}

Expected Output:

Double: 0.1 + 0.2 = 0.30000000000000004
Is 0.1 + 0.2 == 0.3? False

BigFloat: 0.1 + 0.2 = 0.3
Is 0.1 + 0.2 == 0.3? True

Circle area with radius 10.123456789:
Area = 321.7526058894951
Nearest integer: 322

Basic Operations

Creating BigFloat Values

// From string (most precise)
BigFloat a = new("123.456789012345678901234567890");

// From double
BigFloat b = new(3.14159265358979323846);

// From integer
BigFloat c = new(42);

// From BigInteger
BigInteger bigInt = BigInteger.Parse("123456789012345678901234567890");
BigFloat d = new(bigInt);

// With specific precision
BigFloat e = BigFloat.IntWithAccuracy(10, 100); // 10 with 100 bits precision

Arithmetic Operations

BigFloat x = new("100.5");
BigFloat y = new("25.25");

// Basic arithmetic
BigFloat sum = x + y;        // 125.75
BigFloat diff = x - y;       // 75.25
BigFloat prod = x * y;       // 2537.625
BigFloat quot = x / y;       // 3.98...
BigFloat rem = x % y;        // 0.5

// Unary operations
BigFloat neg = -x;           // -100.5
BigFloat abs = BigFloat.Abs(x);  // 100.5

Comparisons

BigFloat a = new("100.1");
BigFloat b = new("100.2");

// Comparison operators
bool isEqual = a == b;       // false
bool isNotEqual = a != b;    // true
bool isLess = a < b;         // true
bool isGreater = a > b;      // false
bool isLessOrEqual = a <= b; // true
bool isGreaterOrEqual = a >= b; // false

// CompareTo method
int comparison = a.CompareTo(b); // -1 (a < b)

Type Conversions

BigFloat bf = new("123.456");

// Check if fits in standard type
if (bf.FitsInADouble())
{
    double d = (double)bf;
}

// Explicit conversions
int intValue = (int)bf;      // 123 (truncates)
long longValue = (long)bf;   // 123
decimal decValue = (decimal)bf; // 123.456

// To string with format
string str1 = bf.ToString();      // "123.456"
string str2 = bf.ToString("E");   // "1.23456E+2"
string str3 = bf.ToString("X");   // Hexadecimal

Understanding Precision

🔍 Key Concept: Guard Bits

BigFloat maintains 32 "guard bits" as hidden extra precision. These bits help maintain accuracy through chains of operations, preventing cumulative rounding errors.

You can see guard bits in action by formatting values that exceed the chosen precision—the trailing X characters indicate rounded-out digits.

Precision in Action

Precision Examples
// When you see output like "232XXXXXXXX", the X's indicate out-of-precision digits
BigFloat large = new("232000000000");
Console.WriteLine(large); // May display as "232XXXXXXXX" or "2.32e+11"

// Parsing with precision separator
// Format: "precise_part|guard_bits"
BigFloat precise = BigFloat.Parse("123.456|789");
Console.WriteLine($"Value: {precise}");

// Setting specific precision
BigFloat original = new("3.14159265358979323846264338327950288");
BigFloat rounded = BigFloat.SetPrecisionWithRound(original, 100); // 100 bits

// Extending precision (adds zeros, doesn't add information)
BigFloat extended = BigFloat.ExtendPrecision(original, 50);

⚠️ Important: Base-2 vs Base-10

BigFloat uses binary (base-2) internally. Most decimal numbers cannot be represented exactly in binary, leading to small precision differences:

  • 0.1 in decimal = 0.00011001100110011... (repeating) in binary
  • 0.25 in decimal = 0.01 (exact) in binary

📏 Constructor precision rule

All numeric constructors now follow the same split between in-precision bits and guard bits:

  • In-precision first: Take the requested binaryPrecision bits, clamped so no more than 32 source bits land in the guard region (for double, this enforces at least 21 in-precision bits).
  • Guard mapping: Any remaining source bits (up to 32) occupy the most significant guard bits; unused guard bits are zero.
  • Scaling and zeroes: binaryScaler simply shifts Scale/BinaryExponent. Zero inputs keep _size at 0 and encode the requested precision in Scale.

Defaults: double keeps 37 in-precision bits + 16 guard bits from the IEEE payload; float keeps 16 + 8; integers default to 31/63/64 in-precision bits (auto-widening to cover the full payload when it exceeds the default) with remaining guard bits zeroed; decimal adds 96 extra in-precision bits atop its 96-bit mantissa.

Accuracy Controls (New in 2025)

Precision-management APIs were refreshed in the 2025 releases. Prefer these helpers over the older ExtendPrecision/ReducePrecision calls (still present for backward compatibility):

Grow or shrink accuracy

BigFloat x = new("123.456");

// Add 64 working bits without changing the value
BigFloat wider = BigFloat.AdjustAccuracy(x, +64);

// Remove 32 bits and round before dropping them
BigFloat trimmed = BigFloat.AdjustPrecision(x, -32);

// Set an exact accuracy target (Scale + Precision)
BigFloat target = BigFloat.SetAccuracy(x, 256);
Console.WriteLine(target.Size); // includes 32 guard bits

Rounding and truncation

BigFloat value = new("9876.54321");

// Round to nearest whole number ignoring guard bits
int nearest = BigFloat.ToNearestInt(value);

// Preserve accuracy metadata when forcing an integer
BigFloat integer = value.TruncateToIntegerKeepingAccuracy();

// Reduce size with rounding at the cut point
BigFloat rounded = BigFloat.SetPrecisionWithRound(value, 80);

Parsing with precision hints

// "guard" bits after the pipe are interpreted as accuracy hints
BigFloat parsed = BigFloat.Parse("123.456|789");
Console.WriteLine(parsed.Scale);    // radix-point offset
Console.WriteLine(parsed.Size);     // mantissa + guard bits