From 159fae90bffbc3d464d17f3399c0b8102e7af4a1 Mon Sep 17 00:00:00 2001 From: Sei Lisa Date: Thu, 22 Dec 2016 01:05:21 +0100 Subject: [PATCH] Improve llFrand. - Remove it from lslextrafuncs, and move all the code to lslbasefuncs. - Make it behave like SL's more accurately. Denormals return 0 always in SL. - Use int() for truncation rather then floor/ceil. - Add test cases. --- lslopt/lslbasefuncs.py | 41 +++++++++++++++++++++++------------------ lslopt/lslextrafuncs.py | 12 ++---------- testfuncs.py | 15 +++++++++++++++ 3 files changed, 40 insertions(+), 28 deletions(-) diff --git a/lslopt/lslbasefuncs.py b/lslopt/lslbasefuncs.py index 00933ca..87cb3c7 100644 --- a/lslopt/lslbasefuncs.py +++ b/lslopt/lslbasefuncs.py @@ -20,8 +20,8 @@ # The functions it implements are all functions that always return the same # result when given the same input, and that have no side effects. # -# For example, llAbs() is here, but llFrand() is not, because it doesn't always -# return the same result. +# For example, llAbs() is here, but llGetPos() is not, because it doesn't +# always return the same result. # # This implies that functions present in this module can be precomputed if # their arguments are constants. @@ -1053,36 +1053,41 @@ def llFloor(f): return -2147483648 return int(math.floor(f)) -if lslcommon.IsCalc: - import time - import random - def llFrand(lim): - assert isfloat(lim) - if math.isinf(lim): - return 0. - if math.isnan(lim): - return lim +def llFrand(lim): + assert isfloat(lim) + if math.isinf(lim) or abs(lim) < 1.1754943508222875e-38: + return 0. + if math.isnan(lim): + return lim - lim = F32(lim) # apply constraints + if lslcommon.IsCalc: + import random val = random.random() * lim # Truncate, rather than rounding m, e = math.frexp(val) - m = m * 16777216.0 - m = math.floor(m) if m > 0 else math.ceil(m) # don't invoke __trunc__ - val = F32(math.ldexp(m * 0.000000059604644775390625, e)) + val = F32(math.ldexp(int(m * 16777216.) * .000000059604644775390625, e)) if val == lim: + # this should never happen + # (it can happen on denormals, but these cause output of 0.0) val = 0. return val - def llGenerateKey(): + # Can't give a concrete value + raise ELSLCantCompute + +def llGenerateKey(): + if lslcommon.IsCalc: + import time + import random + s = hashlib.md5((u'%.17g %f %f' % (time.time(), random.random(), random.random())).encode('utf8') ).hexdigest() return Key(s[:8] + '-' + s[8:12] + '-' + s[12:16] + '-' + s[16:20] + '-' + s[20:32]) -# Otherwise they're not implemented, as they don't give the same output for -# the same input. + # Can't give a concrete value + raise ELSLCantCompute def llGetListEntryType(lst, pos): assert islist(lst) diff --git a/lslopt/lslextrafuncs.py b/lslopt/lslextrafuncs.py index 5a97531..bac6fba 100644 --- a/lslopt/lslextrafuncs.py +++ b/lslopt/lslextrafuncs.py @@ -17,11 +17,10 @@ # Extra functions that have predictable return values for certain arguments. -import lslcommon from lslcommon import Key #, Vector, Quaternion from lslbasefuncs import ELSLCantCompute, isinteger, iskey, islist, \ - isfloat, isvector, isstring, NULL_KEY, ZERO_VECTOR, ZERO_ROTATION, cond -#isrotation + isvector, isstring, NULL_KEY, ZERO_VECTOR, ZERO_ROTATION, cond +#isfloat, isrotation TouchEvents = ('touch', 'touch_start', 'touch_end') DetectionEvents = ('touch', 'touch_start', 'touch_end', @@ -147,13 +146,6 @@ def llEdgeOfWorld(v1, v2): return 1 raise ELSLCantCompute -if not lslcommon.IsCalc: - def llFrand(f): - assert isfloat(f) - if f == 0: - return 0. - raise ELSLCantCompute - def llGetAgentInfo(id): assert iskey(id) if not cond(id): diff --git a/testfuncs.py b/testfuncs.py index 9d291f9..63d1c3c 100644 --- a/testfuncs.py +++ b/testfuncs.py @@ -890,6 +890,21 @@ def do_tests(): test('llList2CSV([llPow(nan,F32(1.3))])', u'nan') test('llList2CSV([Vector((-nan,nan,-inf))])', u'<-nan, nan, -inf>') + test('llFrand(0.0)', 0.0) + test('llFrand(-0.0)', 0.0) + test('llFrand(Infinity)', 0.0) + test('llFrand(-Infinity)', 0.0) + test('llFrand(-NaN)', -NaN) + test('llFrand(NaN)', NaN) + for i in range(10): + test('llFrand(F32(1.4e-45))', 0.0) + test('llFrand(F32(1.1754943508222875e-38))', 0.0) + + lslcommon.IsCalc = True + test('cond(llGenerateKey())', True) + lslcommon.IsCalc = False + shouldexcept('llGenerateKey()', ELSLCantCompute) + testXB64S("", "", "") testXB64S(u"Hello, World!", u"", u"Hello, World!") testXB64S("AAAAA==AAAAA=", "_X", "/X/X/==X/X/X=")