要是实在不知道要干什么,那就喝两杯思路就来了!

导航菜单

一个函数,三种境界

一开始,我们写加法函数往往只追求一个目标:能算出结果就行。这是最自然、最直接的写法,也是大多数人起步时的样子。

function numAdd (x, y) { return x + y; }

随着系统越来越复杂,我们开始意识到:代码不仅要对,更要稳、可控、可查。于是,我们按照企业工程标准,把它重构为更健壮的企业级实现

/**
 * 企业级数值加法函数(解决JS浮点数精度、类型异常、边界值等问题)
 * @param {number} x - 加数1(必须为有效数字)
 * @param {number} y - 加数2(必须为有效数字)
 * @returns {number} 两个数的精确和
 * @throws {TypeError} 传入非数字类型时抛出
 * @throws {RangeError} 传入NaN/Infinity等无效数值时抛出
 * @example
 * numAdd(0.1, 0.2) → 0.3
 * numAdd(10, 20) → 30
 * numAdd("1", 2) → 抛出TypeError
 */
function numAdd(x, y) {
  // 1. 参数数量校验:严格限制仅传入2个参数
  if (arguments.length !== 2) {
    const error = new Error(`函数numAdd仅接受2个参数,实际传入${arguments.length}个`);
    console.error(`[numAdd][参数错误]`, error.message); // 企业级:错误日志可追溯
    throw error;
  }

  // 2. 类型校验:严格校验是否为数字类型(排除字符串、对象等)
  const isXNumber = typeof x === 'number';
  const isYNumber = typeof y === 'number';
  if (!isXNumber || !isYNumber) {
    const error = new TypeError(
      `参数类型错误:x=${x}(${typeof x})、y=${y}(${typeof y}),必须均为number类型`
    );
    console.error(`[numAdd][类型错误]`, error.message);
    throw error;
  }

  // 3. 有效数值校验:排除NaN、Infinity、-Infinity
  const isValidNumber = (num) => !Number.isNaN(num) && isFinite(num);
  if (!isValidNumber(x) || !isValidNumber(y)) {
    const error = new RangeError(
      `无效数值:x=${x}、y=${y},不允许为NaN/Infinity/-Infinity`
    );
    console.error(`[numAdd][数值错误]`, error.message);
    throw error;
  }

  // 4. 浮点数精度处理(解决0.1+0.2=0.30000000000000004问题)
  // 核心逻辑:将浮点数转为整数计算,再还原(企业级金融场景必备)
  const getDecimalLength = (num) => {
    const str = num.toString();
    const decimalPart = str.split('.')[1] || '';
    return decimalPart.length;
  };
  const maxDecimal = Math.max(getDecimalLength(x), getDecimalLength(y));
  const multiplier = Math.pow(10, maxDecimal);
  
  // 转整数计算避免精度丢失,再除以倍数还原
  const sum = (Math.round(x * multiplier) + Math.round(y * multiplier)) / multiplier;

  // 5. 企业级:记录操作日志(便于审计/排查问题)
  console.info(`[numAdd][执行成功] x=${x}, y=${y}, sum=${sum}`);

  return sum;
}

但世界上还有一类系统,不允许任何意外、任何模糊、任何不可控行为。为了适配这种顶级安全要求,我们最终把函数推向DO-178C 航天级严谨度

/**
 * 航天/飞控级高精度数值加法函数(符合DO-178C A级规范)
 * 核心约束:
 * 1. 禁用动态内存分配(如new/delete)、禁用闭包、禁用eval等不确定行为
 * 2. 所有分支100%可覆盖、所有数值边界100%校验
 * 3. 结果可数学证明正确,无精度丢失、无运行时异常
 * 4. 输出结构化审计日志,支持航天级数据溯源
 * 
 * @param {number} x - 加数1(必须为有限有效数字,范围:±1e-10 ~ ±1e18)
 * @param {number} y - 加数2(同x的约束)
 * @returns {number} 精确和(误差<1e-15)
 * @throws {TypeError} 非数字类型时抛出(带错误码:E_TYPE)
 * @throws {RangeError} 数值越界/无效时抛出(带错误码:E_RANGE)
 * @throws {Error} 参数数量错误时抛出(带错误码:E_ARG_COUNT)
 * 
 * 【航天级元信息】
 * 版本:V1.0.0(配置管理基线版本)
 * 验证覆盖率:100%(语句/分支/MC/DC覆盖)
 * 形式化验证:已通过Z3定理证明器验证逻辑正确性
 * 硬件适配:适配航天嵌入式JS引擎(如Espruino RTOS)
 */
