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
供参考,此代码对应this codecademy exercise.
这是一个简单的 cart/checkout 程序,用于注册一个项目并将其成本添加到总数中,它还需要一个项目数量值。
注意:我之前看过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 uses floating point values for its number type and the loss of precision is the cause for this"。
真正的问题是:
为什么相同的计算在第一个解上会丢失精度,而在第二个解上却不会?
JavaScript 对其 number
类型使用浮点值,精度损失是造成这种情况的原因。精度损失是由于浮点数使用二进制编码格式(最有可能IEEE 754),它不能表示大多数数字 准确地说。例如。 0.1
无法在 IEEE 754 中表示:
- 十进制:
0.1
- 二进制表示:
00111101110011001100110011001101
- 十六进制数:
0x3dcccccd
,
- 实际值:
0.10000000149011612
这些结果我用了this online converter。
所以直接回答你的问题:对浮点数的更多操作(乘法而不是乘法)导致错误累积。
一个例子
我最喜欢的精度损失示例之一是这段代码:
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
不是。
正在格式化
如果你只关心"nice"输出,你可以使用#toFixed
方法:
var num = 5.56789;
var n = num.toFixed(2);
这会产生 "5.57"
。
ECMAScript 标准
根据 ECMAScript 标准(ECMA-262,版本 5.1),section 4.3.19.数值的定义如下:
primitive value corresponding to a double-precision 64-bit binary format IEEE 754 value
所以,这就是标准投诉实施为您提供的。任何其他可能在后台运行的内容都是实现细节。
这是由于浮点数固有的不精确性。并非所有有理数都可以用二进制浮点数表示为精确值,因此您看到的是这种近似的结果。 This section on the Wikipedia page 对如何进行这种转换有很好的解释。
如果你真的需要精确的值,你可以使用BigDecimal.js或其他类似的库。
供参考,此代码对应this codecademy exercise.
这是一个简单的 cart/checkout 程序,用于注册一个项目并将其成本添加到总数中,它还需要一个项目数量值。
注意:我之前看过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 uses floating point values for its number type and the loss of precision is the cause for this"。
真正的问题是:
为什么相同的计算在第一个解上会丢失精度,而在第二个解上却不会?
JavaScript 对其 number
类型使用浮点值,精度损失是造成这种情况的原因。精度损失是由于浮点数使用二进制编码格式(最有可能IEEE 754),它不能表示大多数数字 准确地说。例如。 0.1
无法在 IEEE 754 中表示:
- 十进制:
0.1
- 二进制表示:
00111101110011001100110011001101
- 十六进制数:
0x3dcccccd
, - 实际值:
0.10000000149011612
这些结果我用了this online converter。
所以直接回答你的问题:对浮点数的更多操作(乘法而不是乘法)导致错误累积。
一个例子
我最喜欢的精度损失示例之一是这段代码:
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
不是。
正在格式化
如果你只关心"nice"输出,你可以使用#toFixed
方法:
var num = 5.56789;
var n = num.toFixed(2);
这会产生 "5.57"
。
ECMAScript 标准
根据 ECMAScript 标准(ECMA-262,版本 5.1),section 4.3.19.数值的定义如下:
primitive value corresponding to a double-precision 64-bit binary format IEEE 754 value
所以,这就是标准投诉实施为您提供的。任何其他可能在后台运行的内容都是实现细节。
这是由于浮点数固有的不精确性。并非所有有理数都可以用二进制浮点数表示为精确值,因此您看到的是这种近似的结果。 This section on the Wikipedia page 对如何进行这种转换有很好的解释。
如果你真的需要精确的值,你可以使用BigDecimal.js或其他类似的库。