mathjs vs ndarray vs numeric
前端数值计算与科学计算库选型指南
mathjsndarraynumeric类似的npm包:

前端数值计算与科学计算库选型指南

mathjsndarraynumeric 都是用于 JavaScript 环境中的数值计算工具库,但它们在设计目标、API 风格和适用场景上有显著差异。mathjs 是一个功能全面的数学表达式解析与计算库,支持符号运算、单位换算和矩阵操作,适合需要灵活数学表达能力的应用。ndarray 提供了类似 NumPy 的多维数组结构,专注于高性能的数组操作和内存布局控制,常用于图像处理或科学可视化。numeric 是一个较早期的数值计算库,提供线性代数、优化和统计等基础算法,但目前已不再积极维护。

npm下载趋势

3 年

GitHub Stars 排名

统计详情

npm包名称
下载量
Stars
大小
Issues
发布时间
License
mathjs015,0649.43 MB2014 个月前Apache-2.0
ndarray01,243-227 年前MIT
numeric01,442-6914 年前-

mathjs vs ndarray vs numeric:前端数值计算库深度对比

在构建涉及数学计算、数据处理或科学可视化的前端应用时,开发者常面临一个关键问题:该用哪个 JavaScript 数值库?mathjsndarraynumeric 各有特色,但它们的适用边界非常不同。本文从真实开发场景出发,深入比较三者的核心能力、API 设计和工程权衡。

⚠️ 维护状态警示

首先明确一点:numeric 已不再活跃维护。其 GitHub 仓库(github.com/sloisel/numeric)最后一次重要提交在 2016 年,npm 包也无近期更新。官方未标记为“deprecated”,但社区普遍认为它已过时。新项目应避免使用 numeric,本文将其纳入仅为历史参考和迁移对比。

🧮 核心定位与典型用途

mathjs:全功能数学引擎

mathjs 的核心优势在于 表达式解析 + 符号计算 + 单位系统。它不仅能执行数值运算,还能理解 'sin(45 deg) + 2 kg to g' 这样的字符串,并返回带单位的结果。适合:

  • 在线计算器、公式编辑器
  • 工程单位自动换算(如 CAD 工具)
  • 教育类产品中的交互式数学
// mathjs: 表达式解析与单位换算
import { evaluate, unit } from 'mathjs';

const result1 = evaluate('sqrt(16) + sin(pi / 4)'); // ≈ 5.707
const result2 = unit('2 kg').to('g'); // 2000 g

ndarray: 多维数组基础设施

ndarray 的目标是 提供类似 NumPy 的内存高效多维数组。它本身不包含高级数学函数,而是通过配套库(如 ndarray-ops)实现逐元素操作。适合:

  • 图像/视频帧处理(如 Canvas/WebGL 前处理)
  • 科学数据可视化(如热力图、3D 体渲染)
  • 需要共享内存或子数组视图的场景
// ndarray: 创建二维数组并操作
import ndarray from 'ndarray';
import ops from 'ndarray-ops';

const arr = ndarray(new Float32Array(12), [3, 4]); // 3x4 矩阵
ops.mulseq(arr, 2); // 所有元素 *= 2(原地操作)
console.log(arr.get(1, 2)); // 访问 [1][2] 元素

numeric(已过时):基础数值算法集合

numeric 曾提供简洁的线性代数、优化和统计函数,但 API 设计较为陈旧,且无类型定义支持。例如:

// numeric (不推荐新项目使用)
// const x = numeric.solve(A, b); // 求解线性方程组
// const min = numeric.uncmin(f, x0); // 无约束优化

由于缺乏维护,任何依赖 numeric 的新代码都应视为技术债务

🔢 矩阵与线性代数操作对比

假设我们需要创建一个 2x2 矩阵并计算其行列式。

mathjs:高层抽象,支持链式调用

import { matrix, det } from 'mathjs';

const m = matrix([[1, 2], [3, 4]]);
const determinant = det(m); // -2
// 也支持直接传数组:det([[1, 2], [3, 4]])

ndarray:需组合多个库,但内存可控

import ndarray from 'ndarray';
import det from 'ndarray-determinant'; // 需单独安装

const m = ndarray(new Float64Array([1, 2, 3, 4]), [2, 2]);
const determinant = det(m); // -2

numeric(过时):简洁但无扩展性

// const m = [[1, 2], [3, 4]];
// const determinant = numeric.det(m); // -2

💡 关键区别:mathjs 内置了完整的线性代数模块;ndarray 将功能拆分为微库,灵活性高但需手动集成;numeric 虽简洁但已无法获得安全更新。

📐 性能与内存模型

  • mathjs:默认使用普通 JavaScript 数组,对大型矩阵效率较低。虽支持 SparseMatrix,但密集计算场景非其强项。
  • ndarray:直接包装 TypedArray,支持 strided memory layout(如转置矩阵无需复制数据),适合高频操作。
  • numeric:基于普通数组,无内存优化,且多年未更新性能瓶颈。

例如,对 1000x1000 矩阵做逐元素乘法:

// mathjs(较慢,因对象封装开销)
const a = math.matrix(...);
const b = math.multiply(a, 2);

