为什么这个公式在循环 31 次后就失效了?
Why does this formula break after 31 loops?
我有以下代码:
public float SnapValueToCoreBlock(float ValueToSnap)
{
ValueToSnap = ValueToSnap + 0.5f;
ValueToSnap = Mathf.Floor(ValueToSnap);
return ValueToSnap;
}
float floatvar;
int intvar;
for (int z = 0; z < 100; z++)
{
floatvar = z + (Mathf.FloorToInt(0.499999f) + 1) * 0.499999f / (Mathf.FloorToInt(0.499999f) + 1);
intvar = (int)SnapValueToCoreBlock(floatvar);
}
我希望“intvar”在循环中始终等于“z”,但是在 31 次迭代后出现某种舍入误差并且当 z=32 intvar = 33 时,而不是 z=32 intvar = 32
从那时起,intvar 总是减 1,所以对于 z<32 intvar=z 和 z >31 intvar=z+1
我的预期结果总是 z = intvar,我不明白为什么当 z 达到 32 时会任意更改,如果有人能帮助我,我将不胜感激,在此先感谢。
我获取了您的代码并将它们从 Unity 转换为 .NET Framework。这涉及将 Mathf.FloorToInt(someFloat)
更改为 (int)Math.Floor(someFloat)
,将 Mathf.Floor(ValueToSnap)
更改为 (float)Math.Floor(ValueToSnap)
。我相信它会做同样的事情,但它绕过 double
.
我还在你的循环中插入了一个 WriteLine 语句:
for (int z = 0; z < 100; z++)
{
floatvar = z + ((int)Math.Floor(0.499999f) + 1) * 0.499999f / ((int)Math.Floor(0.499999f) + 1);
intvar = (int)SnapValueToCoreBlock(floatvar);
Debug.WriteLine($"FloatVar: {floatvar} Z: {z} IntVar: {intvar}");
}
我看到了同样的行为。
特别是,我在 z
== 9:
处看到中断
FloatVar: 8.499999 Z: 8 IntVar: 8
FloatVar: 9.499999 Z: 9 IntVar: 9
FloatVar: 10.5 Z: 10 IntVar: 10
FloatVar: 11.5 Z: 11 IntVar: 11
在 z
== 32:
FloatVar: 30.5 Z: 30 IntVar: 30
FloatVar: 31.5 Z: 31 IntVar: 31
FloatVar: 32.5 Z: 32 IntVar: 33
FloatVar: 33.5 Z: 33 IntVar: 34
即使我将输出的精度扩展到超出浮点数的精度:
Debug.WriteLine($"FloatVar: {floatvar:0.0000000000000000000} Z: {z} IntVar: {intvar}");
我看到了相同的行为,在 32 以下四舍五入并在其以上四舍五入。
那我大大简化了你的计算:
floatvar2 = z + 0.499999f;
intvar2 = (int)SnapValueToCoreBlock(floatvar2);
我仍然看到相同的行为。
所以,看起来是:
anInteger + 0.499999f + 0.5f;
对于 anInteger < 32
的值, 小于 anInteger + 1.0f
,对于值 anInteger >= 32
,则等于或大于 anInteger + 1.0f
。而且,你知道吗,这并不让我感到惊讶。您正处于浮点精度的边缘(请记住,浮点数的精度约为 6-9 位:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types)。当您从 31 变为 32 时,您最终设置了另一个位,这可能是造成差异的原因。
最终评论 (作为更新)
你说 “0.4999999 是我用于碰撞的变量”。您需要阅读如何比较浮点值,以及如何正确使用 epsilon。这是我发现的一件事 (https://bitbashing.io/comparing-floats.html)。它以 C++ 为重点,但似乎可以解决这些问题。作为最后的评论,如果您曾经使用测量值(例如在化学过程控制系统中),则需要在建立 epsilon.
我有以下代码:
public float SnapValueToCoreBlock(float ValueToSnap)
{
ValueToSnap = ValueToSnap + 0.5f;
ValueToSnap = Mathf.Floor(ValueToSnap);
return ValueToSnap;
}
float floatvar;
int intvar;
for (int z = 0; z < 100; z++)
{
floatvar = z + (Mathf.FloorToInt(0.499999f) + 1) * 0.499999f / (Mathf.FloorToInt(0.499999f) + 1);
intvar = (int)SnapValueToCoreBlock(floatvar);
}
我希望“intvar”在循环中始终等于“z”,但是在 31 次迭代后出现某种舍入误差并且当 z=32 intvar = 33 时,而不是 z=32 intvar = 32
从那时起,intvar 总是减 1,所以对于 z<32 intvar=z 和 z >31 intvar=z+1
我的预期结果总是 z = intvar,我不明白为什么当 z 达到 32 时会任意更改,如果有人能帮助我,我将不胜感激,在此先感谢。
我获取了您的代码并将它们从 Unity 转换为 .NET Framework。这涉及将 Mathf.FloorToInt(someFloat)
更改为 (int)Math.Floor(someFloat)
,将 Mathf.Floor(ValueToSnap)
更改为 (float)Math.Floor(ValueToSnap)
。我相信它会做同样的事情,但它绕过 double
.
我还在你的循环中插入了一个 WriteLine 语句:
for (int z = 0; z < 100; z++)
{
floatvar = z + ((int)Math.Floor(0.499999f) + 1) * 0.499999f / ((int)Math.Floor(0.499999f) + 1);
intvar = (int)SnapValueToCoreBlock(floatvar);
Debug.WriteLine($"FloatVar: {floatvar} Z: {z} IntVar: {intvar}");
}
我看到了同样的行为。
特别是,我在 z
== 9:
FloatVar: 8.499999 Z: 8 IntVar: 8
FloatVar: 9.499999 Z: 9 IntVar: 9
FloatVar: 10.5 Z: 10 IntVar: 10
FloatVar: 11.5 Z: 11 IntVar: 11
在 z
== 32:
FloatVar: 30.5 Z: 30 IntVar: 30
FloatVar: 31.5 Z: 31 IntVar: 31
FloatVar: 32.5 Z: 32 IntVar: 33
FloatVar: 33.5 Z: 33 IntVar: 34
即使我将输出的精度扩展到超出浮点数的精度:
Debug.WriteLine($"FloatVar: {floatvar:0.0000000000000000000} Z: {z} IntVar: {intvar}");
我看到了相同的行为,在 32 以下四舍五入并在其以上四舍五入。
那我大大简化了你的计算:
floatvar2 = z + 0.499999f;
intvar2 = (int)SnapValueToCoreBlock(floatvar2);
我仍然看到相同的行为。
所以,看起来是:
anInteger + 0.499999f + 0.5f;
对于 anInteger < 32
的值, 小于 anInteger + 1.0f
,对于值 anInteger >= 32
,则等于或大于 anInteger + 1.0f
。而且,你知道吗,这并不让我感到惊讶。您正处于浮点精度的边缘(请记住,浮点数的精度约为 6-9 位:https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/builtin-types/floating-point-numeric-types)。当您从 31 变为 32 时,您最终设置了另一个位,这可能是造成差异的原因。
最终评论 (作为更新)
你说 “0.4999999 是我用于碰撞的变量”。您需要阅读如何比较浮点值,以及如何正确使用 epsilon。这是我发现的一件事 (https://bitbashing.io/comparing-floats.html)。它以 C++ 为重点,但似乎可以解决这些问题。作为最后的评论,如果您曾经使用测量值(例如在化学过程控制系统中),则需要在建立 epsilon.