提问人:eric2023 提问时间:7/26/2023 最后编辑:Barmareric2023 更新时间:7/27/2023 访问量:77
为什么这个浮点加法只能通过将所有数字乘以 10 来解决?
why this Floating point addition can be solved only by multiply all the figures by ten?
问:
由于 Javascript 中的浮点运算,我知道将在 Javascript 中,但我不明白为什么只将数字乘以 10,即 ,然后问题就可以解决了。应该乘以 ,对吧?0.2 + 0.1 = 0.30000000000000004
let x = (0.2 * 10 + 0.1 * 10) / 10;
10**10
let y = (0.2 * 10 + 0.1 * 10) / 10;
document.getElementById("abc").innerHTML = "0.2 + 0.1 = " + y;
<!DOCTYPE html>
<html>
<body>
<p id="abc"></p>
</body>
</html>
答:
当你乘以 10 时,多余的位会四舍五入,幸运的是,这最终会产生整数。但这并不总是可以保证的,它只是恰好适用于这两个数字。
正确的解决方案是在乘以 10 后使用显式舍入。
let y = (Math.round(0.2 * 10) + Math.round(0.1 * 10)) / 10;
document.getElementById("abc").innerHTML = "0.2 + 0.1 = " + y;
<!DOCTYPE html>
<html>
<body>
<p id="abc"></p>
</body>
</html>
评论
您似乎在混合碱基,而没有指定何时使用哪一个碱基。了解IEEE754实现将在每次操作后提供正确的舍入操作也会有所帮助。
正如您所指出的,0.1 不能完全表示为二进制分数。最接近的值(对于 64 位浮点数)为:
0.1000000000000000055511151231257827021181583404541015625
当你说“0.00011 0011 0011 0011...0011“,如果说这些是以 2 为基数的数字,而不是十进制数字,会有所帮助。
当你说 0.1 * 10 时,你实际上是在要求:
10 * 0.1000000000000000055511151231257827021181583404541015625
计算机进行精确的计算,然后将其四舍五入到最接近的可表示浮点数。接近 1.0 的其他值(可以用 64 位二进制浮点数准确表示)是:
0.99999999999999988897769753748434595763683319091796875
1.0000000000000002220446049250313080847263336181640625
它们都与数字相距约 1.7e-16,而与选择 1 相关的误差仅为 ~0.5e-16。因此,FPU 应选择 1。
这同样适用于 0.2,它四舍五入到 2。
不幸的是,我不认为 Javascript 会公开许多有用的原语来查看正在发生的事情,所以我一直在使用 Python,因为它非常适合交互式探索这样的事情。
# arbitrary precision decimal
from decimal import Decimal as D
# see https://en.cppreference.com/w/c/numeric/math/nextafter
from math import inf, nextafter
print(D(0.1))
# => 0.1000000000000000055511151231257827021181583404541015625
print(D(nextafter(1, inf)))
# => 1.0000000000000002220446049250313080847263336181640625
print(D(nextafter(1, -inf)))
# => 0.99999999999999988897769753748434595763683319091796875
print(D(nextafter(1, -inf)) - D(0.1)*10)
# => -1.665334536935156540423631668E-16
希望这能让您对内部发生的事情有所了解!
评论
好吧,乘以任何数字,只要两个数字都是整数,就可以进行操作。10 是一个很好的数字,因为只需 10 的幂即可将小数位转换为整数部分。 去掉小数位很有帮助的原因是,二进制可以更好地表示数字,例如 1/3 不表示为 0.333...... 因为它是多余的。二进制发生相同的问题,但由于它使用 2 的幂和其他表示小数的方法,因此数字不同,最终它必须做一些事情。这就是为什么 0.1 + 0.2 = 0.300...4。我不一定确定为什么你会认为应该使用 10^10,它可能与 0.300...4 有关,但浮点答案很可能是错误的,所以这就是为什么方程的项相乘,而不是答案,还可能发生更多的浮点问题,留下更糟糕的结果。这是我的函数,我喜欢用它来解决烦人的算术浮点问题。
//z = 1 or 0 (+ or -)
function float_operator(x, y, z){
const precision = Math.max((Number.isNaN(x) ? 0 : (x.toString().split(".")[1] || "").length),(Number.isNaN(y) ? 0 : (y.toString().split(".")[1] || "").length)) || 1;
const multiplier = Math.pow(10, precision);
const result = z === 1 ? (x * multiplier + y * multiplier) / multiplier : (x * multiplier - y * multiplier) / multiplier;
return result;
}
//> float_operator(0.1, 0.2, 1)
//> 0.3
评论
10**10
Because of Floating point arithmetic in Javascript,
请注意,这不是 Javascript 的东西,而是计算机存储数字的方式。IEEE 754 认证。例如。让我们试试 Rust 示例 ->println!("{}", 0.1 + 0.2);
=0.30000000000000004