Skip to main content

Numeric Evaluation

To obtain an exact numeric evaluation of an expression use expr.evaluate(). To obtain a numeric approximation use expr.N().

Parse + Numeric Free Function

For the common "parse then numeric approximation" flow, use the N() free function:

// import { N } from '@cortex-js/compute-engine'; console.log(N("\\sqrt{2}"));

The N() free function accepts either a LaTeX string or an Expression. It uses a shared ComputeEngine instance created on first call.

Exact Evaluation

An evaluation with expr.evaluate() preserves exact values.

Exact values are:

TypeExamples
Integers42, -1234
Rationals\nicefrac{3}{4}, -\nicefrac{11}{7}
Square Roots of Integers\sqrt{2}, -3\sqrt{5}
Constantse (ExponentialE), \pi (Pi)
console.log(parse('1/3 + 1/4').evaluate()); console.log(parse('\\sqrt{2} + \\sqrt{3} + \\sqrt{75}').evaluate());

Numeric Approximation

To force the evaluation of an expression to be a numeric approximation, use expr.N().

console.log(parse('1/3 + 1/4').N()); console.log(parse('\\sqrt{2} + \\sqrt{3} + \\sqrt{75}').N());

When using expr.evaluate(), if one of the arguments is not an exact value the expression is automatically evaluated as a numeric approximation.

console.log(parse('1/3 + 1/4 + 1.24').evaluate());

Angular Units

When a trigonometric function is given a unitless value, the Compute Engine interprets it using the current angular unit. Set it with ce.angularUnit (default: "rad").

Valid values: "rad", "deg", "grad", "turn".

ce.angularUnit = "deg"; console.log(ce.parse("\\cos 60").N()); // ➔ 0.5

JavaScript Interoperability

The result of expr.evaluate() and expr.N() is a expression.

The numericValue property of this expression is either a machine number (a JavaScript number), a NumericValue object or null if the expression is not a number.

While a NumericValue object can represent arbitrary precision numbers, for use with JavaScript, a reduced precision approximation can be accessed using the re (for the real part of the number) and im (for the imaginary part) properties.

const expr = parse('1/3 + 1/4');
console.log(expr.N().re);
// ➔ 0.5833333333333334

Another way to obtain a JavaScript compatible representation of an expression is to use the valueOf() method of the expression.

const expr = parse('1/3 + 1/4');
console.log(expr.N().valueOf());
// ➔ 0.5833333333333334

The valueOf() method of a expression can be used in JavaScript expressions.

const expr = parse('1/3 + 1/4'); console.log(expr.N().valueOf() + 1);

Unlike the .re property, valueOf() can also return a boolean or a string, depending on the value of the expression.

To get an Expression number literal from a JavaScript number, use ce.expr() or ce.number().

const expr = box(1.5); console.log(expr.valueOf());

Numeric Precision

The number of significant digits used in numeric calculations is controlled by the ce.precision property of the ComputeEngine instance.

ce.precision = 30; console.log(ce.parse('\\pi').N().json);

The default precision is 21.

To set the precision to the default value, use ce.precision = "auto".

To request a specific precision for a single evaluation, use the N function with a precision argument: ["N", expr, digits].

console.log(ce.box(['N', 'Pi', 50]).evaluate().toString());

If the requested precision is greater than ce.precision, the working precision is raised to match and kept raised (display precision is global). If it is at or below ce.precision, the result is rounded to that many significant digits without changing the working precision.

Machine Precision

If the precision is 15 or less, the Compute Engine uses a 64-bit binary floating point format for its internal calculations.

This format is implemented in hardware and well suited to do fast computations. It uses a fixed amount of memory and represents significant digits in base-2 with about 15 digits of precision and with a minimum value of \pm5\times 10^{-324} and a maximum value of \pm1.7976931348623157\times 10^{+308}

To change the precision to machine precision, use ce.precision = "machine".

With this precision, some calculations that have a value very close to 0 may return 0 and some calculations that have a value greater than the maximum value representable by a machine number may return \pm\infty.

warning

Some numeric evaluations using machine numbers cannot produce exact results.

ce.precision = 'machine';
console.log(ce.parse('0.1 + 0.2').N().json);
// ➔ "0.30000000000000004"

While 0.1 and 0.2 look like "round numbers" in base-10, they can only be represented by an approximation in base-2, which introduces cascading errors when manipulating them.

Arbitrary Precision

If ce.precision is greater than 15, numbers are represented as bignum numbers, a string of base-10 digits and an exponent.

Bignum numbers have a minimum value of \pm 10^{-9\,000\,000\,000\,000\,000} and a maximum value of \pm9.99999\ldots \times 10^{+9\,000\,000\,000\,000\,000}.