const NumAddSpace = (() => {
    // ###########################
    // 航天级:固化常量(禁止运行时修改,消除不确定性)
    // ###########################
    // 1. 数值边界(航天场景的物理意义约束:超出则无实际物理意义)
    const CONST = Object.freeze({
        MIN_VALUE: -1e18,    // 最小值(航天数据最大量级)
        MAX_VALUE: 1e18,     // 最大值
        MIN_PRECISION: 1e-10,// 最小精度(低于此无物理意义)
        MAX_DECIMAL: 15,     // 最大小数位(航天传感器最高精度)
        ERROR_CODES: Object.freeze({
            ARG_COUNT: "E_ARG_COUNT",
            TYPE: "E_TYPE",
            RANGE: "E_RANGE"
        })
    });

    // ###########################
    // 航天级:纯函数校验(无副作用、无外部依赖)
    // ###########################
    /**
     * 校验数值是否在航天允许范围内
     * @param {number} num 待校验数值
     * @returns {boolean} 校验结果
     */
    function validateNumberRange(num) {
        // 1. 排除NaN/Infinity/-Infinity
        if (Number.isNaN(num) || !Number.isFinite(num)) return false;
        // 2. 数值范围校验(航天物理量边界)
        if (num < CONST.MIN_VALUE || num > CONST.MAX_VALUE) return false;
        // 3. 最小精度校验(低于此的数值无物理意义,视为无效)
        if (Math.abs(num) > 0 && Math.abs(num) < CONST.MIN_PRECISION) return false;
        return true;
    }

    /**
     * 计算数值小数位数(航天级:禁止动态字符串拼接优化)
     * @param {number} num 数值
     * @returns {number} 小数位数(最大不超过CONST.MAX_DECIMAL)
     */
    function getDecimalLength(num) {
        // 航天级:禁用toString()(可能因引擎实现不同导致结果差异)
        // 改用数学计算法(可证明正确)
        let absNum = Math.abs(num);
        let integerPart = Math.floor(absNum);
        let decimalPart = absNum - integerPart;
        
        if (decimalPart === 0) return 0;
        
        let count = 0;
        // 逐位放大,直到小数部分为整数(最大CONST.MAX_DECIMAL位)
        while (count < CONST.MAX_DECIMAL) {
            decimalPart *= 10;
            count++;
            if (Math.floor(decimalPart) === decimalPart) break;
        }
        return count;
    }

    /**
     * 航天级高精度加法核心逻辑(形式化验证通过)
     * @param {number} x 加数1
     * @param {number} y 加数2
     * @returns {number} 精确和
     */
    function coreAdd(x, y) {
        // 步骤1:计算最大小数位(限制在CONST.MAX_DECIMAL内)
        const decimalX = getDecimalLength(x);
        const decimalY = getDecimalLength(y);
        const maxDecimal = Math.min(Math.max(decimalX, decimalY), CONST.MAX_DECIMAL);
        const multiplier = Math.pow(10, maxDecimal);

        // 步骤2:转整数计算(消除浮点数精度丢失,数学可证明)
        // 航天级:使用Math.round替代Math.floor,避免截断误差
        const intX = Math.round(x * multiplier);
        const intY = Math.round(y * multiplier);
        
        // 步骤3:整数加法(无精度丢失)
        const intSum = intX + intY;
        
        // 步骤4:还原为浮点数,且校验结果范围
        const sum = intSum / multiplier;
        if (!validateNumberRange(sum)) {
            throw new RangeError(`${CONST.ERROR_CODES.RANGE}: 加法结果越界 sum=${sum}`);
        }

        // 步骤5:航天级:结果校验(双重验证,防止计算错误)
        const verifySum = x + y;
        const error = Math.abs(sum - verifySum);
        if (error > 1e-15) {
            throw new Error(`E_VERIFY: 加法结果验证失败 计算值=${sum} 验证值=${verifySum} 误差=${error}`);
        }

        return sum;
    }

    // ###########################
    // 对外暴露的核心函数(航天级:唯一入口,禁止重载)
    // ###########################
    function numAdd(x, y) {
        // 步骤1:参数数量校验(航天级:严格固定参数个数)
        if (arguments.length !== 2) {
            const errMsg = `${CONST.ERROR_CODES.ARG_COUNT}: 必须传入2个参数,实际${arguments.length}个`;
            // 航天级:同步写入审计日志(支持航天地面站溯源)
            console.error(`[SPACE_NUMADD][${new Date().toISOString()}] [ERROR] ${errMsg}`);
            throw new Error(errMsg);
        }

        // 步骤2:类型校验(航天级:禁用typeof之外的类型判断,避免引擎差异)
        if (typeof x !== 'number' || typeof y !== 'number') {
            const errMsg = `${CONST.ERROR_CODES.TYPE}: 参数类型错误 x=${x}(${typeof x}) y=${y}(${typeof y})`;
            console.error(`[SPACE_NUMADD][${new Date().toISOString()}] [ERROR] ${errMsg}`);
            throw new TypeError(errMsg);
        }

        // 步骤3:数值范围校验
        if (!validateNumberRange(x) || !validateNumberRange(y)) {
            const errMsg = `${CONST.ERROR_CODES.RANGE}: 数值无效/越界 x=${x} y=${y} 约束[${CONST.MIN_VALUE}, ${CONST.MAX_VALUE}]`;
            console.error(`[SPACE_NUMADD][${new Date().toISOString()}] [ERROR] ${errMsg}`);
            throw new RangeError(errMsg);
        }

        // 步骤4:执行核心加法(无异常则结果100%正确)
        const sum = coreAdd(x, y);

        // 步骤5:航天级审计日志(包含所有输入输出,支持全链路溯源)
        console.info(`[SPACE_NUMADD][${new Date().toISOString()}] [SUCCESS] x=${x} y=${y} sum=${sum} decimalX=${getDecimalLength(x)} decimalY=${getDecimalLength(y)}`);

        // 步骤6:返回不可变结果(航天级:禁止返回后修改)
        return Object.freeze(sum);
    }

    // 航天级:冻结整个模块,禁止运行时修改任何逻辑
    return Object.freeze({
        numAdd: numAdd,
        // 暴露常量(仅用于验证,禁止修改)
        CONST: CONST
    });
})();

// 对外导出(航天级:唯一导出方式,禁止动态导出)
module.exports = NumAddSpace.numAdd;


发表评论