2 回答
TA贡献1900条经验 获得超5个赞
溶液
我花了一段时间来开发算法并解决所有问题,但事实就是如此。我配音.floatMod()
double floatMod(double x, double y){
// x mod y behaving the same way as Math.floorMod but with doubles
return (x - Math.floor(x/y) * y);
}
下面是一个浮点模型(x, 2.0d) 的输入和输出表作为示例。(我修复了整洁的轻微舍入错误。
double x;
inputs:
x = -4.0 | -3.6 | -3.2 | -2.8 | -2.4
-2.0 | -1.6 | -1.2 | -0.8 | -0.4
+0.0 | +0.4 | +0.8 | +1.2 | +1.6*
+2.0 | +2.4 | +2.8 | +3.2 | +3.6
+4.0 | +4.4 | +4.8 | +5.2 | +5.6
outputs:
*+0.0 | +0.4 | +0.8 | +1.2 | +1.6
下面是一些其他示例。
floatMod(0.1f, 1f); //returns: 0.1
floatMod(1.1f, 1f); //returns: 0.100000024 aka 0.1 + 0.000000024
floatMod(2.1f, 1f); //returns: 0.099999905 aka 0.1 - 0.000000095
floatMod(10000.1f, 1f); //returns: 0.099609375 aka 0.1 - 0.000390625
floatMod(0.1d, 1d); //returns: 0.1
floatMod(1.1d, 1d); //returns: 0.10000000000000009 aka 0.1 + 0.00000000000000009
floatMod(2.1d, 1d); //returns: 0.10000000000000009 aka 0.1 + 0.00000000000000009
floatMod(10000.1d, 1d); //returns: 0.10000000000036380 aka 0.1 - 0.00000000000036380
算法说明
如果您对算法的工作原理感兴趣,我会尽力解释。让我们使用上面的例子。x - Math.floor(x/y) * yfloatMod(x, 2.0d)
首先,取 x 的可能值的以下数行:
●------------------------○
| | | |
-2.4 -2.0 -1.6 -1.2 -0.8 -0.4 +0.0 +0.4 +0.8 +1.2 +1.6 +2.0 +2.4 +2.8 +3.2 +3.6 +4.0 +4.4
垂直线之间的空间表示在两个方向上并排堆叠的长度 y 块。填充圆表示包含,而空心圆表示排他性,上面所示的圆包含由虚线表示的块 0。
接下来,(在本例中为 y = 2.0)在数字线 x 上采用给定位置,并给出块的数量。因此,2.0 是块 0 的末尾和块 1 的开头,因此 2.0/y = 1.0。x/y
我们会说 x/y = c;
1.0/y → 0.5c 作为 1.0 是半块
3.2/y → 1.6c
-2.4/y → -1.2c
等。
接下来,意味着无论我们处于哪个区块中,都将c减少到所述区块的开头。换句话说,哪一个是x?Math.floor(c)
0.5c → 0.0c
1.6c → 1.0c
-1.2c → -2.0c
接下来,它将结果再次乘以y,以x的形式将其取回。
0.0c * y → 0.0
1.0c * y → 2.0
-2.0c * y → -4.0
最后,它只取这个值并计算x离它有多远,就像x离它所在的块的开头有多远一样?
另一种看待它的方式:它需要减去x中的额外块,所以它从块0中计算出x向前或向后多少块,并删除该数量。这使其保持在 0 和 y 的范围内。
1.0 - 0.0 → 1.0
3.2 - 2.0 → 1.2
-2.4 - -4.0 → 1.6
(呃...好吧,在写完了算法的大部分解释之后,我意识到有一种方法可以简化它。在我这样做之后,我意识到它实际上与floorMod算法完全相同,只是使用浮点数。我在这里表现得像某种学者,他发现了万物的统一理论,而我所做的只是从我眼皮底下的东西多走了一步。我保证,我绞尽脑汁从头开始开发它。
我最初的算法在某一时刻变得非常混乱。我仍然很高兴我写了这篇文章,因为我相信这是很好的信息,而且很有趣。-Math.floor(x/y) * y + x
TA贡献1735条经验 获得超5个赞
对模量使用浮点 esp 的一个问题是,您会看到明显的表示错误。你最好做的是舍入结果或计算,这样你就能得到理智的结果。一个简单的方法是假设你只需要N个精度数字,例如6。
public static double floorMod(double x, double y) {
return Math.floorMod(Math.round(x * 1e6), Math.round(y * 1e6)) / 1e6;
}
对于上面的例子,你得到
floorMod(0.1f, 1f); //returns: 0.1
floorMod(1.1f, 1f); //returns: 0.1
floorMod(2.1f, 1f); //returns: 0.1
floorMod(10000.1f, 1f); //returns: 0.099609 due to the limits of float.
floorMod(0.1d, 1d); //returns: 0.1
floorMod(1.1d, 1d); //returns: 0.1
floorMod(2.1d, 1d); //returns: 0.1
floorMod(10000.1d, 1d); //returns: 0.1
另一种方法是指定精度
public static double floorMod(double x, double y, double precision) {
double factor = Math.round(1 / precision);
return Math.floorMod(Math.round(x * factor), Math.round(y * factor)) / factor;
}
floorMod(10000.1f, 1f, 0.1); // returns 0.1
添加回答
举报