ce.precision = 21;
console.log(ce.parse('0.1 + 0.2').N().json);
// ➔ "0.3"

Trigonometric operations are accurate for precision up to 1,000.

Serialization and Precision

The precision property controls how computations are performed. It also affects how results are displayed:

  • .latex and .toString() round numeric output to ce.precision significant digits. This hides noise digits that arise from internal rounding in division and transcendental functions.
  • .json and toJSON() emit the full unrounded value for lossless data interchange. Use expr.toMathJson({ fractionalDigits: 'auto' }) to get precision-rounded MathJSON output.

To explicitly control digits in MathJSON output, use expr.toMathJson() with the fractionalDigits option:

expr.toMathJson({ fractionalDigits: 'auto' }); // rounded to ce.precision
expr.toMathJson({ fractionalDigits: 'max' }); // all digits (default)
expr.toMathJson({ fractionalDigits: 5 }); // 5 fractional digits

When the precision is greater than 15, the return value of expr.N().json may be a MathJSON number with a num string containing all available digits:

{
"num": "3.14159265358979323846264338327950288419716939937510"
}
Note

The .json output may contain more digits than ce.precision because some arithmetic operations (addition, subtraction, multiplication) are exact and preserve all digits. Digits beyond the working precision are not guaranteed to be accurate. For display purposes, use .latex or .toString() which automatically round to the working precision.

Repeated Evaluation

To repeatedly evaluate an expression use ce.assign() to change the value of variables. ce.assign() changes the value associated with one or more variables in the current scope.

ce.assign() accepts booleans, numbers, bigints, expressions, or functions. Use undefined to clear a value.

const expr = ce.parse("3x^2+4x+2"); for (let x = 0; x < 1; x += 0.01) { ce.assign('x', x); console.log(`f(${x}) = ${expr.evaluate()}`); }

You can also use expr.subs(), but this will create a brand new expression on each iteration, and will be much slower.

const expr = ce.parse("3x^2+4x+2"); for (let x = 0; x < 1; x += 0.01) { console.log(`f(${x}) = ${expr.subs({ x }).evaluate()}`); }

To reset a variable to be unbound to a value use ce.assign()

ce.assign("x", undefined); console.log(ce.parse("3x^2+4x+2").N()); // ➔ "3x^2+4x+2"

To change the value of a variable set its value property:

ce.symbol("x").value = 5;

ce.symbol("x").value = undefined;

Compiling

If performance is important, the expression can be compiled to a JavaScript function.

To get a compiled version of an expression use the compile() function:

import { compile } from '@cortex-js/compute-engine';

const result = compile("3x^2+4x+2");
for (const x = 0; x < 1; x += 0.01) console.log(result.run({ x }));
Note

The syntax {x} is a shortcut for {"x": x}, in other words it defines an argument named "x" (which is used in the definition of the expression expr) with the value of the JavaScript variable x (which is used in the for loop).

This will usually result in a much faster evaluation than using expr.N() but this approach has some limitations.

Simplifying Before Evaluating

When using expr.N(), no rewriting of the expression is done before it is evaluated.

Because of the limitations of machine numbers, this may produce surprising results.

For example:

ce.precision = "machine";
const x = ce.parse("0.1 + 0.2").N();
console.log(ce.expr(["Subtract", x, x]).N());
// ➔ 2.7755575615628914e-17

However, the result of x - x from ce.simplify() is 0 since the simplification is done symbolically, before any floating point calculations are made.

const x = ce.parse('0.1 + 0.2').N();
console.log(ce.parse('x - x').simplify());
// ➔ 0

In some cases, it may be advantageous to invoke expr.simplify() before using expr.N().

Tolerance

Two numbers that are sufficiently close to each other are considered equal.

To control how close two numbers have to be before they are considered equal, set the tolerance property of a ComputeEngine instance.

By default, the tolerance is 10^{-10}.

The tolerance is accounted for by the Chop function to determine when to replace a number of a small magnitude with the exact integer 0.

It is also used when doing some comparison to zero: a number whose absolute value is smaller than the tolerance will be considered equal to 0.

Numeric Functions

The topics below from the MathJSON Standard Library can provide numeric evaluations for their numeric functions:

TopicSymbols/Functions
ArithmeticAdd Multiply Power Exp Log ExponentialE ImaginaryUnit...
CalculusDerivative Integrate...
ComplexReal Conjugate, ComplexRoots...
Special FunctionsGamma Factorial...
StatisticsStandardDeviation Mean Erf...
TrigonometryPi Cos Sin Tan...