jQuery Ajax回调自动舍入小数问题及解决咨询
嘿,我来帮你解决这两个和数值精度相关的问题,咱们一步步来:
一、修复jQuery Ajax解析Decimal JSON的精度问题
首先得搞清楚为啥会出现这个情况:JavaScript里的Number类型是64位双精度浮点数,它没办法精确表示所有十进制小数——比如你提到的5.74999999999999999999999999,这个数在二进制里是无限循环的,JS会自动把它近似成最接近的可表示值,也就是5.75。
而jQuery的Ajax默认会自动把JSON响应解析成JS对象(内部其实也是用JSON.parse),所以当服务器返回高精度的decimal数值时,转成Number就会丢精度。你说JSON.parse能正确解析,大概率是你直接处理原始响应文本时,有没有可能服务器返回的是字符串形式的数值?如果是纯数值类型的话,JSON.parse同样会转成Number,也会有精度问题哦。
两种可行的修复方案:
让服务器返回字符串格式的Decimal值
这是最省心的办法——把高精度的decimal数值用字符串包裹起来返回(比如{"amount": "5.74999999999999999999999999"}),这样不管是jQuery还是JSON.parse,解析后得到的都是字符串,完全不会丢精度。之后你要是需要计算,可以用专门的高精度数值库(比如decimal.js)来处理这些字符串。禁止jQuery自动解析JSON,手动处理响应
要是没法改服务器返回格式,那就在Ajax请求里设置dataType: "text",让jQuery直接返回原始的响应文本,别自动解析成对象。然后你可以自己用JSON.parse配合reviver函数,或者用高精度库来处理:$.ajax({ url: "你的接口地址", dataType: "text", // 告诉jQuery别自动解析JSON success: function(rawResponse) { // 方案1:把指定字段的数值转成字符串保留精度 const data = JSON.parse(rawResponse, (key, value) => { // 这里判断哪些字段是需要保留精度的decimal字段 if (typeof value === "number" && key === "你的Decimal字段名") { return value.toString(); // 转成字符串就不会丢精度了 } return value; }); // 方案2:用decimal.js转成高精度数值对象(需要先引入这个库) // const data = JSON.parse(rawResponse, (key, value) => { // if (typeof value === "number" && key === "你的Decimal字段名") { // return new Decimal(value.toString()); // } // return value; // }); } });
二、关于浮点数计算的精度问题解释
你提到的(37.272727272727272727272727270 *522.5)/100得到194.75而非预期的4.7499999999999999999999,这里有两个点要说明:
先纠正一个计算错误:37.27乘以522.5的结果远大于4.75哦,实际算下来:
37.27272727272727 * 522.5 = 19475,除以100就是194.75,这个结果是对的,你可能是写错预期值啦😉。浮点数精度的本质:就算是正确的计算,你偶尔也会遇到精度偏差,这是因为十进制小数在二进制浮点数里没法精确表示。比如
0.1在二进制里是无限循环的,所以0.1 + 0.2不等于0.3,而是0.30000000000000004。Windows计算器、谷歌搜索、浏览器控制台之所以显示“正确”的结果,是因为它们在输出的时候做了近似处理——把二进制浮点数转成十进制字符串时,会四舍五入到合理的位数,让用户看起来是正常的,而不是显示底层的二进制近似值。
说白了,二进制浮点数的精度限制是计算机底层的特性,所有用64位双精度浮点数的语言(包括JS)都会遇到这个问题。如果需要绝对精确的十进制计算,必须用专门的高精度数值库(比如decimal.js、big.js),不能用原生的Number类型。
内容的提问来源于stack exchange,提问作者Nithin B

