在 JavaScript 中,处理小数精度和生成随机数是开发中非常常见的需求。下面分别对保留小数 和随机函数进行详解,包括核心方法、使用示例、常见陷阱和最佳实践。
一、保留小数(数值格式化)
1. Number.prototype.toFixed(digits)
- 功能 :将数字转换为字符串,并保留指定的小数位数(四舍五入)。
- 参数 :
digits-- 0~100 之间的整数,表示小数位数。 - 返回值 :字符串(注意不是数字)。
- 特点:自动补零或舍入,但存在浮点数精度导致的"非标准舍入"问题。
javascript
const num = 3.14159;
console.log(num.toFixed(2)); // "3.14" 四舍五入
console.log(num.toFixed(4)); // "3.1416"
// 补零示例
const num2 = 5;
console.log(num2.toFixed(2)); // "5.00"
// ⚠️ 浮点数精度问题
console.log((1.005).toFixed(2)); // 期望 "1.01",实际 "1.00" (因为 1.005 在计算机中实际存储为 1.004999...)
解决方案 :对于精确的货币计算,推荐先乘以 10 的幂次转为整数运算,或使用 Math.round 封装。
javascript
function preciseToFixed(num, digits) {
const factor = Math.pow(10, digits);
return (Math.round(num * factor) / factor).toFixed(digits);
}
console.log(preciseToFixed(1.005, 2)); // "1.01"
2. Number.prototype.toPrecision(precision)
- 功能 :返回数字的有效数字 位数(四舍五入),若整数部分位数超过
precision,则返回科学计数法。 - 参数 :
precision-- 1~100 之间的整数。 - 返回值:字符串。
javascript
const num = 123.456;
console.log(num.toPrecision(4)); // "123.5"
console.log(num.toPrecision(2)); // "1.2e+2" 科学计数法
console.log((0.00123).toPrecision(2)); // "0.0012"
3. Math.round() + 乘除法(保留小数并返回数字)
- 如果需要保留小数位数但保持数字类型(而非字符串),常用此方法。
- 原理:乘以
10^n取整,再除以10^n。
javascript
function roundTo(num, digits) {
const factor = Math.pow(10, digits);
return Math.round(num * factor) / factor;
}
console.log(roundTo(3.14159, 2)); // 3.14
console.log(roundTo(1.005, 2)); // 1.01 (比 toFixed 更精确)
console.log(typeof roundTo(3.14, 1)); // "number"
⚠️ 注意
Math.round采用"四舍五入",对于-1.005等负数同样适用。
4. Intl.NumberFormat(国际化格式化)
- 适合需要格式化显示(如千位分隔符、货币符号、特定区域的小数点风格)。
- 返回字符串,但支持更丰富的本地化配置。
javascript
const formatter = new Intl.NumberFormat('en-US', {
minimumFractionDigits: 2,
maximumFractionDigits: 2,
});
console.log(formatter.format(1234.567)); // "1,234.57"
// 中文环境
const cnFormatter = new Intl.NumberFormat('zh-CN', {
minimumFractionDigits: 2,
});
console.log(cnFormatter.format(1234.5)); // "1,234.50"
5. 避免浮点数陷阱的通用原则
- 对于财务、支付等要求精确小数的场景,推荐使用整数(分、厘)存储和运算,仅在展示时转为带小数的字符串。
- 或使用第三方库:
decimal.js、big.js、bignumber.js。
二、随机函数
JavaScript 中最常用的随机函数是 Math.random(),它返回一个 伪随机 浮点数,范围 [0, 1)(包含 0,不包含 1)。
1. 基础用法:Math.random()
javascript
console.log(Math.random()); // 例如 0.23145678912345678
2. 生成指定范围的随机数
2.1 生成 [0, max) 的浮点数
javascript
function randomFloat(max) {
return Math.random() * max;
}
console.log(randomFloat(10)); // 例如 3.789
2.2 生成 [min, max) 的浮点数
javascript
function randomRange(min, max) {
return min + Math.random() * (max - min);
}
console.log(randomRange(5, 10)); // 例如 7.234
2.3 生成 [min, max] 的整数(包含两端)
- 使用
Math.floor():注意边界处理,公式为Math.floor(Math.random() * (max - min + 1)) + min。
javascript
function randomInt(min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
console.log(randomInt(1, 6)); // 模拟骰子:1~6 等概率
- 使用
Math.round()会导致端点概率不均等(两端概率减半),不推荐。
2.4 生成 [min, max) 的整数(包含 min,不包含 max)
javascript
function randomIntExclusiveMax(min, max) {
return Math.floor(Math.random() * (max - min)) + min;
}
console.log(randomIntExclusiveMax(1, 10)); // 1~9
3. 其他常见随机应用
3.1 随机布尔值
javascript
const randomBool = Math.random() < 0.5; // true/false 概率各半
3.2 从数组中随机取一项
javascript
const arr = ['apple', 'banana', 'cherry'];
const randomItem = arr[Math.floor(Math.random() * arr.length)];
3.3 随机颜色(十六进制)
javascript
function randomHexColor() {
return '#' + Math.floor(Math.random() * 0xffffff).toString(16).padStart(6, '0');
}
console.log(randomHexColor()); // 例如 "#a3f2c1"
3.4 随机字符串(指定长度)
javascript
function randomString(length) {
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
let result = '';
for (let i = 0; i < length; i++) {
result += chars[Math.floor(Math.random() * chars.length)];
}
return result;
}
console.log(randomString(8)); // 例如 "Kj3nP9xQ"
4. 安全性:密码学安全随机数
Math.random() 不适合用于安全敏感场景(如生成令牌、验证码、密钥),因为它是伪随机的且种子可预测。
应使用 Web Crypto API 的 crypto.getRandomValues()(浏览器)或 crypto.randomBytes()(Node.js)。
浏览器端示例(生成 0~255 的随机整数数组)
javascript
const array = new Uint32Array(1);
crypto.getRandomValues(array);
const secureRandom = array[0] / (0xffffffff + 1); // 得到 [0,1) 的高质量随机数
console.log(secureRandom);
生成随机整数(例如 1~100):
javascript
function secureRandomInt(min, max) {
const range = max - min + 1;
const maxValid = Math.floor(0xffffffff / range) * range;
let randomValue;
do {
randomValue = crypto.getRandomValues(new Uint32Array(1))[0];
} while (randomValue >= maxValid);
return min + (randomValue % range);
}
console.log(secureRandomInt(1, 100));
5. 伪随机种子(进阶)
Math.random() 无法手动设置种子,但若需要可复现的随机序列(例如游戏地图生成),可以实现一个简单的线性同余生成器(LCG)或使用 seedrandom 库。
javascript
// 简易 LCG(不用于密码学)
function createSeededRandom(seed) {
let state = seed;
return function() {
state = (state * 1103515245 + 12345) & 0x7fffffff;
return state / 0x7fffffff;
};
}
const rng = createSeededRandom(42);
console.log(rng()); // 固定序列的第一个随机数
console.log(rng());
总结对比表
| 需求 | 推荐方法 | 返回类型 | 注意事项 |
|---|---|---|---|
| 保留 n 位小数并显示(四舍五入) | num.toFixed(n) |
字符串 | 存在浮点数精度问题 |
| 保留 n 位小数并继续计算 | Math.round(num * 10^n) / 10^n |
数字 | 比 toFixed 更精确,但仍受浮点限制 |
| 本地化格式化(千位分隔等) | Intl.NumberFormat |
字符串 | 支持区域敏感 |
生成 [0,1) 随机浮点数 |
Math.random() |
数字 | 伪随机,不安全 |
生成 [min,max] 随机整数 |
Math.floor(Math.random()*(max-min+1))+min |
数字 | 等概率,常用 |
生成 [min,max) 随机浮点数 |
min + Math.random() * (max - min) |
数字 | |
| 安全随机(密码学) | crypto.getRandomValues() |
多种类型 | 用于令牌、验证码等 |
掌握以上方法,可以轻松处理 JavaScript 中绝大部分小数格式化和随机数生成的需求。