什么是一个完全像Math.floorMod()的方法,但浮点数而不是整数?

问题描述 投票:3回答:2

以下是Math.floorMod(x, 5)的一组输入和输出。

int x;

inputs:
x = -15 | -14 | -13 | -12 | -11
    -10 | -09 | -08 | -07 | -06
    -05 | -04 | -03 | -02 | -01
    +00 | +01 | +02 | +03 | +04*
    +05 | +06 | +07 | +08 | +09
    +10 | +11 | +12 | +13 | +14

outputs:
   *+00 | +01 | +02 | +03 | +04

需要明确的是,第1列中的所有输入都会导致第1列中的输出等。

我希望能用漂浮物做到这一点,但我一直无法找到任何Math.relevantMethod()来帮助我。这个想法是所有花车应该映射到0y(第二个参数)Pac-Man风格之间的一系列花车。

java floating-point modulo
2个回答
3
投票

Solution

我花了一段时间来开发算法并计算出所有的扭结,但现在是这样。我把它命名为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);
}

这是floatMod(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.8相当于输入(double)0.8f,它不如0.8d精确。即使您将输出转换为浮点数,也会获得更精确的结果。还要注意,离0.0 x越远,舍入误差就越大。这就是浮动的工作原理。所以...

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

Algorithm explanation

如果您对x - Math.floor(x/y) * y算法的工作原理感兴趣,我会尽力解释。让我们使用上面的floatMod(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。

接下来,在这种情况下,qazxsw point = 2.0)在数字线x上取一个给定的位置,并根据块给出它的含义。所以,2.0是块0的结束和块1的开始,所以2.0 / y = 1.0。

我们会说x / y = c;

1.0 / y→0.5c,1.0为半块 3.2 / y→1.6c -2.4 / y→-1.2c 等等

接下来,x/y意味着我们所处的任何一块,将c减少到所述块的开头。换句话说,哪一个是x in?

0.5c→0.0c 1.6c→1.0c -1.2c→-2.0c

接下来,它再次将结果乘以y,以x为单位将其返回。

0.0c *和→0.0 1.0c *和→2.0 -2.0c *和→-4.0

最后,它只需要取这个值并计算x离它的距离,就像它距离块的开头x有多远?

查看它的另一种方式:它需要在x中减去多余的块,因此它会计算出多少块向前或向后x来自块0并删除该量。这使它保持在0和y的范围内。

1.0 - 0.0 → 1.0 3.2 - 2.0 → 1.2 -2.4 - -4.0 → 1.6


(呃......好吧,在写完算法的大部分解释之后,我意识到有一种简化它的方法。在我做了之后,我意识到它实际上与floorMod算法完全相同,只是浮点数。我我在这里表现得就像是某种学者,当我所做的一切都是从我的鼻子底下采取了一个额外的步骤时发现了所有事物的统一理论。我确实承诺我绞尽脑汁从头开始研究这一点。

我原来的算法是Math.floor(c),在某个时刻解释起来非常混乱。我仍然很高兴我写了这篇文章,因为我相信这是一个很好的信息而且非常有趣。)


0
投票

使用浮点esp模数的一个问题是您会看到显着的表示错误。你最好做的是对结果或计算进行舍入,以便获得理智的结果。一种简单的方法是假设您只需要N位精度,例如6。

-Math.floor(x/y) * y + x

对于上面的例子你得到

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
© www.soinside.com 2019 - 2024. All rights reserved.