← Back to team overview

widelands-dev team mailing list archive

[Merge] lp:~widelands-dev/widelands/terrain_affinity_as_int into lp:widelands

 

GunChleoc has proposed merging lp:~widelands-dev/widelands/terrain_affinity_as_int into lp:widelands.

Commit message:
Fix desyncs caused by floating point arithmetic

- Floating point arithmetic can become inconsistent across platforms/compilers/hardware
- Upscale terrain affinity constants to turn them into ints
- Upscale the result of the affinity calculation and then use std::floor to cut it off at
  largely reduced precision for consistent rounding behavior. This should fix inconsistencies
  with double rounding and rounding direction.
- Catch a possible division by 0.


Requested reviews:
  Widelands Developers (widelands-dev)
Related bugs:
  Bug #1800338 in widelands: "Shipnames mismatch in multiplayer"
  https://bugs.launchpad.net/widelands/+bug/1800338

For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/terrain_affinity_as_int/+merge/358299

Fix cross-platform desyncs caused by floating point arithmetic. Further reading:

https://randomascii.wordpress.com/2012/03/21/intermediate-floating-point-precision/

https://arxiv.org/abs/cs/0701192

Test savegame:

https://bugs.launchpad.net/widelands/+bug/1800338/comments/3

With this savegame, I can cause a multiplayer desync on the same machine by compiling one copy of Widelands with GCC and another copy with Clang. The desync happens before the ship finishes building. With this branch, the desync is gone.
-- 
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/terrain_affinity_as_int into lp:widelands.
=== modified file 'data/tribes/immovables/berry_bushes/blueberry/init.lua'
--- data/tribes/immovables/berry_bushes/blueberry/init.lua	2018-02-17 15:41:29 +0000
+++ data/tribes/immovables/berry_bushes/blueberry/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 45, -- Temperature is in arbitrary units.
-   preferred_humidity = 0.75,  -- In percent (1 being very wet).
-   preferred_fertility = 0.4,  -- In percent (1 being very fertile).
-   pickiness = 0.15,           -- Lower means it is less picky, i.e. it can deal better.
+   preferred_humidity = 750,   -- Values between 0 and 1000 (1000 being very wet).
+   preferred_fertility = 400,  -- Values between 0 and 1000 (1000 being very fertile).
+   pickiness = 15,             -- Lower means it is less picky, i.e. it can deal better.
 }
 
 tribes:new_immovable_type {

=== modified file 'data/tribes/immovables/berry_bushes/currant_black/init.lua'
--- data/tribes/immovables/berry_bushes/currant_black/init.lua	2018-02-17 15:41:29 +0000
+++ data/tribes/immovables/berry_bushes/currant_black/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 120,
-   preferred_humidity = 0.1,
-   preferred_fertility = 0.2,
-   pickiness = 0.5,
+   preferred_humidity = 100,
+   preferred_fertility = 200,
+   pickiness = 50,
 }
 
 tribes:new_immovable_type {

=== modified file 'data/tribes/immovables/berry_bushes/currant_red/init.lua'
--- data/tribes/immovables/berry_bushes/currant_red/init.lua	2018-02-17 15:41:29 +0000
+++ data/tribes/immovables/berry_bushes/currant_red/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 95,
-   preferred_humidity = 0.55,
-   preferred_fertility = 0.45,
-   pickiness = 0.4,
+   preferred_humidity = 550,
+   preferred_fertility = 450,
+   pickiness = 40,
 }
 
 tribes:new_immovable_type {

=== modified file 'data/tribes/immovables/berry_bushes/desert_hackberry/init.lua'
--- data/tribes/immovables/berry_bushes/desert_hackberry/init.lua	2018-02-17 15:41:29 +0000
+++ data/tribes/immovables/berry_bushes/desert_hackberry/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 170, -- Temperature is in arbitrary units.
-   preferred_humidity = 0.05,   -- In percent (1 being very wet).
-   preferred_fertility = 0.05,  -- In percent (1 being very fertile).
-   pickiness = 0.3,             -- Lower means it is less picky, i.e. it can deal better.
+   preferred_humidity = 50,     -- Values between 0 and 1000 (1000 being very wet).
+   preferred_fertility = 50,    -- Values between 0 and 1000 (1000 being very fertile).
+   pickiness = 30,             -- Lower means it is less picky, i.e. it can deal better.
 }
 
 tribes:new_immovable_type {

=== modified file 'data/tribes/immovables/berry_bushes/juniper/init.lua'
--- data/tribes/immovables/berry_bushes/juniper/init.lua	2018-04-21 15:27:00 +0000
+++ data/tribes/immovables/berry_bushes/juniper/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 115,
-   preferred_humidity = 0.6,
-   preferred_fertility = 0.2,
-   pickiness = 0.15,
+   preferred_humidity = 600,
+   preferred_fertility = 200,
+   pickiness = 15,
 }
 
 tribes:new_immovable_type {

=== modified file 'data/tribes/immovables/berry_bushes/raspberry/init.lua'
--- data/tribes/immovables/berry_bushes/raspberry/init.lua	2018-02-17 15:41:29 +0000
+++ data/tribes/immovables/berry_bushes/raspberry/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 50,
-   preferred_humidity = 0.3,
-   preferred_fertility = 0.05,
-   pickiness = 0.1,
+   preferred_humidity = 300,
+   preferred_fertility = 50,
+   pickiness = 10,
 }
 
 tribes:new_immovable_type {

=== modified file 'data/tribes/immovables/berry_bushes/sea_buckthorn/init.lua'
--- data/tribes/immovables/berry_bushes/sea_buckthorn/init.lua	2018-02-17 15:41:29 +0000
+++ data/tribes/immovables/berry_bushes/sea_buckthorn/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 100, -- Temperature is in arbitrary units.
-   preferred_humidity = 0.95,   -- In percent (1 being very wet).
-   preferred_fertility = 0.05,  -- In percent (1 being very fertile).
-   pickiness = 0.15,            -- Lower means it is less picky, i.e. it can deal better.
+   preferred_humidity = 950,    -- Values between 0 and 1000 (1000 being very wet).
+   preferred_fertility = 50,    -- Values between 0 and 1000 (1000 being very fertile).
+   pickiness = 15,              -- Lower means it is less picky, i.e. it can deal better.
 }
 
 tribes:new_immovable_type {

=== modified file 'data/tribes/immovables/berry_bushes/strawberry/init.lua'
--- data/tribes/immovables/berry_bushes/strawberry/init.lua	2018-05-02 08:13:07 +0000
+++ data/tribes/immovables/berry_bushes/strawberry/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 110, -- Temperature is in arbitrary units.
-   preferred_humidity = 0.6,    -- In percent (1 being very wet).
-   preferred_fertility = 0.8,   -- In percent (1 being very fertile).
-   pickiness = 0.2,             -- Lower means it is less picky, i.e. it can deal better.
+   preferred_humidity = 600,    -- Values between 0 and 1000 (1000 being very wet).
+   preferred_fertility = 800,   -- Values between 0 and 1000 (1000 being very fertile).
+   pickiness = 20,             -- Lower means it is less picky, i.e. it can deal better.
 }
 
 tribes:new_immovable_type {

=== modified file 'data/world/immovables/trees/alder/init.lua'
--- data/world/immovables/trees/alder/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/alder/init.lua	2018-11-05 08:10:25 +0000
@@ -4,15 +4,15 @@
    -- Temperature is in arbitrary units.
    preferred_temperature = 125,
 
-   -- In percent (1 being very wet).
-   preferred_humidity = 0.65,
-
-   -- In percent (1 being very fertile).
-   preferred_fertility = 0.6,
-
-   -- A value in [0, 1] that defines how well this can deal with non-ideal
+   -- Value between 0 and 1000 (1000 being very wet).
+   preferred_humidity = 650,
+
+   -- Values between 0 and 1000 (1000 being very fertile).
+   preferred_fertility = 600,
+
+   -- A value in [0, 100] that defines how well this can deal with non-ideal
    -- situations. Lower means it is less picky, i.e. it can deal better.
-   pickiness = 0.6,
+   pickiness = 60,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/aspen/init.lua'
--- data/world/immovables/trees/aspen/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/aspen/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 100,
-   preferred_humidity = 0.6,
-   preferred_fertility = 0.7,
-   pickiness = 0.8,
+   preferred_humidity = 600,
+   preferred_fertility = 700,
+   pickiness = 80,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/beech/init.lua'
--- data/world/immovables/trees/beech/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/beech/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 110,
-   preferred_humidity = 0.4,
-   preferred_fertility = 0.6,
-   pickiness = 0.6,
+   preferred_humidity = 400,
+   preferred_fertility = 600,
+   pickiness = 60,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/birch/init.lua'
--- data/world/immovables/trees/birch/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/birch/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 80,
-   preferred_humidity = 0.65,
-   preferred_fertility = 0.6,
-   pickiness = 0.6,
+   preferred_humidity = 650,
+   preferred_fertility = 600,
+   pickiness = 60,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/cirrus/init.lua'
--- data/world/immovables/trees/cirrus/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/cirrus/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 110,
-   preferred_humidity = 0.15,
-   preferred_fertility = 0.95,
-   pickiness = 0.8,
+   preferred_humidity = 150,
+   preferred_fertility = 950,
+   pickiness = 80,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/larch/init.lua'
--- data/world/immovables/trees/larch/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/larch/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 50,
-   preferred_humidity = 0.8,
-   preferred_fertility = 0.45,
-   pickiness = 0.8,
+   preferred_humidity = 800,
+   preferred_fertility = 450,
+   pickiness = 80,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/liana/init.lua'
--- data/world/immovables/trees/liana/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/liana/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 120,
-   preferred_humidity = 0.2,
-   preferred_fertility = 0.7,
-   pickiness = 0.6,
+   preferred_humidity = 200,
+   preferred_fertility = 700,
+   pickiness = 60,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/maple/init.lua'
--- data/world/immovables/trees/maple/init.lua	2016-02-28 08:33:59 +0000
+++ data/world/immovables/trees/maple/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 110,
-   preferred_humidity = 0.55,
-   preferred_fertility = 0.8,
-   pickiness = 0.8,
+   preferred_humidity = 550,
+   preferred_fertility = 800,
+   pickiness = 80,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/mushroom_dark/init.lua'
--- data/world/immovables/trees/mushroom_dark/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/mushroom_dark/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 100,
-   preferred_humidity = 0.2,
-   preferred_fertility = 0.8,
-   pickiness = 0.8,
+   preferred_humidity = 200,
+   preferred_fertility = 800,
+   pickiness = 80,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/mushroom_green/init.lua'
--- data/world/immovables/trees/mushroom_green/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/mushroom_green/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 105,
-   preferred_humidity = 0.2,
-   preferred_fertility = 0.9,
-   pickiness = 0.8,
+   preferred_humidity = 200,
+   preferred_fertility = 900,
+   pickiness = 80,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/mushroom_red/init.lua'
--- data/world/immovables/trees/mushroom_red/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/mushroom_red/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 80,
-   preferred_humidity = 0.35,
-   preferred_fertility = 0.85,
-   pickiness = 0.6,
+   preferred_humidity = 350,
+   preferred_fertility = 850,
+   pickiness = 60,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/oak/init.lua'
--- data/world/immovables/trees/oak/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/oak/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 90,
-   preferred_humidity = 0.7,
-   preferred_fertility = 0.5,
-   pickiness = 0.6,
+   preferred_humidity = 700,
+   preferred_fertility = 500,
+   pickiness = 60,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/palm_borassus/init.lua'
--- data/world/immovables/trees/palm_borassus/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/palm_borassus/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 180,
-   preferred_humidity = 0.4,
-   preferred_fertility = 0.4,
-   pickiness = 0.6,
+   preferred_humidity = 400,
+   preferred_fertility = 400,
+   pickiness = 60,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/palm_coconut/init.lua'
--- data/world/immovables/trees/palm_coconut/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/palm_coconut/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 130,
-   preferred_humidity = 0.5,
-   preferred_fertility = 0.6,
-   pickiness = 0.6,
+   preferred_humidity = 500,
+   preferred_fertility = 600,
+   pickiness = 60,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/palm_date/init.lua'
--- data/world/immovables/trees/palm_date/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/palm_date/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 160,
-   preferred_humidity = 0.5,
-   preferred_fertility = 0.5,
-   pickiness = 0.8,
+   preferred_humidity = 500,
+   preferred_fertility = 500,
+   pickiness = 80,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/palm_oil/init.lua'
--- data/world/immovables/trees/palm_oil/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/palm_oil/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 150,
-   preferred_humidity = 0.55,
-   preferred_fertility = 0.5,
-   pickiness = 0.8,
+   preferred_humidity = 550,
+   preferred_fertility = 500,
+   pickiness = 80,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/palm_roystonea/init.lua'
--- data/world/immovables/trees/palm_roystonea/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/palm_roystonea/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 160,
-   preferred_humidity = 0.6,
-   preferred_fertility = 0.6,
-   pickiness = 0.9,
+   preferred_humidity = 600,
+   preferred_fertility = 600,
+   pickiness = 90,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/rowan/init.lua'
--- data/world/immovables/trees/rowan/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/rowan/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 105,
-   preferred_humidity = 0.65,
-   preferred_fertility = 0.75,
-   pickiness = 0.8,
+   preferred_humidity = 650,
+   preferred_fertility = 750,
+   pickiness = 80,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/spruce/init.lua'
--- data/world/immovables/trees/spruce/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/spruce/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 60,
-   preferred_humidity = 0.7,
-   preferred_fertility = 0.5,
-   pickiness = 0.6,
+   preferred_humidity = 700,
+   preferred_fertility = 500,
+   pickiness = 60,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/twine/init.lua'
--- data/world/immovables/trees/twine/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/twine/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 95,
-   preferred_humidity = 0.2,
-   preferred_fertility = 0.4,
-   pickiness = 0.8,
+   preferred_humidity = 200,
+   preferred_fertility = 400,
+   pickiness = 80,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/umbrella_green/init.lua'
--- data/world/immovables/trees/umbrella_green/init.lua	2016-02-10 19:50:13 +0000
+++ data/world/immovables/trees/umbrella_green/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 110,
-   preferred_humidity = 0.2,
-   preferred_fertility = 0.85,
-   pickiness = 0.8,
+   preferred_humidity = 200,
+   preferred_fertility = 850,
+   pickiness = 80,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/immovables/trees/umbrella_red/init.lua'
--- data/world/immovables/trees/umbrella_red/init.lua	2016-02-29 17:10:00 +0000
+++ data/world/immovables/trees/umbrella_red/init.lua	2018-11-05 08:10:25 +0000
@@ -2,9 +2,9 @@
 
 terrain_affinity = {
    preferred_temperature = 90,
-   preferred_humidity = 0.15,
-   preferred_fertility = 0.825,
-   pickiness = 0.8,
+   preferred_humidity = 150,
+   preferred_fertility = 825,
+   pickiness = 80,
 }
 
 world:new_immovable_type{

=== modified file 'data/world/terrains/init.lua'
--- data/world/terrains/init.lua	2017-09-02 16:12:50 +0000
+++ data/world/terrains/init.lua	2018-11-05 08:10:25 +0000
@@ -131,17 +131,17 @@
 --
 --    **humidity**
 --        *Mandatory*. A terrain affinity constant. These are used to model how well
---        trees will grow on this terrain. Humidity is in percent (1 being very wet).
+--        trees will grow on this terrain. Values range from 1 - 1000 (1000 being very wet).
 --        Example::
 --
---            humidity = 0.6,
+--            humidity = 600,
 --
 --    **fertility**
 --        *Mandatory*. A terrain affinity constant. These are used to model how well
---        trees will grow on this terrain. Fertility is in percent (1 being very
+--        trees will grow on this terrain. Values range from 1 - 1000 (1000 being very
 --        fertile). Example::
 --
---            fertility = 0.7,
+--            fertility = 700,
 --
 
 ------------------------
@@ -167,8 +167,8 @@
    dither_layer = 340,
 
    temperature = 100,
-   humidity = 0.6,
-   fertility = 0.7,
+   humidity = 600,
+   fertility = 700,
 }
 
 
@@ -187,8 +187,8 @@
    textures = { pics_dir .. "summer/meadow2_00.png" },
    dither_layer = 350,
    temperature = 100,
-   humidity = 0.6,
-   fertility = 0.65,
+   humidity = 600,
+   fertility = 650,
 
 }
 
@@ -208,8 +208,8 @@
    textures = { pics_dir .. "summer/meadow3_00.png" },
    dither_layer = 350,
    temperature = 105,
-   humidity = 0.55,
-   fertility = 0.8,
+   humidity = 550,
+   fertility = 800,
 }
 
 