// ndarray(快,原地操作)
const a = ndarray(typedArray, [1000, 1000]);
ops.mulseq(a, 2); // 直接修改内存

🌐 与现代前端生态兼容性

  • TypeScript 支持mathjsndarray 均提供官方类型定义;numeric 无官方支持。
  • Tree-shakingmathjs 支持按需导入(如 import { add } from 'mathjs/function/arithmetic/add'),但配置复杂;ndarray 因微库设计天然支持摇树;numeric 为单文件,无法分割。
  • ESM 支持:三者均支持,但 numeric 的模块格式较旧。

🛠️ 错误处理与调试体验

  • mathjs:抛出带上下文信息的错误(如 'Dimension mismatch (2x3 != 3x2)'),便于调试。
  • ndarray:错误信息较底层(如索引越界直接报 undefined),需开发者自行验证。
  • numeric:错误信息简略,且多年未改进。

📊 何时选择哪个?

场景推荐库理由
用户输入数学公式(如 "2x + 3 = 7"mathjs唯一支持表达式解析和符号计算
单位换算(如 "5 ft to cm"mathjs内置单位系统,覆盖 200+ 单位
图像/视频帧处理ndarray内存高效,支持子区域视图
科学数据可视化(如 Plotly.js 前处理)ndarray与 D3、Regl 等库生态兼容
简单线性代数(小矩阵)mathjs无需额外依赖,API 直观
高性能大规模数值计算考虑 WebAssembly 方案三者均非最优,可评估 onnxruntime-webtensorflow.js
新项目中的数值计算避免 numeric维护状态风险高

💡 最终建议

  • 教育/工程类应用 → 选 mathjs。它的表达式解析和单位系统能极大简化业务逻辑。
  • 图形/可视化密集型应用 → 选 ndarray。它的内存模型和生态工具链专为多维数据设计。
  • 任何新项目避开 numeric。即使它看起来“够用”,长期维护成本和安全风险远超初期便利。

记住:没有“最好”的库,只有“最合适”当前场景的工具。明确你的核心需求(是解析公式?还是处理像素?),再做选择。

如何选择: mathjs vs ndarray vs numeric

  • mathjs:

    选择 mathjs 如果你需要在应用中支持用户输入的数学表达式(如计算器、公式编辑器)、单位换算、复数或高精度计算。它提供了丰富的内置函数和可扩展的表达式解析器,适合教育类、工程类或数据分析类前端产品。但注意其体积较大,若仅需简单矩阵运算可能过于重量级。

  • ndarray:

    选择 ndarray 如果你正在处理多维数据(如图像像素、3D 体数据)并需要高效的切片、视图和原地操作。它与 cwisendarray-ops 等生态库配合良好,适合 WebGL 前处理、科学可视化或需要精细控制内存布局的场景。但它不提供高级数学函数(如求解方程),需自行组合底层操作。

  • numeric:

    不要在新项目中使用 numeric。根据其 GitHub 仓库和 npm 页面信息,该项目自 2016 年后已无实质性更新,且官方未声明长期维护计划。虽然它曾提供简洁的线性代数接口,但缺乏现代 TypeScript 支持、性能优化和安全更新,建议优先评估 mathjs 或基于 WebAssembly 的替代方案。

mathjs的README

math.js

https://mathjs.org

Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser with support for symbolic computation, comes with a large set of built-in functions and constants, and offers an integrated solution to work with different data types like numbers, big numbers, complex numbers, fractions, units, and matrices. Powerful and easy to use.

Version Downloads Build Status Maintenance License FOSSA Status Codecov Github Sponsor

Features

  • Supports numbers, bignumbers, bigints, complex numbers, fractions, units, strings, arrays, and matrices.
  • Is compatible with JavaScript's built-in Math library.
  • Contains a flexible expression parser.
  • Does symbolic computation.
  • Comes with a large set of built-in functions and constants.
  • Can be used as a command line application as well.
  • Runs on any JavaScript engine.
  • Is easily extensible.
  • Open source.

Usage

Math.js can be used in both node.js and in the browser.

Install math.js using npm:

npm install mathjs

Or download mathjs via one of the CDN's listed on the downloads page:

    https://mathjs.org/download.html

Math.js can be used similar to JavaScript's built-in Math library. Besides that, math.js can evaluate expressions and supports chained operations.

import {
  atan2, chain, derivative, e, evaluate, log, pi, pow, round, sqrt
} from 'mathjs'

// functions and constants
round(e, 3)                    // 2.718
atan2(3, -3) / pi              // 0.75
log(10000, 10)                 // 4
sqrt(-4)                       // 2i
pow([[-1, 2], [3, 1]], 2)      // [[7, 0], [0, 7]]
derivative('x^2 + x', 'x')     // 2 * x + 1

// expressions
evaluate('12 / (2.3 + 0.7)')   // 4
evaluate('12.7 cm to inch')    // 5 inch
evaluate('sin(45 deg) ^ 2')    // 0.5
evaluate('9 / 3 + 2i')         // 3 + 2i
evaluate('det([-1, 2; 3, 1])') // -7

// chaining
chain(3)
    .add(4)
    .multiply(2)
    .done()  // 14

See the Getting Started for a more detailed tutorial.

Browser support

Math.js works on any ES2020 compatible JavaScript engine, including node.js, Chrome, Firefox, Safari, and Edge.

Documentation

Build

First clone the project from github:

git clone git@github.com:josdejong/mathjs.git
cd mathjs

Install the project dependencies:

npm install

Then, the project can be build by executing the build script via npm:

npm run build

This will build ESM output, CommonJS output, and the bundle math.js from the source files and put them in the folder lib.

Develop

When developing new features for mathjs, it is good to be aware of the following background information.

Code

The code of mathjs is written in ES modules, and requires all files to have a real, relative path, meaning the files must have a *.js extension. Please configure adding file extensions on auto import in your IDE.

Architecture

What mathjs tries to achieve is to offer an environment where you can do calculations with mixed data types, like multiplying a regular number with a Complex number or a BigNumber, and work with all of those in matrices. Mathjs also allows to add a new data type with little effort.

The solution that mathjs uses has two main ingredients:

  • Typed functions. All functions are created using typed-function. This makes it easier to (dynamically) create and extend a single function with new data types, automatically do type conversions on function inputs, etc. So, if you create function multiply for two numbers, you can extend it with support for multiplying your own data type, say MyDecimal. If you define a conversion from MyDecimal to number, the typed-function will automatically allow you to multiply a MyDecimal with a number.

  • Dependency injection. When we have a function multiply with support for MyDecimal, thanks to the dependency injection, other functions using multiply under the hood, like prod, will automatically support MyDecimal too. This also works the other way around: if you don't need the heavyweight multiply (which supports BigNumbers, matrices, etc), and you just need a plain and simple number support, you can use a lightweight implementation of multiply just for numbers, and inject that in prod and other functions.

At the lowest level, mathjs has immutable factory functions which create immutable functions. The core function math.create(...) creates a new instance having functions created from all passed factory functions. A mathjs instance is a collection of created functions. It contains a function like math.import to allow extending the instance with new functions, which can then be used in the expression parser.

Implementing a new function

A common case is to implement a new function. This involves the following steps:

  • Implement the function in the right category, for example ./src/function/arithmetic/myNewFunction.js, where you can replace arithmetic with the proper category, and myNewFunction with the name of the new function. Add the new function to the index files ./src/factoriesAny.js and possibly ./src/factoriesNumber.js.
  • Write documentation on the function in the source code comment of myNewFunction.js. This documentation is used to auto generate documentation on the website. It should include a History section with one line, indicating the upcoming version number in which the function will be created.
  • Write embedded documentation for the new function in ./src/expression/embeddedDocs/function/arithmetic/myNewFunction.js. Add the new documentation to the index file ./src/expression/embeddedDocs/embeddedDocs.js.
  • Write unit tests for the function in ./test/unit-tests/function/arithmetic/myNewFunction.test.js.
  • Write the necessary TypeScript definitions for the new function in ./types/index.d.ts, and write tests for it in ./test/typescript-tests/testTypes.ts. This is described in ./types/EXPLANATION.md -- make sure to read that page, as Typescript definitions must be added in multiple places in the code.
  • Ensure the code style is ok by running npm run lint (run npm run format to fix the code style automatically).

Build scripts

The build script currently generates two types of output:

  • any, generate entry points to create full versions of all functions
  • number: generating and entry points to create lightweight functions just supporting number

For each function, an object is generated containing the factory functions of all dependencies of the function. This allows to just load a specific set of functions, and not load or bundle any other functionality. So for example, to just create function add you can do math.create(addDependencies).

Test

To execute tests for the library, install the project dependencies once:

npm install

Then, the tests can be executed:

npm test

To test the type definitions:

npm run test:types

Additionally, the tests can be run on FireFox using headless mode:

npm run test:browser

To run the tests remotely on LambdaTest, first set the environment variables LT_USERNAME and LT_ACCESS_KEY with your username and access key and then execute:

npm run test:lambdatest

You can separately run the code linter, though it is also executed with npm test:

npm run lint

To automatically fix linting issue, run:

npm run format

To test code coverage of the tests:

npm run coverage

To see the coverage results, open the generated report in your browser:

./coverage/lcov-report/index.html

Continuous integration testing

Continuous integration tests are run on GitHub Actions and TestMu AI (formerly LambdaTest) every time a commit is pushed to GitHub. GitHub Actions runs the tests for different versions of node.js, and TestMu AI runs the tests on all major browsers.

TestMu AI

Thanks, GitHub Actions and TestMu AI for the generous free hosting of this open source project!

License

mathjs is published under the Apache 2.0 license:

Copyright (C) 2013-2026 Jos de Jong <wjosdejong@gmail.com>

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

   https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

mathjs contains a JavaScript port of the CSparse library, published under the LGPL-2.1+ license:

CSparse: a Concise Sparse matrix package.
Copyright (c) 2006, Timothy A. Davis.
http://www.suitesparse.com

--------------------------------------------------------------------------------

CSparse is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.

CSparse is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public
License along with this Module; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA