在javascript中的简单数学中,在循环中将数字X次相加会产生与事先乘以X并在之后相加不同的结果
Simple math in javascript, adding number X times in a loop gives different result from multiplying by X beforehand and adding it afterwards
作为参考,此代码对应于此 codecademy 练习。
这是一个简单的购物车/结帐程序,用于注册商品并将其成本添加到总数中,它还需要商品数量值。
注意:我以前读过关于javascript对数学很奇怪的文章,所以我没有被吓到,但我非常好奇,有兴趣了解这种特殊情况下的行为是什么,所以也许我会得到一些见解,以便能够防止这种情况在未来发生。
所以,我有 2 个练习解决方案,第一个是好的 - 第二个没有问题,我想。他们在这里:
第一条。
与第一种方法效率低下的事实无关(多次遍历所有可能的情况并一遍又一遍地添加项目成本)。引起我注意的是返回的额外十进制值。
var cashRegister = {
total:0,
add: function(itemCost){
this.total += itemCost;
},
scan: function(item,quantity) {
for (var i = 1 ; i <= quantity ; i++ ){
switch (item) {
case "eggs": this.add(0.98); break;
case "milk": this.add(1.23); break;
case "magazine": this.add(4.99); break;
case "chocolate": this.add(0.45); break;
}
}
}
};
// scan each item 4 times
cashRegister.scan("eggs",4);
console.log('Your bill is '+ cashRegister.total);
cashRegister.scan("milk",4);
console.log('Your bill is '+ cashRegister.total);
cashRegister.scan("magazine",4);
console.log('Your bill is '+ cashRegister.total);
cashRegister.scan("chocolate",4);
console.log('Your bill is '+ cashRegister.total);
控制台输出:
Your bill is 3.92
Your bill is 8.840000000000002
Your bill is 28.800000000000004
Your bill is 30.6
Your bill is 30.6
第二个
此解决方案将物料数量乘以碰巧出现的任何情况。
var cashRegister = {
total:0,
add: function(itemCost){
this.total += itemCost;
},
scan: function(item,quantity) {
switch (item) {
case "eggs": this.add(0.98*quantity); break;
case "milk": this.add(1.23*quantity); break;
case "magazine": this.add(4.99*quantity); break;
case "chocolate": this.add(0.45*quantity); break;
}
}
};
// scan each item 4 times
cashRegister.scan("eggs",4);
console.log('Your bill is '+cashRegister.total);
cashRegister.scan("milk",4);
console.log('Your bill is '+cashRegister.total);
cashRegister.scan("magazine",4);
console.log('Your bill is '+cashRegister.total);
cashRegister.scan("chocolate",4);
console.log('Your bill is '+cashRegister.total);
控制台输出干净:
Your bill is 3.92
Your bill is 8.84
Your bill is 28.8
Your bill is 30.6
Your bill is 30.6
那么,第一个解决方案是怎么回事,以便返回小数盛会?
提前非常感谢你。
编辑:感谢您尝试帮助我解决我的疑问。虽然我意识到我需要明确表示我知道"JavaScript 使用浮点值作为其数字类型,精度损失是造成这种情况的原因"。
真正的问题是:
为什么相同的计算在第一种解上会失去精度,而在第二种解上却不会丢失精度?
JavaScript 为其number
类型使用浮点值,精度损失是造成这种情况的原因。精度损失是由于浮点数使用二进制编码格式(很可能是IEEE 754)这一事实造成的,并且它不能精确地表示大多数数字。例如 0.1
不能在 IEEE 754 中表示:
- 十进制:
0.1
- 二进制版本:
00111101110011001100110011001101
- 十六进制代表:
0x3dcccccd
, - 实际值:
0.10000000149011612
我已经使用了这个在线转换器来获得这些结果。
所以直接回答你的问题:浮点数上的更多运算(多重加法,而不是乘法)会导致误差的累积。
一个例子
我最喜欢的精度损失示例之一是以下代码:
var a = 0, b = 0;
for (var i = 1; i < 100; i++) {
a += Math.pow(10, -9*i);
}
for (var j = 100; j < 1; j--) {
b += Math.pow(10, -9*i);
}
alert(a == b);
这会false
发出警报,因为b
由于精度损失而为零,而a
则不是。
格式
如果你只关心"不错"的输出,你可以使用 #toFixed
方法:
var num = 5.56789;
var n = num.toFixed(2);
这会产生"5.57"
.
ECMAScript 标准
根据 ECMAScript 标准(ECMA-262,版本 5.1)第 4.3.19 节。 数字值的定义如下:
对应于双精度 64 位二进制格式 IEEE 754 值的基元值
因此,这就是标准投诉实施为您提供的内容。其他任何可能在后台的内容都是实现细节。
这是由于浮点数固有的不精确性。并非所有有理数都可以用二进制浮点数表示为精确值,因此您所看到的是这种近似的结果。维基百科页面上的这一部分很好地解释了这种转换是如何进行的。
如果你真的需要精确的值,你可以使用 BigDecimal.js 或其他类似的库。
- electronic BrowserWindow的最小高度和宽度在hide()show()方法之后不起作用
- 奇怪的Javascript结果
- 在chrome.tabs.onCreated之后加载HTML页面
- Javascript(jQuery)给了我奇怪的结果
- 在Jquery detachment()和appendTo()之后定位元素
- AngularJS:ng之后,重复$scope值未按预期更新
- JSONP请求返回结果,但也触发error_callback
- 如何在chrome扩展中存储数据/结果,以及如何使用setTimeout使其只被调用一次
- 为什么元素的宽度在页面加载之后和那一刻之后不同
- 没有在Angular应用程序中定义firebase(在firebase迁移之后)
- Javascript,输出结果后页面不断刷新
- 这是使用html快照和谷歌获取的预期结果吗?SEO/SPA
- 在javascript中的简单数学中,在循环中将数字X次相加会产生与事先乘以X并在之后相加不同的结果
- 如何解析javascript/jquery以在ajax结果内/之后回显/执行
- 为什么defered.promise在defered.resolve(myobject)之后的结果与myobject不同
- 在每个函数的特定结果之后插入元素
- 在AJAX调用之后,我无法在视图中看到结果
- Ajax请求在其他请求(.NET+jQuery)的成功结果之后
- 在使用splice()之前和之后获取结果数组
- 谷歌位置自动补全-昨晚之后没有结果