@@ -228,8 +228,8 @@
    textures = { pics_dir .. "summer/meadow4_00.png" },
    dither_layer = 350,
    temperature = 110,
-   humidity = 0.65,
-   fertility = 0.75,
+   humidity = 650,
+   fertility = 750,
 }
 
 
@@ -244,8 +244,8 @@
    textures = { pics_dir .. "summer/steppe_00.png" },
    dither_layer = 330,
    temperature = 100,
-   humidity = 0.4,
-   fertility = 0.4,
+   humidity = 400,
+   fertility = 400,
 }
 
 
@@ -260,8 +260,8 @@
    textures = { pics_dir .. "summer/steppe_barren_00.png" },
    dither_layer = 320,
    temperature = 100,
-   humidity = 0.15,
-   fertility = 0.15,
+   humidity = 150,
+   fertility = 150,
 }
 
 
@@ -276,8 +276,8 @@
    textures = { pics_dir .. "summer/mountain_meadow_00.png" },
    dither_layer = 160,
    temperature = 75,
-   humidity = 0.8,
-   fertility = 0.45,
+   humidity = 800,
+   fertility = 450,
 }
 
 world:new_terrain_type{
@@ -296,8 +296,8 @@
    textures = { pics_dir .. "summer/forested_mountain1_00.png" },
    dither_layer = 71,
    temperature = 50,
-   humidity = 0.75,
-   fertility = 0.5,
+   humidity = 750,
+   fertility = 500,
 }
 
 world:new_terrain_type{
@@ -316,8 +316,8 @@
    textures = { pics_dir .. "summer/forested_mountain2_00.png" },
    dither_layer = 71,
    temperature = 50,
-   humidity = 0.75,
-   fertility = 0.5,
+   humidity = 750,
+   fertility = 500,
 }
 
 world:new_terrain_type{
@@ -331,8 +331,8 @@
    textures = { pics_dir .. "summer/mountain1_00.png" },
    dither_layer = 70,
    temperature = 80,
-   humidity = 0.1,
-   fertility = 0.1,
+   humidity = 100,
+   fertility = 100,
 }
 
 
