FormulaEngineer 1.0.1

dotnet add package FormulaEngineer --version 1.0.1
                    
NuGet\Install-Package FormulaEngineer -Version 1.0.1
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="FormulaEngineer" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="FormulaEngineer" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="FormulaEngineer" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add FormulaEngineer --version 1.0.1
                    
#r "nuget: FormulaEngineer, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package FormulaEngineer@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=FormulaEngineer&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=FormulaEngineer&version=1.0.1
                    
Install as a Cake Tool

FormulaEngineer

A high-performance .NET library that reads, writes, and accurately evaluates Excel formulas — without Excel installed.

NuGet Downloads Website


What FormulaEngineer does

Most .NET Excel libraries can open and create .xlsx files. Very few can evaluate the formulas inside them correctly. FormulaEngineer is built around that gap: a from-scratch formula engine that matches Excel's output to last-bit floating-point precision.

  • Accurate formula evaluation — verified against hundreds of real-world formulas; where Excel's cached values exist in the source workbooks, results match bit-for-bit
  • 250+ Excel functions — lookup, financial, statistical, math, text, date/time, logical, information, and database families
  • Handles the hard cases — dynamic arrays, structured table references, cross-sheet INDIRECT, spill semantics (#SPILL! / #CALC!), full error propagation
  • Reads and writes formulas — write formulas into cells, then read them back and evaluate them with no Excel involved
  • Ultra-fast streaming reader and writer — forward-only, single-enumeration, flat memory profile regardless of file size
  • Cross-platform — pure managed code for .NET 6 and later; Windows, Linux, macOS. No Excel installation, no COM interop.

Install

dotnet add package FormulaEngineer

Targets net6.0 and later.


A note on formula strings

When you create a formula with ExcelFormula.From(...), pass the formula without a leading =. Write SUM(A1:A3), not =SUM(A1:A3).


Activating a license

During the 7-day trial period, there is no need to call LicenseManager.ActivateFromFile("license.lic");.

For paid Pro or Enterprise licenses, activate your .lic file like this:

using FormulaEngineer.Licensing;

LicenseManager.ActivateFromFile("license.lic");

Writing formulas

Pass an ExcelFormula into WriteRow to write a formula cell. Each example below writes a workbook; the matching example in the next section reads it back and evaluates it.

Example 1 — SUM

Writes three numbers and a SUM formula over them.

using FormulaEngineer.Api;
using FormulaEngineer.Licensing;

LicenseManager.ActivateFromFile("license.lic");

using (var writer = new ExcelWriter("sum.xlsx"))
{
    using (var sheet = writer.CreateWorksheet("Data"))
    {
        sheet.WriteRow(10);                              // A1
        sheet.WriteRow(20);                              // A2
        sheet.WriteRow(30);                              // A3
        sheet.WriteRow(ExcelFormula.From("SUM(A1:A3)")); // A4 — the formula
    }

    writer.Close();
}

The formula cell is in row 4, column 1. It sums 10 + 20 + 30.

Example 2 — Cell arithmetic

Writes two numbers and a formula that multiplies them.

using FormulaEngineer.Api;
using FormulaEngineer.Licensing;

LicenseManager.ActivateFromFile("license.lic");

using (var writer = new ExcelWriter("multiply.xlsx"))
{
    using (var sheet = writer.CreateWorksheet("Data"))
    {
        // A1 = 7, B1 = 6, C1 = the formula
        sheet.WriteRow(7, 6, ExcelFormula.From("A1*B1"));
    }

    writer.Close();
}

The formula cell is in row 1, column 3. It multiplies 7 * 6.

Example 3 — Conditional logic (medium)

Writes a header row, a score, and an IF formula that returns text.

using FormulaEngineer.Api;
using FormulaEngineer.Licensing;

LicenseManager.ActivateFromFile("license.lic");

using (var writer = new ExcelWriter("conditional.xlsx"))
{
    using (var sheet = writer.CreateWorksheet("Scores"))
    {
        sheet.WriteRow("Score", "Result");                                      // A1, B1
        sheet.WriteRow(135, ExcelFormula.From("IF(A2>100, \"High\", \"Low\")")); // A2 = 135, B2 = formula
    }

    writer.Close();
}

The formula cell is in row 2, column 2. Since 135 > 100, it returns the text "High".

Example 4 — VLOOKUP over a table (difficult)

Writes a three-row lookup table and a VLOOKUP formula against it.

using FormulaEngineer.Api;
using FormulaEngineer.Licensing;

LicenseManager.ActivateFromFile("license.lic");

using (var writer = new ExcelWriter("lookup.xlsx"))
{
    using (var sheet = writer.CreateWorksheet("Regions"))
    {
        sheet.WriteRow("EMEA", 1_240_000);  // A1, B1
        sheet.WriteRow("APAC", 2_180_000);  // A2, B2
        sheet.WriteRow("AMER", 3_950_000);  // A3, B3
        sheet.WriteRow(ExcelFormula.From("VLOOKUP(\"APAC\", A1:B3, 2, FALSE)")); // A4 — the formula
    }

    writer.Close();
}

The formula cell is in row 4, column 1. It looks up "APAC" in the table and returns the value from the second column.

Example 5 — Multiple formulas in one sheet (advanced)

Writes an invoice sheet where formulas work together — line totals feed a subtotal, which feeds the tax and total, which the status formula tests.

using FormulaEngineer.Api;
using FormulaEngineer.Licensing;

LicenseManager.ActivateFromFile("license.lic");

using (var writer = new ExcelWriter("invoice.xlsx"))
{
    using (var sheet = writer.CreateWorksheet("Invoice"))
    {
        sheet.WriteRow("Item", "Qty", "Unit Price", "Line Total");      // row 1 — header
        sheet.WriteRow("Widget", 4, 25.00, ExcelFormula.From("B2*C2")); // row 2
        sheet.WriteRow("Gadget", 2, 80.00, ExcelFormula.From("B3*C3")); // row 3
        sheet.WriteRow("Cable",  10, 3.50, ExcelFormula.From("B4*C4")); // row 4
        sheet.WriteRow("Subtotal", "", "", ExcelFormula.From("SUM(D2:D4)"));                              // row 5
        sheet.WriteRow("Tax (8%)", "", "", ExcelFormula.From("D5*0.08"));                                 // row 6
        sheet.WriteRow("Total",    "", "", ExcelFormula.From("D5+D6"));                                   // row 7
        sheet.WriteRow("Status",   "", "", ExcelFormula.From("IF(D7>200,\"Large order\",\"Standard\")")); // row 8
    }

    writer.Close();
}

The line totals (B*C) feed the subtotal (SUM), which feeds the tax line and the total, which the status formula then tests. The formula cells are all in column 4, rows 2 through 8.


Reading and evaluating formulas

Each example opens the workbook written above, reads the formula cell to get its formula text, then evaluates it — printing both. The formula text is read first with one worksheet reader; the evaluation uses a separate reader afterwards.

Example 1 — SUM

using FormulaEngineer.Api;
using FormulaEngineer.Licensing;

LicenseManager.ActivateFromFile("license.lic");

using var reader    = new ExcelReader("sum.xlsx");
using var evaluator = new ExcelFormulaEvaluator();

// Read the formula text (row 4, column 1)
string formulaText;
using (var sheet = reader.OpenWorksheet("Data"))
    formulaText = ((ExcelFormula)sheet.ReadRows().Last()[0]!).FormulaText;

// Evaluate it
object? result;
using (var sheet = reader.OpenWorksheet("Data"))
    result = evaluator.EvaluateCell(sheet, row: 4, col: 1);

Console.WriteLine($"Formula:  {formulaText}");   // SUM(A1:A3)
Console.WriteLine($"Result:   {result}");        // 60

Example 2 — Cell arithmetic

using FormulaEngineer.Api;
using FormulaEngineer.Licensing;

LicenseManager.ActivateFromFile("license.lic");

using var reader    = new ExcelReader("multiply.xlsx");
using var evaluator = new ExcelFormulaEvaluator();

// Read the formula text (row 1, column 3)
string formulaText;
using (var sheet = reader.OpenWorksheet("Data"))
    formulaText = ((ExcelFormula)sheet.ReadRows().First()[2]!).FormulaText;

// Evaluate it
object? result;
using (var sheet = reader.OpenWorksheet("Data"))
    result = evaluator.EvaluateCell(sheet, row: 1, col: 3);

Console.WriteLine($"Formula:  {formulaText}");   // A1*B1
Console.WriteLine($"Result:   {result}");        // 42

Example 3 — Conditional logic (medium)

using FormulaEngineer.Api;
using FormulaEngineer.Licensing;

LicenseManager.ActivateFromFile("license.lic");

using var reader    = new ExcelReader("conditional.xlsx");
using var evaluator = new ExcelFormulaEvaluator();

// Read the formula text (row 2, column 2)
string formulaText;
using (var sheet = reader.OpenWorksheet("Scores"))
    formulaText = ((ExcelFormula)sheet.ReadRows().Last()[1]!).FormulaText;

// Evaluate it
object? result;
using (var sheet = reader.OpenWorksheet("Scores"))
    result = evaluator.EvaluateCell(sheet, row: 2, col: 2);

Console.WriteLine($"Formula:  {formulaText}");   // IF(A2>100, "High", "Low")
Console.WriteLine($"Result:   {result}");        // High

Example 4 — VLOOKUP over a table (difficult)

using FormulaEngineer.Api;
using FormulaEngineer.Licensing;

LicenseManager.ActivateFromFile("license.lic");

using var reader    = new ExcelReader("lookup.xlsx");
using var evaluator = new ExcelFormulaEvaluator();

// Read the formula text (row 4, column 1)
string formulaText;
using (var sheet = reader.OpenWorksheet("Regions"))
    formulaText = ((ExcelFormula)sheet.ReadRows().Last()[0]!).FormulaText;

// Evaluate it
object? result;
using (var sheet = reader.OpenWorksheet("Regions"))
    result = evaluator.EvaluateCell(sheet, row: 4, col: 1);

Console.WriteLine($"Formula:  {formulaText}");   // VLOOKUP("APAC", A1:B3, 2, FALSE)
Console.WriteLine($"Result:   {result}");        // 2180000

Example 5 — Multiple formulas in one sheet (advanced)

using FormulaEngineer.Api;
using FormulaEngineer.Licensing;

LicenseManager.ActivateFromFile("license.lic");

using var reader    = new ExcelReader("invoice.xlsx");
using var evaluator = new ExcelFormulaEvaluator();

// Read the formula text from every row (formula cells are in column 4)
List<string> formulaTexts;
using (var sheet = reader.OpenWorksheet("Invoice"))
    formulaTexts = sheet.ReadRows()
        .Where(row => row.Length > 3 && row[3] is ExcelFormula)
        .Select(row => ((ExcelFormula)row[3]!).FormulaText)
        .ToList();

// Evaluate the formula cell in each row (rows 2 through 8, column 4)
List<object?> results;
using (var sheet = reader.OpenWorksheet("Invoice"))
    results = Enumerable.Range(2, 7)
        .Select(rowNum => evaluator.EvaluateCell(sheet, row: rowNum, col: 4))
        .ToList();

for (int i = 0; i < formulaTexts.Count; i++)
    Console.WriteLine($"Formula:  {formulaTexts[i],-40}  Result:  {results[i]}");

// Formula:  B2*C2                                     Result:  100
// Formula:  B3*C3                                     Result:  160
// Formula:  B4*C4                                     Result:  35
// Formula:  SUM(D2:D4)                                Result:  295
// Formula:  D5*0.08                                   Result:  23.6
// Formula:  D5+D6                                     Result:  318.6
// Formula:  IF(D7>200,"Large order","Standard")       Result:  Large order

You can also evaluate an entire sheet at once with evaluator.EvaluateSheet(sheet), which returns each row with formula cells replaced by their computed values.


The formula engine

The engine has been verified against hundreds of formulas drawn from production-style workbooks — covering the lookup, financial, statistical, aggregate, date/time, text, logical, and information families. Where Excel's cached values exist in the source files, the evaluator matches them bit-for-bit. Floating-point arithmetic follows IEEE 754 exactly, including the canonical edge cases Excel itself produces.

Function families

  • LookupLOOKUP, VLOOKUP, HLOOKUP, XLOOKUP, INDEX, MATCH — including wildcards, case-sensitive matching, reverse search, multi-criteria, and structured table references
  • Dynamic arraysFILTER, SEQUENCE, TRANSPOSE, SORT, UNIQUE, spill semantics, array-to-array concatenation, boolean-to-numeric coercion
  • FinancialPMT, IPMT, PPMT, IRR, XIRR, MIRR, NPV, XNPV, PV, FV, RATE, NPER, and more — verified to last-ULP precision across every compounding frequency
  • Date and timeDATE, EDATE, EOMONTH, YEAR, MONTH, DAY, TODAY, NOW, WORKDAY, NETWORKDAYS, YEARFRAC — with correct leap-year handling and end-of-month clamping
  • AggregateSUM, AVERAGE, COUNT, COUNTA, COUNTIF, COUNTIFS, SUMIF, SUMIFS, AVERAGEIF, AVERAGEIFS, SUMPRODUCT, AGGREGATE, SUBTOTAL
  • Logical and informationIF, IFS, AND, OR, NOT, XOR, IFERROR, IFNA, SWITCH, ISBLANK, ISERR, ISNA, ISNUMBER, ISEVEN, ISODD, TYPE, ERROR.TYPE, CELL
  • TextCONCAT, CONCATENATE, TEXTJOIN, TEXT, LEFT, MID, RIGHT, LEN, SEARCH, FIND, SUBSTITUTE, REPLACE, EXACT, VALUE
  • Reference — named ranges (workbook- and sheet-scoped), cross-sheet references, structured table references (Table[Col], Table[#Headers], Table[]), INDIRECT with scalar or array arguments, OFFSET
  • DatabaseDSUM, DCOUNT, DAVERAGE, DMAX, DMIN, DGET, and the rest of the D-function family

FormulaEngineer also handles the patterns that break most non-Microsoft formula engines — INDIRECT fed an array of sheet names, header-driven nested XLOOKUP that returns an entire column dynamically, FILTER + SEQUENCE for Nth-occurrence lookups, the CHOOSE-virtual-table case-sensitive VLOOKUP trick, and matrix lookups with composite headers — all evaluated correctly against real workbooks.


Reading and writing plain workbooks

FormulaEngineer is also a fast streaming reader and writer for ordinary (non-formula) spreadsheets.

Writing a workbook

using FormulaEngineer.Api;
using FormulaEngineer.Licensing;

LicenseManager.ActivateFromFile("license.lic");

using (var writer = new ExcelWriter("report.xlsx"))
{
    using (var sheet = writer.CreateWorksheet("Q4"))
    {
        sheet.WriteRow("Region", "Revenue", "Growth");
        sheet.WriteRow("EMEA", 1_240_000, 0.18);
        sheet.WriteRow("APAC", 2_180_000, 0.32);
        sheet.WriteRow("AMER", 3_950_000, 0.14);
    }

    writer.Close();
}

Forward-only, one active worksheet at a time. WriteRows, SkipRows, SkipColumns, MergeCells, and named ranges are all supported.

Reading a workbook

using FormulaEngineer.Api;
using FormulaEngineer.Licensing;

LicenseManager.ActivateFromFile("license.lic");

using var reader = new ExcelReader("report.xlsx");

foreach (var sheetName in reader.WorksheetNames)
{
    using var sheet = reader.OpenWorksheet(sheetName);

    foreach (object?[] row in sheet.ReadRows())
    {
        for (int i = 0; i < row.Length; i++)
            Console.WriteLine(row[i]);
    }
}

Worksheets are single-enumeration — read top to bottom, once. The reader streams; memory stays flat regardless of file size.


Licensing

FormulaEngineer includes a built-in 7-day trial. No license required to evaluate.

Tier Use case
Trial 7 days, full API, throttled request counts per capability
Pro Production use
Enterprise Production use with extended terms

Above the trial caps, each subsequent request incurs a short delay. Paid licenses bypass throttling entirely.

To purchase a Pro or Enterprise license, visit nasvelocityexcel.com/pricing.

Getting a license

Each license is bound to a machine fingerprint. To obtain yours, visit nasvelocityexcel.com and use the machine hash calculator, then include the hash with your purchase. Your .lic file is issued for that machine.

Activation

using FormulaEngineer.Licensing;

// Activate from a .lic file
LicenseManager.ActivateFromFile("license.lic");

// Check licensing state
if (!LicenseManager.IsLicensed())
{
    int trialDays = LicenseManager.GetRemainingTrialDays();
    Console.WriteLine($"Not licensed. Trial days remaining: {trialDays}");
}

Requirements

  • .NET 6 or later
  • Network access for trial registration and license validation

License

FormulaEngineer is proprietary software. Refer to the LICENSE file for full terms.



© 2026 Mohd Nasrullah Kazmi. All rights reserved.

Product Compatible and additional computed target framework versions.
.NET net6.0 is compatible.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 93 5/26/2026
1.0.0 116 5/15/2026