JavaScript中如何实现数字保留固定两位小数返回150.00格式数值
首先你要明确一个JS语言的基础规则:原始Number类型本身不存储末尾无意义的0,遵循IEEE 754双精度浮点数规范的前提下,150和150.00是完全相同的值,二进制存储结构没有任何区别,不存在“Number类型的150.00”这种东西,所有把toFixed()结果转成Number的操作,都会自动丢弃末尾的0,这是语言层面的固定行为,没有hack手段可以绕过。
你之前觉得toFixed(2)返回字符串不符合要求,本质是混淆了「数值计算」和「格式化展示」两个场景的需求,对应不同的实现方案。
方案1:需要固定返回带两位小数、补足末尾0的结果(格式化场景)
如果你的需求是最终输出必须带.00后缀(比如页面展示、传给要求固定两位小数格式的接口),那你要的本来就是字符串类型,直接用toFixed(2)即可,不需要转Number:
const takeNumber = (numberToConvert) => { // 入参合法性校验,避免NaN、非数字入参导致异常 if (typeof numberToConvert !== 'number' || Number.isNaN(numberToConvert)) { return '0.00'; } return numberToConvert.toFixed(2); }; takeNumber(150) // 返回 "150.00",完全符合格式要求
这个方案是90%场景下的正确选择:如果接口要求传两位小数的数字,你传数字150和字符串"150.00"在绝大多数请求协议下后端拿到的数值是一致的;如果后端明确要求必须带两位小数格式,那它要的本来就是字符串类型,传纯数字过去后端根本拿不到末尾的0。
方案2:只需要保证数值精度到两位小数,不需要携带格式(计算场景)
如果你只是要做数值计算、避免浮点数精度误差,不需要输出的时候自带末尾0,那直接返回Number类型即可,不需要纠结控制台打印出来有没有末尾0——参与计算时150和150.00没有任何区别,等需要展示的时候再调用方案1的方法转成带格式的字符串就行。
注意要修正浮点数计算的精度误差(比如1.005 * 100实际得到100.49999999999999导致四舍五入错误的问题):
const takeNumber = (numberToConvert) => { if (typeof numberToConvert !== 'number' || Number.isNaN(numberToConvert)) { return 0; } // 加Number.EPSILON修正极小值误差 return Math.round((numberToConvert + Number.EPSILON) * 100) / 100; }; takeNumber(150) // 返回数字150,计算精度符合两位小数要求
方案3:需要同时兼顾数值计算和自动格式化输出(特殊场景)
如果你需要返回的值既能像普通数字一样参与计算,又能在转字符串、页面渲染的时候自动带出两位小数,可以用自定义封装对象实现:
function TwoDecimal(num) { const validNum = typeof num === 'number' && !Number.isNaN(num) ? num : 0; this._value = Math.round((validNum + Number.EPSILON) * 100) / 100; } // 参与数值运算时自动转成原始数字 TwoDecimal.prototype.valueOf = function() { return this._value; }; // 转字符串、渲染时自动返回两位小数格式 TwoDecimal.prototype.toString = function() { return this._value.toFixed(2); }; const takeNumber = (numberToConvert) => new TwoDecimal(numberToConvert); const res = takeNumber(150); console.log(res + 10) // 计算时得到160,和普通数字行为一致 console.log(String(res)) // 转字符串时得到"150.00"
这个方案返回的是自定义对象不是原始Number类型,没有特殊需求不建议使用。
不要尝试写Number(x.toFixed(2))、parseFloat(x.toFixed(2))这类代码来实现“返回数字类型的150.00”,从语言规范层面就不可能实现,所有这类写法最终得到的都是不带末尾0的纯数字,属于无效尝试。
内容的提问来源于stack exchange,提问作者William