@@ -347,8 +347,8 @@
    textures = { pics_dir .. "summer/mountain2_00.png" },
    dither_layer = 70,
    temperature = 80,
-   humidity = 0.1,
-   fertility = 0.1,
+   humidity = 100,
+   fertility = 100,
 }
 
 
@@ -363,8 +363,8 @@
    textures = { pics_dir .. "summer/mountain3_00.png" },
    dither_layer = 70,
    temperature = 80,
-   humidity = 0.1,
-   fertility = 0.1,
+   humidity = 100,
+   fertility = 100,
 }
 
 
@@ -379,8 +379,8 @@
    textures = { pics_dir .. "summer/mountain4_00.png" },
    dither_layer = 70,
    temperature = 80,
-   humidity = 0.1,
-   fertility = 0.1,
+   humidity = 100,
+   fertility = 100,
 }
 
 world:new_terrain_type{
@@ -394,8 +394,8 @@
    textures = { pics_dir .. "summer/beach_00.png" },
    dither_layer = 60,
    temperature = 120,
-   humidity = 0.6,
-   fertility = 0.2,
+   humidity = 600,
+   fertility = 200,
 }
 
 world:new_terrain_type{
@@ -410,8 +410,8 @@
    dither_layer = 370,
    fps = 14,
    temperature = 105,
-   humidity = 0.999,
-   fertility = 0.1,
+   humidity = 999,
+   fertility = 100,
 }
 world:new_terrain_type{
    name = "summer_snow",
@@ -424,8 +424,8 @@
    textures = { pics_dir .. "summer/snow_00.png" },
    dither_layer = 220,
    temperature = 50,
-   humidity = 0.999,
-   fertility = 0.001,
+   humidity = 999,
+   fertility = 1,
 }
 
 
@@ -440,9 +440,9 @@
    textures = path.list_files(pics_dir .. "summer/lava/lava_??.png"),
    dither_layer = 30,
    fps = 4,
-   temperature = 1273.0,
-   humidity = 0.001,
-   fertility = 0.001,
+   temperature = 1273,
+   humidity = 1,
+   fertility = 1,
 }
 
 
@@ -458,8 +458,8 @@
    dither_layer = 180,
    fps = 14,
    temperature = 100,
-   humidity = 0.999,
-   fertility = 0.001,
+   humidity = 999,
+   fertility = 1,
 }
 
 ------------------------
@@ -482,8 +482,8 @@
    textures = { pics_dir .. "wasteland/ashes_00.png" },
    dither_layer = 400,
    temperature = 120,
-   humidity = 0.15,
-   fertility = 0.9,
+   humidity = 150,
+   fertility = 900,
 }
 
 
@@ -502,8 +502,8 @@
    textures = { pics_dir .. "wasteland/ashes2_00.png" },
    dither_layer = 410,
    temperature = 118,
-   humidity = 0.13,
-   fertility = 0.999,
+   humidity = 130,
+   fertility = 999,
 }
 
 
@@ -522,8 +522,8 @@
    textures = { pics_dir .. "wasteland/hardground1_00.png" },
    dither_layer = 420,
    temperature = 100,
