Hi Jason,
Thanks so much for your explanation. It is very helpful for me as I
can better understand what's going and heave a sign of relief that it
is not a coding problem on my part, which I was wondering. (rookie
modeler here...).
May I ask one question, which might also be a bit simple.
Given that the simulation has stopped (even when tick is at zero after
initialization), why is it that we see resulting value of the
floating-point operation still constantly changing. Is it because even
after the model has stopped, the model is actually still running, and
hence the iteration continues?
Great appreciation,
Phang
On Friday, September 9, 2016 at 11:09:03 PM UTC+8, Jason Bertsche wrote:
While perhaps surprising at first glance—I know it caught me
off-guard the first time I saw it—this is working as expected.
I'll explain why.
When you query a patchset (or any agentset), those queries are run
on the patchset in a random iteration order. So `[self] of
(patch-set (patch 0 0) (patch 0 1))` will sometimes result in
`[(patch 0 0) (patch 0 1)]`, and, other times, will result in
`[(patch 0 1) (patch 0 0)]`. That is the first component of the
problem.
Now, on to the second component: IEEE floating point math. The
IEEE standard is followed for representing floating point numbers
in essentially every significant programming language. However,
one of the nuances of the IEEE representation is that the order in
which operations are applied matters. Usually, you don't start to
see this until you have a very precise number (i.e. with a lot of
digits after the decimal point). For example, here's a brief
session I just did in the Haskell REPL:
ghci> let x = 0.000000000000000001
ghci> let y = 0.0000000000000000094
ghci> let z = 0.000000000000000003
ghci> x * y * z
2.82e-53
ghci> y * z * x
2.8199999999999994e-53
Humbug. Let's ask Scala:
scala> val x = 0.000000000000000001
x: Double = 1.0E-18
scala> val y = 0.0000000000000000094
y: Double = 9.4E-18
scala> val z = 0.000000000000000003
z: Double = 3.0E-18
scala> x * y * z
res0: Double = 2.82E-53
scala> y * z * x
res1: Double = 2.8199999999999994E-53
Okay, enough with the weird languages. Let's ask a mainstream
language; let's ask JavaScript:
jjs> var x = 0.000000000000000001
jjs> var y = 0.0000000000000000094
jjs> var z = 0.000000000000000003
jjs> x * y * z
2.82e-53
jjs> y * z * x
2.8199999999999994e-53
Welp... at least they're all consistently wrong.
As it turns out, this otherwise unexpected (and, frankly,
mathematical law breaking) behavior is, for example, why many
people urge avoiding floating point numbers
<https://urldefense.proofpoint.com/v2/url?u=http-3A__stackoverflow.com_a_3730040_5288538&d=CwMFaQ&c=yHlS04HhBraes5BQ9ueu5zKhE7rtNXt_d012z2PA6ws&r=sxGjFFyk2A6rYHwAdDtnqeTKj3cEcXDGDo_G9va2ruI&m=UJIbYwNnMHzzX0An8VO5WIX1w6dpTngCNi-bkb9HB5U&s=FMSrUhE3i_5gwaHpkvaDbN-DGx955ey8djeBwHCx-h0&e=>
for representing amounts of money in software; sometimes, little
bits of money here or there just disappear.
So what we have is that patchsets are iterated over randomly, and
the order in which math operations are applied matters.
Therefore, it is predictable, then, that asking the same agentset
to perform the same mathematical operations multiple times will
sometimes lead to very, very slightly different results.
As for solutions to this problem, indeed, using `precision` is a
way to deal with it. Another option would be to sort the values
before averaging them, since that would lead to a consistent
ordering of the numbers, and, therefore, a consistent order of
mathematical operations.
On 09/08/2016 10:26 PM, Phang wrote:
Dear All,
I have a persistent problem that some of you might have
encountered and I am hoping if there could be some that could
help me.
I am developing a relatively simple Cellular Automata. Model runs
fine except when I ask patches to calculate mean (or even sum) of
more than 8 neighbors. The problem appears that the decimal
points usually 10th decimal place onwards keeps changing (EVEN)
after simulation has stopped running! As a matter of fact, when I
calculate mean just after initialization (therefore only SETUP,
but before GO) at tick = 0, I would get constantly-changing
values for the calculation output. This is quite weird to me and
I have tried a few other PCs. Mac and LINUX not yet, but I do not
expect problem to be that complicated.
Naturally, I used the native in-radius function to increase
neighborhood sizes (in radial fashion) and also workarounds such
as patches-at to create a larger Von Neumann neighborhood,
but the same problems occur. Problem is avoided when native
neighbor and neighbor4 functions are used.
I know I could potentially use Precision to constrain the values,
but I realised that at the back (when i did a check) the values
are still changing.
Deeply appreciate any insight on this,
Best Regards,
Phang
--
You received this message because you are subscribed to the
Google Groups "netlogo-devel" group.
To unsubscribe from this group and stop receiving emails from it,
send an email to [email protected] <javascript:>.
For more options, visit https://groups.google.com/d/optout
<https://urldefense.proofpoint.com/v2/url?u=https-3A__groups.google.com_d_optout&d=CwMFaQ&c=yHlS04HhBraes5BQ9ueu5zKhE7rtNXt_d012z2PA6ws&r=sxGjFFyk2A6rYHwAdDtnqeTKj3cEcXDGDo_G9va2ruI&m=8G_vRa06ok9y5eR6o0ZRbvNWLW9o5qM16EUIsVV3Y5U&s=UVSbYh6DGUqGshYj1SfQG-2YlXYfzc47xw3xWxc_l5k&e=>.
--
You received this message because you are subscribed to the Google
Groups "netlogo-devel" group.
To unsubscribe from this group and stop receiving emails from it, send
an email to [email protected]
<mailto:[email protected]>.
For more options, visit https://groups.google.com/d/optout
<https://urldefense.proofpoint.com/v2/url?u=https-3A__groups.google.com_d_optout&d=CwMFaQ&c=yHlS04HhBraes5BQ9ueu5zKhE7rtNXt_d012z2PA6ws&r=sxGjFFyk2A6rYHwAdDtnqeTKj3cEcXDGDo_G9va2ruI&m=UJIbYwNnMHzzX0An8VO5WIX1w6dpTngCNi-bkb9HB5U&s=WTDfLSxYPihPu7t_D1vHCZElerlyEDZq_1HZmyla5uU&e=>.