Strange values ​​for mathematical expression

I am developing a game that works on Windows and Android, but I have a problem that I cannot solve. Basically I have a 4x5 grid with some buttons, and these buttons are filled every second with a random number, which should be 2, 4 or 8. If you click on two buttons with the same number, the amount will be calculated. This is a firemonkey project.

The game works great, but you can see this problem in the figures below. When I run the game on my Windows computer, it generates 2, 4 or 8. Under the android, It generates 2, 4, 7 and 8. Random numbers are created this way:

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

This variable contains the number that will be displayed on the button. Why do I have different results on Windows and Android? These are two screenshots.

I am sure that the function is correct, because I built it (exp (ln (2) * (1 + x)) = http://prnt.sc/dmdc3u ) and when x is 0.1 or 2 (= when random the number is 0.1 or 2). Could this be a compiler problem?

Note. I already solved this problem using a workaround that you can see below, but first I used the code that you can see in the problem and I would like to understand what is happening.

 valueToOutput := Trunc(Exp(Ln(2) * (1+Random(3)))); //this will always give 2, 4 or 8 if valueToOutput = 7 then valueToOutput := valueToOutput + 1; 
+6
source share
3 answers

Floating-point calculations are repeated for the same input for the same floating-point control state.

With three separate inputs, your expression should have three different possible outputs. Therefore, the only explanation is that something is changing the state of the floating point control, for example. rounding mode, precision, etc. during program execution.

If floating point arithmetic can do the calculation exactly, you don't need to round to an integer. But if you have to round, at least round to the nearest using Round , not Trunc .

However, this is a categorically incorrect way to perform the discrete selection task randomly from 2, 4, and 8. Do it like this:

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

Another way is to put the possible outputs into an array, and then select them as follows:

 Result := arr[Random(3)]; 

This becomes more attractive when there are more options to choose from.

The golden rule is that if you can avoid using floating point, do it. Floating point is slower and harder to reason than integer arithmetic. Use it only when necessary.

+6
source

Conducting this as an answer because it does not match the comments

I know almost nothing about androïd, but I would use the following approach on Windows to narrow it down. Probably a similar approach exists for Androïd.

  • Store temporary calculations in global variables. This makes it easy to extract the values ​​from crashdump that we are about to take. Raise an exception if you push the wrong value.
  • Attach procdump to the running process. Procdump from Sysinternals allows you to create a dump file for each detected exception. The command line will be similar to procdump -ma -e 1 Project1.exe
  • Run the calculation and wait for its exception.
  • Analyze the dump. The values ​​of tmp variables can be retrieved from memory.

the code

 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; 

Variable Layout Example

Variable Layout Example

+2
source

We do not see all this. For example, you say that windows generate 2, 4 or 8, but we see 16 in the grid. You say that Android generates 7, but we see 14 in the grid. So it’s clear that you are multiplying by 2 places. My analysis will be that since 2 * 4 is 8, the Android version only generates 2, 4 and 7 (not 8), as, as David says, you only have 3 initial states, so there should only be Three end states. Using Trunc in this situation requires trouble. I think that everything will be facilitated by using Round instead of Trunc like this.

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

However, as David says, this is the wrong way to do this.

0
source

Source: https://habr.com/ru/post/1013404/


All Articles