-   humidity = 0.25,
-   fertility = 0.8,
+   humidity = 250,
+   fertility = 800,
 }
 
 
@@ -542,8 +542,8 @@
    textures = { pics_dir .. "wasteland/hardground2_00.png" },
    dither_layer = 370,
    temperature = 95,
-   humidity = 0.15,
-   fertility = 0.85,
+   humidity = 150,
+   fertility = 850,
 }
 
 
@@ -562,8 +562,8 @@
    textures = { pics_dir .. "wasteland/hardground3_00.png" },
    dither_layer = 380,
    temperature = 105,
-   humidity = 0.2,
-   fertility = 0.9,
+   humidity = 200,
+   fertility = 900,
 }
 
 
@@ -582,8 +582,8 @@
    textures = { pics_dir .. "wasteland/hardground4_00.png" },
    dither_layer = 390,
    temperature = 90,
-   humidity = 0.2,
-   fertility = 0.8,
+   humidity = 200,
+   fertility = 800,
 }
 
 
@@ -598,8 +598,8 @@
    textures = { pics_dir .. "wasteland/hardlava_00.png" },
    dither_layer = 360,
    temperature = 120,
-   humidity = 0.1,
-   fertility = 0.2,
+   humidity = 100,
+   fertility = 200,
 }
 
 
@@ -618,8 +618,8 @@
    textures = { pics_dir .. "wasteland/forested_mountain1_00.png" },
    dither_layer = 81,
    temperature = 110,
-   humidity = 0.15,
-   fertility = 0.95,
+   humidity = 150,
+   fertility = 950,
 }
 
 world:new_terrain_type{
@@ -637,8 +637,8 @@
    textures = { pics_dir .. "wasteland/forested_mountain2_00.png" },
    dither_layer = 81,
    temperature = 95,
-   humidity = 0.2,
-   fertility = 0.4,
+   humidity = 200,
+   fertility = 400,
 }
 
 world:new_terrain_type{
@@ -652,8 +652,8 @@
    textures = { pics_dir .. "wasteland/mountain1_00.png" },
    dither_layer = 90,
    temperature = 80,
-   humidity = 0.05,
-   fertility = 0.2,
+   humidity = 50,
+   fertility = 200,
 }
 
 
@@ -668,8 +668,8 @@
    textures = { pics_dir .. "wasteland/mountain2_00.png" },
    dither_layer = 90,
    temperature = 80,
-   humidity = 0.05,
-   fertility = 0.2,
+   humidity = 50,
+   fertility = 200,
 }
 
 
@@ -684,8 +684,8 @@
    textures = { pics_dir .. "wasteland/mountain3_00.png" },
    dither_layer = 90,
    temperature = 80,
-   humidity = 0.05,
-   fertility = 0.2,
+   humidity = 50,
+   fertility = 200,
 }
 
 
@@ -700,8 +700,8 @@
    textures = { pics_dir .. "wasteland/mountain4_00.png" },
    dither_layer = 80,
    temperature = 80,
-   humidity = 0.05,
-   fertility = 0.2,
+   humidity = 50,
+   fertility = 200,
 }
 
 
@@ -716,8 +716,8 @@
    textures = { pics_dir .. "wasteland/beach_00.png" },
    dither_layer = 50,
    temperature = 60,
-   humidity = 0.4,
-   fertility = 0.2,
+   humidity = 400,
+   fertility = 200,
 }
 
 
@@ -732,9 +732,9 @@
    textures = path.list_files(pics_dir .. "wasteland/lava_stone1/lava-stone1_??.png"),
    dither_layer = 20,
    fps = 7,
-   temperature = 1273.0,
-   humidity = 0.001,
-   fertility = 0.001,
+   temperature = 1273,
+   humidity = 1,
+   fertility = 1,
 }
 
 
@@ -749,9 +749,9 @@
    textures = path.list_files(pics_dir .. "wasteland/lava_stone2/lava-stone2_??.png"),
    dither_layer = 10,
    fps = 7,
-   temperature = 1273.0,
-   humidity = 0.001,
-   fertility = 0.001,
+   temperature = 1273,
+   humidity = 1,
+   fertility = 1,
 }
 
 
@@ -767,8 +767,8 @@
    dither_layer = 170,
    fps = 14,
    temperature = 100,
-   humidity = 0.999,
-   fertility = 0.001,
+   humidity = 999,
+   fertility = 1,
 }
 
 
@@ -792,8 +792,8 @@
    textures = { pics_dir .. "winter/tundra_00.png" },
    dither_layer = 230,
    temperature = 50,
-   humidity = 0.85,
-   fertility = 0.45,
+   humidity = 850,
+   fertility = 450,
 }
 
 
@@ -812,8 +812,8 @@
    textures = { pics_dir .. "winter/tundra2_00.png" },
    dither_layer = 240,
    temperature = 55,
-   humidity = 0.75,
-   fertility = 0.45,
+   humidity = 750,
+   fertility = 450,
 }
 
 
@@ -832,8 +832,8 @@
    textures = { pics_dir .. "winter/tundra3_00.png" },
    dither_layer = 240,
    temperature = 50,
-   humidity = 0.8,
-   fertility = 0.4,
+   humidity = 800,
+   fertility = 400,
 }
 
 
@@ -848,8 +848,8 @@
    textures = { pics_dir .. "winter/tundra_taiga_00.png" },
    dither_layer = 230,
    temperature = 40,
-   humidity = 0.75,
-   fertility = 0.4,
+   humidity = 750,
+   fertility = 400,
 }
 
 
@@ -864,8 +864,8 @@
    textures = { pics_dir .. "winter/taiga_00.png" },
    dither_layer = 250,
    temperature = 35,
-   humidity = 0.75,
-   fertility = 0.3,
+   humidity = 750,
+   fertility = 300,
 }
 
 
@@ -880,8 +880,8 @@
    textures = { pics_dir .. "winter/snow_00.png" },
    dither_layer = 250,
    temperature = 25,
-   humidity = 0.8,
-   fertility = 0.1,
+   humidity = 800,
+   fertility = 100,
 }
 
 
@@ -900,8 +900,8 @@
    textures = { pics_dir .. "winter/forested_mountain1_00.png" },
    dither_layer = 101,
    temperature = 35,
-   humidity = 0.7,
-   fertility = 0.4,
+   humidity = 700,
+   fertility = 400,
 }
 
 world:new_terrain_type{
@@ -919,8 +919,8 @@
    textures = { pics_dir .. "winter/forested_mountain2_00.png" },
    dither_layer = 101,
    temperature = 35,
-   humidity = 0.7,
-   fertility = 0.4,
+   humidity = 700,
+   fertility = 400,
 }
 
 world:new_terrain_type{
@@ -934,8 +934,8 @@
    textures = { pics_dir .. "winter/mountain1_00.png" },
    dither_layer = 110,
    temperature = 20,
-   humidity = 0.3,
-   fertility = 0.05,
+   humidity = 300,
+   fertility = 50,
 }
 
 
@@ -950,8 +950,8 @@
    textures = { pics_dir .. "winter/mountain2_00.png" },
    dither_layer = 110,
    temperature = 20,
-   humidity = 0.3,
-   fertility = 0.05,
+   humidity = 300,
+   fertility = 50,
 }
 
 
