From 453809b65c9823e1957ebcc9e496ff4df7722000 Mon Sep 17 00:00:00 2001 From: jyelon Date: Wed, 6 Apr 2022 15:09:28 -0400 Subject: [PATCH] A small tweak to lua random --- luprex/core/cpp/util.cpp | 8 ++++++++ luprex/core/cpp/util.hpp | 3 +++ luprex/core/cpp/world-accessor.cpp | 11 +++++++---- 3 files changed, 18 insertions(+), 4 deletions(-) diff --git a/luprex/core/cpp/util.cpp b/luprex/core/cpp/util.cpp index fdff486a..4bb9eeb5 100644 --- a/luprex/core/cpp/util.cpp +++ b/luprex/core/cpp/util.cpp @@ -157,6 +157,10 @@ uint64_t hash_ints(uint64_t a, uint64_t b, uint64_t c, uint64_t d) { return h1; } +double hash_to_double(uint64_t hash) { + return (hash >> (64-53)) * 0x1p-53; +} + StringVec split(const eng::string &s, char sep) { StringVec result; int start = 0; @@ -498,6 +502,10 @@ LuaDefine(unittests_util, "", "some unit tests") { LuaAssertStrEq(L, util::hash_to_hex(util::HashValue(0x1234,0x789a)), "0000000000001234000000000000789a"); + // Test hash_to_double + LuaAssert(L, util::hash_to_double(0x1000000000000000) == 1.0/16.0); + LuaAssert(L, util::hash_to_double(0x7000000000000000) == 7.0/16.0); + LuaAssert(L, util::hash_to_double(0xF000000000000000) == 15.0/16.0); return 0; } diff --git a/luprex/core/cpp/util.hpp b/luprex/core/cpp/util.hpp index 46bf2961..1a17e559 100644 --- a/luprex/core/cpp/util.hpp +++ b/luprex/core/cpp/util.hpp @@ -70,6 +70,9 @@ eng::string hash_to_hex(const HashValue &hash); // This is a good hash, but not cryptographically good. uint64_t hash_ints(uint64_t n1, uint64_t n2, uint64_t n3, uint64_t n4); +// Convert a 64-bit hash value into a floating point number between 0 and 1. +double hash_to_double(uint64_t hash); + // Split a string into multiple strings StringVec split(const eng::string &s, char sep); diff --git a/luprex/core/cpp/world-accessor.cpp b/luprex/core/cpp/world-accessor.cpp index f1b4682b..7f58bda7 100644 --- a/luprex/core/cpp/world-accessor.cpp +++ b/luprex/core/cpp/world-accessor.cpp @@ -412,12 +412,15 @@ LuaDefine(math_random, "(args...)", } } - // Generate the hash and convert to a lua_Number. - uint64_t hash = util::hash_ints(seed, count, salt, 456); if (!have_range) { - double result = (hash & LuaStack::MAXINT) * 0x1p-53; - lua_pushnumber(L, result); + // Generate the hash and convert to a double. + uint64_t hash = util::hash_ints(seed, count, salt, 456); + lua_pushnumber(L, util::hash_to_double(hash)); } else { + // Generate the hash and scale it into the desired range. + // This code is not quite right: the results are not quite + // uniform, this is especially true for very long ranges. + uint64_t hash = util::hash_ints(seed, count, salt, 456); uint64_t range = (high - low) + 1; uint64_t offset = (hash & 0x7FFFFFFFFFFFFFFF) % range; int64_t result = low + int64_t(offset);