数学表达式的奇怪值

时间:2016-12-21 22:16:28

标签: android delphi firemonkey

我正在开发一款适用于Windows和Android的游戏,但它有一个我无法解决的问题。基本上我有一个带有一些按钮的4x5网格,这些按钮每秒填充一个必须为2,4或8的随机数。如果点击两个具有相同数字的按钮,则计算总和。这是一个firemonkey项目。

游戏运行正常,但您可以在下面的图片中看到问题。当我在我的Windows机器上运行游戏时,它会生成2,4或8.在android下它生成2,4, 7 和8.以这种方式创建随机数:

valueToOutput := Trunc(Exp(Ln(2) * (1+Random(3))));

该变量保存按钮中显示的数字。为什么我在windows和android中得到不同的结果?这是两个截图

我确信该函数是正确的,因为我已经绘制了它(exp(ln(2)*(1 + x))= http://prnt.sc/dmdc3u)并且当x是0,1或2时(=当随机数是0,1或2)。这可能是编译器的问题吗?

注意:我已经使用您在下面看到的解决方法解决了这个问题,但起初我使用了您可以在问题中看到的代码,我想了解发生了什么

valueToOutput := Trunc(Exp(Ln(2) * (1+Random(3))));

//this will always give 2, 4 or 8
if valueToOutput = 7 then
 valueToOutput := valueToOutput + 1;

3 个答案:

答案 0 :(得分:6)

对于相同的浮点控制状态,浮点计算对于相同的输入是可重复的。

有三个不同的输入,因此您的表达式应具有三个不同的可能输出。因此,唯一的解释是某些东西改变浮点控制状态,例如,程序执行期间的舍入模式,精度等。

如果浮点运算可以精确地执行计算,则不需要舍入为整数。但是如果你必须使用Round而不是Trunc至少四舍五入到最近。

也就是说,这绝对是执行随机选择2,4和8之一的离散任务的错误方法。这样做是这样的:

case Random(3) of
0:
  Result := 2;
1:
  Result := 4;
2:
  Result := 8;
end;

另一种方法是将可能的输出放入一个数组中,然后选择它们:

Result := arr[Random(3)];

当有更多值可供选择时,这会变得更有吸引力。

一条黄金法则是,如果你可以避免使用浮点数那么做。浮点比整数算术更慢,更难以推理。仅在必要时使用它。

答案 1 :(得分:2)

将此作为答案发布,因为它不符合评论

我对androïd几乎一无所知,但我会在Windows上使用以下方法来缩小范围。可能,Androïd也存在类似的方法。

  1. 在全局变量中保留临时计算。这样可以更容易地从我们将要采用的故障转储中提取值。点击错误的值时会引发异常。
  2. 将procdump附加到正在运行的进程。来自Sysinternals的Procdump允许您为遇到的每个异常创建转储文件。命令行类似于procdump -ma -e 1 Project1.exe
  3. 运行计算并等待它引发异常。
  4. 分析转储。可以从内存中提取tmp变量的值。
  5. <强>代码

    var
      tmpRandom: Integer;
      tmpLn: Extended;
      tmpLnRandom: Extended;
      tmpExp: Extended;
      tmpTrunc: Int64;
    
    procedure TForm1.btn1Click(Sender: TObject);
    var
      I: Int64;
    begin
      while true do
      begin
          tmpRandom := Random(3);
          tmpLn := Ln(2);
          tmpLnRandom := tmpLn * (1+tmpRandom);
          tmpExp := Exp(tmpLnRandom);
          tmpTrunc := Trunc(tmpExp);
          I := tmpTrunc;
          if (I and 1 = 1) then
            raise Exception.CreateFmt('I = %0:d', [I]);
      end;
    end;
    

    变量的示例布局

    Example layout of variables

答案 2 :(得分:0)

我们没有看到整件事。例如,你说windows生成2,4或8,但我们在网格中看到16。你说Android生成7,但我们在网格中看到14。很明显,你在某个地方乘以2。我的分析是,因为2 * 4是8,安卓版本生成2,4和7,而不是8,因为正如大卫所说,你只有3个起始状态,所以必须只有三个结束状态。在这种情况下使用Trunc是一件麻烦事。我认为通过使用Round而不是Trunc来缓解事情,就像这样。

valueToOutput := Round(Exp(Ln(2) * (1+Random(3))));

如上所述,正如大卫所说,这不是正确的做法。