@@ -966,8 +966,8 @@
    textures = { pics_dir .. "winter/mountain3_00.png" },
    dither_layer = 100,
    temperature = 20,
-   humidity = 0.3,
-   fertility = 0.05,
+   humidity = 300,
+   fertility = 50,
 }
 
 
@@ -982,8 +982,8 @@
    textures = { pics_dir .. "winter/mountain4_00.png" },
    dither_layer = 100,
    temperature = 20,
-   humidity = 0.3,
-   fertility = 0.05,
+   humidity = 300,
+   fertility = 50,
 }
 world:new_terrain_type{
    name = "ice",
@@ -996,8 +996,8 @@
    textures = { pics_dir .. "winter/ice_00.png" },
    dither_layer = 260,
    temperature = 25,
-   humidity = 0.5,
-   fertility = 0.1,
+   humidity = 500,
+   fertility = 100,
 }
 
 
@@ -1012,8 +1012,8 @@
    textures = { pics_dir .. "winter/beach_00.png" },
    dither_layer = 40,
    temperature = 60,
-   humidity = 0.5,
-   fertility = 0.1,
+   humidity = 500,
+   fertility = 100,
 }
 
 
@@ -1029,8 +1029,8 @@
    dither_layer = 210,
    fps = 5,
    temperature = 50,
-   humidity = 0.999,
-   fertility = 0.001,
+   humidity = 999,
+   fertility = 1,
 }
 
 
@@ -1046,8 +1046,8 @@
    dither_layer = 210,
    fps = 5,
    temperature = 50,
-   humidity = 0.999,
-   fertility = 0.001,
+   humidity = 999,
+   fertility = 1,
 }
 
 
@@ -1063,8 +1063,8 @@
    dither_layer = 190,
    fps = 8,
    temperature = 50,
-   humidity = 0.999,
-   fertility = 0.001,
+   humidity = 999,
+   fertility = 1,
 }
 
 
@@ -1083,8 +1083,8 @@
    textures = { pics_dir .. "desert/desert4_00.png" },
    dither_layer = 270,
    temperature = 168,
-   humidity = 0.001,
-   fertility = 0.1,
+   humidity = 1,
+   fertility = 100,
 }
 
 world:new_terrain_type{
@@ -1098,8 +1098,8 @@
    textures = { pics_dir .. "desert/drysoil_00.png" },
    dither_layer = 300,
    temperature = 172,
-   humidity = 0.2,
-   fertility = 0.2,
+   humidity = 200,
+   fertility = 200,
 }
 world:new_terrain_type{
    name = "desert_steppe",
@@ -1116,8 +1116,8 @@
    textures = { pics_dir .. "desert/steppe_00.png" },
    dither_layer = 360,
    temperature = 155,
-   humidity = 0.5,
-   fertility = 0.5,
+   humidity = 500,
+   fertility = 500,
 }
 
 
@@ -1136,8 +1136,8 @@
    textures = { pics_dir .. "desert/meadow_00.png" },
    dither_layer = 310,
    temperature = 160,
-   humidity = 0.6,
-   fertility = 0.6,
+   humidity = 600,
+   fertility = 600,
 }
 
 
@@ -1156,8 +1156,8 @@
    textures = { pics_dir .. "desert/mountainmeadow_00.png" },
    dither_layer = 150,
    temperature = 145,
-   humidity = 0.5,
-   fertility = 0.5,
+   humidity = 500,
+   fertility = 500,
 }
 
 
@@ -1176,8 +1176,8 @@
    textures = { pics_dir .. "desert/highmountainmeadow_00.png" },
    dither_layer = 150,
    temperature = 140,
-   humidity = 0.4,
-   fertility = 0.4,
+   humidity = 400,
+   fertility = 400,
 }
 
 
@@ -1196,8 +1196,8 @@
    textures = { pics_dir .. "desert/forested_mountain1_00.png" },
    dither_layer = 71,
    temperature = 141,
-   humidity = 0.5,
-   fertility = 0.5,
+   humidity = 500,
+   fertility = 500,
 }
 
 world:new_terrain_type{
@@ -1215,8 +1215,8 @@
    textures = { pics_dir .. "desert/forested_mountain2_00.png" },
    dither_layer = 141,
    temperature = 120,
-   humidity = 0.5,
-   fertility = 0.5,
+   humidity = 500,
+   fertility = 500,
 }
 
 
@@ -1231,8 +1231,8 @@
    textures = { pics_dir .. "desert/mountain1_00.png" },
    dither_layer = 120,
    temperature = 130,
-   humidity = 0.05,
-   fertility = 0.05,
+   humidity = 50,
+   fertility = 50,
 }
 
 
@@ -1247,8 +1247,8 @@
    textures = { pics_dir .. "desert/mountain2_00.png" },
    dither_layer = 120,
    temperature = 130,
-   humidity = 0.05,
-   fertility = 0.05,
+   humidity = 50,
+   fertility = 50,
 }
 
 
@@ -1263,8 +1263,8 @@
    textures = { pics_dir .. "desert/mountain3_00.png" },
    dither_layer = 130,
    temperature = 130,
-   humidity = 0.05,
-   fertility = 0.05,
+   humidity = 50,
+   fertility = 50,
 }
 
 
@@ -1279,8 +1279,8 @@
    textures = { pics_dir .. "desert/mountain4_00.png" },
    dither_layer = 140,
    temperature = 130,
-   humidity = 0.05,
-   fertility = 0.05,
+   humidity = 50,
+   fertility = 50,
 }
 world:new_terrain_type{
    name = "desert1",
@@ -1293,8 +1293,8 @@
    textures = { pics_dir .. "desert/desert1_00.png" },
    dither_layer = 290,
    temperature = 167,
-   humidity = 0.001,
-   fertility = 0.001,
+   humidity = 1,
+   fertility = 1,
 }
 
 
@@ -1309,8 +1309,8 @@
    textures = { pics_dir .. "desert/desert2_00.png" },
    dither_layer = 280,
    temperature = 168,
-   humidity = 0.001,
-   fertility = 0.001,
+   humidity = 1,
+   fertility = 1,
 }
 
 
@@ -1325,8 +1325,8 @@
    textures = { pics_dir .. "desert/desert3_00.png" },
    dither_layer = 280,
    temperature = 178,
-   humidity = 0.001,
-   fertility = 0.001,
+   humidity = 1,
+   fertility = 1,
 }
 
 
@@ -1341,8 +1341,8 @@
    textures = { pics_dir .. "desert/beach_00.png" },
    dither_layer = 60,
    temperature = 179,
-   humidity = 0.5,
-   fertility = 0.1,
+   humidity = 500,
+   fertility = 100,
 }
 
 
@@ -1358,6 +1358,6 @@
    dither_layer = 200,
    fps = 5,
    temperature = 150,
-   humidity = 0.999,
-   fertility = 0.001,
+   humidity = 999,
+   fertility = 1,
 }

=== modified file 'src/logic/game.cc'
--- src/logic/game.cc	2018-10-21 09:42:03 +0000
+++ src/logic/game.cc	2018-11-05 08:10:25 +0000
@@ -1071,8 +1071,4 @@
 		fw.unsigned_32(general_stats_[p - 1].custom_statistic[j]);
 	}
 }
-
-double logic_rand_as_double(Game* game) {
-	return static_cast<double>(game->logic_rand()) / std::numeric_limits<uint32_t>::max();
-}
 }

=== modified file 'src/logic/game.h'
--- src/logic/game.h	2018-10-21 09:42:03 +0000
+++ src/logic/game.h	2018-11-05 08:10:25 +0000
@@ -347,9 +347,6 @@
 	location.y += logic_rand() % s - radius;
 	return location;
 }
-
-// Returns a value between [0., 1].
-double logic_rand_as_double(Game* game);
 }
 
 #endif  // end of include guard: WL_LOGIC_GAME_H

=== modified file 'src/logic/map_objects/immovable.cc'
--- src/logic/map_objects/immovable.cc	2018-09-25 06:32:35 +0000
+++ src/logic/map_objects/immovable.cc	2018-11-05 08:10:25 +0000
@@ -943,7 +943,7 @@
 	FCoords const f = map.get_fcoords(immovable.get_position());
 	const ImmovableDescr& descr = immovable.descr();
 
-	if (logic_rand_as_double(&game) <
+	if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
 	    probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) {
 		MapObjectDescr::OwnerType owner_type = descr.owner_type();
 		Player* owner = immovable.get_owner();
@@ -1030,7 +1030,7 @@
 	FCoords const f = map.get_fcoords(immovable.get_position());
 	const ImmovableDescr& descr = immovable.descr();
 
-	if (logic_rand_as_double(&game) <
+	if ((game.logic_rand() % TerrainAffinity::kPrecisionFactor) <
 	    probability_to_grow(descr.terrain_affinity(), f, map, game.world().terrains())) {
 		// Seed a new tree.
 		MapFringeRegion<> mr(map, Area<>(f, 0));
@@ -1047,7 +1047,7 @@
 		const FCoords new_location = map.get_fcoords(mr.location());
 		if (!new_location.field->get_immovable() &&
 		    (new_location.field->nodecaps() & MOVECAPS_WALK) &&
-		    logic_rand_as_double(&game) < probability_to_grow(descr.terrain_affinity(), new_location,
+		    (game.logic_rand() % TerrainAffinity::kPrecisionFactor) < probability_to_grow(descr.terrain_affinity(), new_location,
 		                                                      map, game.world().terrains())) {
 			game.create_immovable_with_name(mr.location(), type_name, descr.owner_type(),
 			                                nullptr /* owner */, nullptr /* former_building_descr */);

=== modified file 'src/logic/map_objects/terrain_affinity.cc'
--- src/logic/map_objects/terrain_affinity.cc	2018-04-07 16:59:00 +0000
+++ src/logic/map_objects/terrain_affinity.cc	2018-11-05 08:10:25 +0000
@@ -33,85 +33,92 @@
 
 namespace {
 
+// Literature on cross-platform floating point precision-problems:
+// https://arxiv.org/abs/cs/0701192
+// Monniaux, David (2008): "The pitfalls of verifying floating-point computations",
+// in: ACM Transactions on Programming Languages and Systems 30, 3 (2008) 12.
+//
+// Recommends using heximal float constants, but we'd need to switch to C++17 for that.
+//
+// https://randomascii.wordpress.com/2012/03/21/intermediate-floating-point-precision/
+
 constexpr double pow2(const double& a) {
 	return a * a;
 }
 
 // Helper function for probability_to_grow
 // Calculates the probability to grow for the given affinity and terrain values
-double calculate_probability_to_grow(const TerrainAffinity& affinity,
-                                     double terrain_humidity,
-                                     double terrain_fertility,
-                                     double terrain_temperature) {
-
-	constexpr double kHumidityWeight = 0.500086642549548;
-	constexpr double kFertilityWeight = 0.5292268046607387;
-	constexpr double kTemperatureWeight = 61.31300863608306;
-
-	const double sigma_humidity = (1. - affinity.pickiness());
-	const double sigma_temperature = (1. - affinity.pickiness());
-	const double sigma_fertility = (1. - affinity.pickiness());
-
-	return exp((-pow2((affinity.preferred_fertility() - terrain_fertility) /
-	                  (kFertilityWeight * sigma_fertility)) -
-	            pow2((affinity.preferred_humidity() - terrain_humidity) /
-	                 (kHumidityWeight * sigma_humidity)) -
-	            pow2((affinity.preferred_temperature() - terrain_temperature) /
-	                 (kTemperatureWeight * sigma_temperature))) /
-	           2);
+inline unsigned int calculate_probability_to_grow(const TerrainAffinity& affinity,
+                                     int terrain_humidity,
+                                     int terrain_fertility,
+                                     int terrain_temperature) {
+	constexpr double kHumidityWeight = 5.00086642549548;
+	constexpr double kFertilityWeight = 5.292268046607387;
+	constexpr double kTemperatureWeight = 0.6131300863608306;
+
+    // Avoid division by 0
+    assert(affinity.pickiness() < 100);
+	const double sigma = std::floor(100.0 - affinity.pickiness());
+
+	const double result = exp(-(pow2(((affinity.preferred_fertility() - terrain_fertility) /
+                                       kFertilityWeight) / sigma) + (pow2(((affinity.preferred_humidity() - terrain_humidity) /
+                                                                kHumidityWeight) / sigma) + pow2(((affinity.preferred_temperature() - terrain_temperature) /
+                                                                                       kTemperatureWeight) / sigma))) / 2.0);
+
+    return static_cast<unsigned int>(std::max(0.0, std::floor(result * static_cast<double>(TerrainAffinity::kPrecisionFactor))));
 }
 
 }  // namespace
 
 TerrainAffinity::TerrainAffinity(const LuaTable& table, const std::string& immovable_name)
-   : preferred_fertility_(table.get_double("preferred_fertility")),
-     preferred_humidity_(table.get_double("preferred_humidity")),
-     preferred_temperature_(table.get_double("preferred_temperature")),
-     pickiness_(table.get_double("pickiness")) {
-	if (!(0 <= preferred_fertility_ && preferred_fertility_ <= 1.)) {
-		throw GameDataError("%s: preferred_fertility is not in [0, 1].", immovable_name.c_str());
-	}
-	if (!(0 <= preferred_humidity_ && preferred_humidity_ <= 1.)) {
-		throw GameDataError("%s: preferred_humidity is not in [0, 1].", immovable_name.c_str());
-	}
-	if (!(0 <= pickiness_ && pickiness_ <= 1.)) {
-		throw GameDataError("%s: pickiness is not in [0, 1].", immovable_name.c_str());
+   : preferred_fertility_(table.get_int("preferred_fertility")),
+     preferred_humidity_(table.get_int("preferred_humidity")),
+     preferred_temperature_(table.get_int("preferred_temperature")),
+     pickiness_(table.get_int("pickiness")) {
+	if (!(0 <= preferred_fertility_ && preferred_fertility_ <= 1000)) {
+		throw GameDataError("%s: preferred_fertility is not in [0, 1000].", immovable_name.c_str());
+	}
+	if (!(0 <= preferred_humidity_ && preferred_humidity_ <= 1000)) {
+		throw GameDataError("%s: preferred_humidity is not in [0, 1000].", immovable_name.c_str());
+	}
+	if (!(0 <= pickiness_ && pickiness_ < 100)) {
+		throw GameDataError("%s: pickiness is not in [0, 99].", immovable_name.c_str());
 	}
 	if (preferred_temperature_ < 0) {
 		throw GameDataError("%s: preferred_temperature is not possible.", immovable_name.c_str());
 	}
 }
 
-double TerrainAffinity::preferred_temperature() const {
+int TerrainAffinity::preferred_temperature() const {
 	return preferred_temperature_;
 }
 
-double TerrainAffinity::preferred_fertility() const {
+int TerrainAffinity::preferred_fertility() const {
 	return preferred_fertility_;
 }
 
-double TerrainAffinity::preferred_humidity() const {
+int TerrainAffinity::preferred_humidity() const {
 	return preferred_humidity_;
 }
 
-double TerrainAffinity::pickiness() const {
+int TerrainAffinity::pickiness() const {
 	return pickiness_;
 }
 
-double probability_to_grow(const TerrainAffinity& affinity,
+unsigned int probability_to_grow(const TerrainAffinity& affinity,
                            const FCoords& fcoords,
                            const Map& map,
                            const DescriptionMaintainer<TerrainDescription>& terrains) {
-	double terrain_humidity = 0;
-	double terrain_fertility = 0;
-	double terrain_temperature = 0;
+	int terrain_humidity = 0;
+	int terrain_fertility = 0;
+	int terrain_temperature = 0;
 
 	const auto average = [&terrain_humidity, &terrain_fertility, &terrain_temperature,
 	                      &terrains](const int terrain_index) {
 		const TerrainDescription& t = terrains.get(terrain_index);
-		terrain_humidity += t.humidity() / 6.;
-		terrain_temperature += t.temperature() / 6.;
-		terrain_fertility += t.fertility() / 6.;
+		terrain_humidity += t.humidity();
+		terrain_temperature += t.temperature();
+		terrain_fertility += t.fertility();
 	};
 
 	average(fcoords.field->terrain_d());
@@ -136,10 +143,10 @@
 	}
 
 	return calculate_probability_to_grow(
-	   affinity, terrain_humidity, terrain_fertility, terrain_temperature);
+	   affinity, terrain_humidity / 6, terrain_fertility / 6, terrain_temperature / 6);
 }
 
-double probability_to_grow(const TerrainAffinity& affinity, const TerrainDescription& terrain) {
+unsigned int probability_to_grow(const TerrainAffinity& affinity, const TerrainDescription& terrain) {
 
 	return calculate_probability_to_grow(
 	   affinity, terrain.humidity(), terrain.fertility(), terrain.temperature());

=== modified file 'src/logic/map_objects/terrain_affinity.h'
--- src/logic/map_objects/terrain_affinity.h	2018-04-07 16:59:00 +0000
+++ src/logic/map_objects/terrain_affinity.h	2018-11-05 08:10:25 +0000
@@ -31,7 +31,6 @@
 
 class Map;
 class TerrainDescription;
-class World;
 struct FCoords;
 
 // Describes the parameters and the pickiness of Immovables towards terrain
@@ -39,39 +38,41 @@
 // define this.
 class TerrainAffinity {
 public:
+    static constexpr int kPrecisionFactor = 100000000;
+
 	explicit TerrainAffinity(const LuaTable& table, const std::string& immovable_name);
 
 	// Preferred temperature is in arbitrary units.
-	double preferred_temperature() const;
-
-	// Preferred fertility in percent [0, 1].
-	double preferred_fertility() const;
-
-	// Preferred humidity in percent [0, 1].
-	double preferred_humidity() const;
-
-	// A value in [0, 1] that defines how well this can deal with non-ideal
+	int preferred_temperature() const;
+
+	// Preferred fertility, ranging from 0 to 1000.
+	int preferred_fertility() const;
+
+	// Preferred humidity, ranging from 0 to 1000.
+	int preferred_humidity() const;
+
+	// A value in [0, 100] that defines how well this can deal with non-ideal
 	// situations. Lower means it is less picky, i.e. it can deal better.
-	double pickiness() const;
+	int pickiness() const;
 
 private:
-	double preferred_fertility_;
-	double preferred_humidity_;
-	double preferred_temperature_;
-	double pickiness_;
+	const int preferred_fertility_;
+	const int preferred_humidity_;
+	const int preferred_temperature_;
+	const int pickiness_;
 
 	DISALLOW_COPY_AND_ASSIGN(TerrainAffinity);
 };
 
 // Returns a value in [0., 1.] that describes the suitability for the
 // 'immovable_affinity' for 'field'. Higher is better suited.
-double probability_to_grow(const TerrainAffinity& immovable_affinity,
+unsigned int probability_to_grow(const TerrainAffinity& immovable_affinity,
                            const FCoords& fcoords,
                            const Map& map,
                            const DescriptionMaintainer<TerrainDescription>& terrains);
 
 // Probability to grow for a single terrain
-double probability_to_grow(const TerrainAffinity& immovable_affinity,
+unsigned int probability_to_grow(const TerrainAffinity& immovable_affinity,
                            const TerrainDescription& terrain);
 
 }  // namespace Widelands

=== modified file 'src/logic/map_objects/tribes/worker.cc'
--- src/logic/map_objects/tribes/worker.cc	2018-09-25 06:32:35 +0000
+++ src/logic/map_objects/tribes/worker.cc	2018-11-05 08:10:25 +0000
@@ -452,11 +452,11 @@
 	const uint32_t attribute_id = ImmovableDescr::get_attribute_id("tree_sapling");
 
 	const DescriptionMaintainer<TerrainDescription>& terrains = game.world().terrains();
-	double best = 0.0;
+	double best = 0;
 	for (DescriptionIndex i = 0; i < immovables.size(); ++i) {
 		const ImmovableDescr& immovable_descr = immovables.get(i);
 		if (immovable_descr.has_attribute(attribute_id) && immovable_descr.has_terrain_affinity()) {
-			double probability =
+			int probability =
 			   probability_to_grow(immovable_descr.terrain_affinity(), fpos, map, terrains);
 			if (probability > best) {
 				best = probability;
@@ -464,7 +464,7 @@
 		}
 	}
 	// normalize value to int16 range
-	const int16_t correct_val = (std::numeric_limits<int16_t>::max() - 1) * best;
+	const int16_t correct_val = (std::numeric_limits<int16_t>::max() - 1) * (best / TerrainAffinity::kPrecisionFactor);
 
 	if (x_check && (correct_val != cache_entry)) {
 		forester_cache.clear();
@@ -798,7 +798,7 @@
 	// affinity). We will pick one of them at random later. The container is
 	// picked to be a stable sorting one, so that no deyncs happen in
 	// multiplayer.
-	std::set<std::tuple<double, DescriptionIndex, MapObjectDescr::OwnerType>>
+	std::set<std::tuple<int, DescriptionIndex, MapObjectDescr::OwnerType>>
 	   best_suited_immovables_index;
 
 	// Checks if the 'immovable_description' has a terrain_affinity, if so use it. Otherwise assume
@@ -809,7 +809,7 @@
 		if (!immovable_description.has_attribute(attribute_id)) {
 			return;
 		}
-		double p = 1.;
+		int p = TerrainAffinity::kPrecisionFactor;
 		if (immovable_description.has_terrain_affinity()) {
 			p = probability_to_grow(
 			   immovable_description.terrain_affinity(), fpos, map, game.world().terrains());
@@ -856,18 +856,18 @@
 	// Randomly pick one of the immovables to be planted.
 
 	// Each candidate is weighted by its probability to grow.
-	double total_weight = 0.0;
+	int total_weight = 0;
 	for (const auto& bsii : best_suited_immovables_index) {
-		double weight = std::get<0>(bsii);
-		total_weight += weight * weight;
+		const int weight = std::get<0>(bsii);
+		total_weight += static_cast<int>(std::floor(std::sqrt(weight)));
 	}
 
-	double choice = logic_rand_as_double(&game) * total_weight;
+	int choice = game.logic_rand() % total_weight;
 	for (const auto& bsii : best_suited_immovables_index) {
-		double weight = std::get<0>(bsii);
+		const int weight = std::get<0>(bsii);
 		state.ivar2 = std::get<1>(bsii);
 		state.ivar3 = static_cast<int>(std::get<2>(bsii));
-		choice -= weight * weight;
+		choice -= static_cast<int>(std::floor(std::sqrt(weight)));
 		if (0 > choice) {
 			break;
 		}

=== modified file 'src/logic/map_objects/world/terrain_description.cc'
--- src/logic/map_objects/world/terrain_description.cc	2018-04-07 16:59:00 +0000
+++ src/logic/map_objects/world/terrain_description.cc	2018-11-05 08:10:25 +0000
@@ -105,19 +105,19 @@
      default_resource_index_(world.get_resource(table.get_string("default_resource").c_str())),
      default_resource_amount_(table.get_int("default_resource_amount")),
      dither_layer_(table.get_int("dither_layer")),
-     temperature_(table.get_double("temperature")),
-     fertility_(table.get_double("fertility")),
-     humidity_(table.get_double("humidity")) {
+     temperature_(table.get_int("temperature")),
+     fertility_(table.get_int("fertility")),
+     humidity_(table.get_int("humidity")) {
 
 	if (table.has_key("tooltips")) {
 		custom_tooltips_ = table.get_table("tooltips")->array_entries<std::string>();
 	}
 
-	if (!(0 < fertility_ && fertility_ < 1.)) {
-		throw GameDataError("%s: fertility is not in (0, 1).", name_.c_str());
+	if (!(0 < fertility_ && fertility_ < 1000)) {
+		throw GameDataError("%s: fertility is not in (0, 1000).", name_.c_str());
 	}
-	if (!(0 < humidity_ && humidity_ < 1.)) {
-		throw GameDataError("%s: humidity is not in (0, 1).", name_.c_str());
+	if (!(0 < humidity_ && humidity_ < 1000)) {
+		throw GameDataError("%s: humidity is not in (0, 1000).", name_.c_str());
 	}
 	if (temperature_ < 0) {
 		throw GameDataError("%s: temperature is not possible.", name_.c_str());
@@ -244,15 +244,15 @@
 	return dither_layer_;
 }
 
-double TerrainDescription::temperature() const {
+int TerrainDescription::temperature() const {
 	return temperature_;
 }
 
-double TerrainDescription::humidity() const {
+int TerrainDescription::humidity() const {
 	return humidity_;
 }
 
-double TerrainDescription::fertility() const {
+int TerrainDescription::fertility() const {
 	return fertility_;
 }
 

=== modified file 'src/logic/map_objects/world/terrain_description.h'
--- src/logic/map_objects/world/terrain_description.h	2018-04-27 06:11:05 +0000
+++ src/logic/map_objects/world/terrain_description.h	2018-11-05 08:10:25 +0000
@@ -115,13 +115,13 @@
 
 	/// Parameters for terrain affinity of immovables.
 	/// Temperature is in arbitrary units.
-	double temperature() const;
-
-	/// Humidity in percent [0, 1].
-	double humidity() const;
-
-	/// Fertility in percent [0, 1].
-	double fertility() const;
+	int temperature() const;
+
+	/// Humidity, ranging from 0 to 1000.
+	int humidity() const;
+
+	/// Fertility, ranging from 0 to 1000.
+	int fertility() const;
 
 	/// Additional tooptip entries for the editor
 	const std::vector<std::string>& custom_tooltips() const {
@@ -139,9 +139,9 @@
 	int default_resource_amount_;
 	int dither_layer_;
 	int frame_length_;
-	double temperature_;
-	double fertility_;
-	double humidity_;
+	int temperature_;
+	int fertility_;
+	int humidity_;
 	std::vector<std::string> texture_paths_;
 	std::vector<const Image*> textures_;
 	RGBColor minimap_colors_[256];

=== modified file 'src/scripting/lua_map.cc'
--- src/scripting/lua_map.cc	2018-10-27 09:45:14 +0000
+++ src/scripting/lua_map.cc	2018-11-05 08:10:25 +0000
@@ -1924,8 +1924,8 @@
 
          returns the terrain affinity values for this immovable
 
-         (RO) a table containing numbers labeled as pickiness (double), preferred_fertility (double),
-         preferred_humidity (double), and preferred_temperature (uint),
+         (RO) a table containing numbers labeled as pickiness (uint), preferred_fertility (uint),
+         preferred_humidity (uint), and preferred_temperature (uint),
          or nil if the immovable has no terrain affinity.
 */
 int LuaImmovableDescription::get_terrain_affinity(lua_State* L) {
@@ -1933,13 +1933,13 @@
 		const TerrainAffinity& affinity = get()->terrain_affinity();
 		lua_newtable(L);
 		lua_pushstring(L, "pickiness");
-		lua_pushdouble(L, affinity.pickiness());
+		lua_pushuint32(L, affinity.pickiness());
 		lua_settable(L, -3);
 		lua_pushstring(L, "preferred_fertility");
-		lua_pushdouble(L, affinity.preferred_fertility());
+		lua_pushuint32(L, affinity.preferred_fertility());
 		lua_settable(L, -3);
 		lua_pushstring(L, "preferred_humidity");
-		lua_pushdouble(L, affinity.preferred_humidity());
+		lua_pushuint32(L, affinity.preferred_humidity());
 		lua_settable(L, -3);
 		lua_pushstring(L, "preferred_temperature");
 		lua_pushuint32(L, affinity.preferred_temperature());
@@ -2031,7 +2031,7 @@
 	if (get()->has_terrain_affinity()) {
 		const TerrainDescription* terrain =
 		   (*get_user_class<LuaMaps::LuaTerrainDescription>(L, 2))->get();
-		lua_pushdouble(L, Widelands::probability_to_grow(get()->terrain_affinity(), *terrain));
+		lua_pushdouble(L, Widelands::probability_to_grow(get()->terrain_affinity(), *terrain) / static_cast<double>(Widelands::TerrainAffinity::kPrecisionFactor));
 	} else {
 		lua_pushnil(L);
 	}
@@ -3510,22 +3510,22 @@
 /* RST
    .. attribute:: fertility
 
-         (RO) the :class:`double` fertility value for this terrain
+         (RO) the :class:`uint` fertility value for this terrain
 */
 
 int LuaTerrainDescription::get_fertility(lua_State* L) {
-	lua_pushdouble(L, get()->fertility());
+	lua_pushuint32(L, get()->fertility());
 	return 1;
 }
 
 /* RST
    .. attribute:: humidity
 
-         (RO) the :class:`double` humidity value for this terrain
+         (RO) the :class:`uint` humidity value for this terrain
 */
 
 int LuaTerrainDescription::get_humidity(lua_State* L) {
-	lua_pushdouble(L, get()->humidity());
+	lua_pushuint32(L, get()->humidity());
 	return 1;
 }
 


Follow ups