Skip to content

Commit fc29a28

Browse files
committed
Fix float->int LSL cast behavior, update Tailslide for similar fix.
1 parent 7555e10 commit fc29a28

File tree

5 files changed

+46
-37
lines changed

5 files changed

+46
-37
lines changed

VM/include/llsl.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
#pragma once
22

3+
#include <cstdint>
4+
35
// Must match lscript's definition!
46
enum class LSLIType : uint8_t {
57
LST_NULL = 0,
@@ -96,3 +98,16 @@ int lsl_cast_list_elem_poszero(lua_State *L);
9698
void conj_quat(float *quat);
9799
void rot_vec(const float *vec, const float *rot, float *res);
98100
void copy_quat(float *dest, const float *src);
101+
102+
// Convert double to int32, returning INT32_MIN for NaN, Inf, or out-of-range values.
103+
// Matches Mono's unspecified overflow behavior.
104+
inline int32_t lsl_float_to_int(double nval)
105+
{
106+
constexpr double kMaxInt32 = 2147483648.0;
107+
if (nval >= -kMaxInt32 && nval < kMaxInt32)
108+
{
109+
return (int32_t)nval;
110+
}
111+
// NaN, Inf, or out-of-range
112+
return INT32_MIN;
113+
}

VM/src/llsl.cpp

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -254,22 +254,7 @@ static int _lsl_cast_internal(lua_State* L, bool in_list, bool neg_zero, bool ni
254254
{
255255
case LSLIType::LST_INTEGER:
256256
{
257-
// If this seems weird, that's because it is. Mono truncates to 32-bit float
258-
// before converting to integer. You'll note that we cast to int64_t first before
259-
// truncating to int32_t, which also seems odd. We need that to emulate x86-64
260-
// integer wraparound on AArch64, or `print(((integer)((float)0x7FffFFfe)))`
261-
// will print 2147483647 rather than -2147483648. See
262-
// https://stackoverflow.com/questions/66279679/casting-float-to-int-with-wrap-around-on-aarch64-arm64
263-
float nval = (float)nvalue(val);
264-
if (isfinite(nval))
265-
{
266-
setintvalue(&new_tv, (int32_t)((int64_t)(nval)));
267-
}
268-
else
269-
{
270-
// Mono treats non-finite values as INT32_MIN.
271-
setintvalue(&new_tv, INT32_MIN);
272-
}
257+
setintvalue(&new_tv, lsl_float_to_int(nvalue(val)));
273258
break;
274259
}
275260
case LSLIType::LST_STRING:

VM/src/lvmexecute.cpp

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "lbuiltins.h"
1414
#include "lnumutils.h"
1515
#include "lbytecode.h"
16+
#include "llsl.h"
1617

1718
#include <cmath>
1819
#include <string.h>
@@ -376,18 +377,9 @@ static void luau_execute(lua_State* L)
376377
}
377378
else
378379
{
379-
// float -> int (matches lsl_cast() and avoids AArch64 weirdness)
380+
// float -> int (matches lsl_cast())
380381
LUAU_ASSERT(ttisnumber(rb));
381-
float nval = (float)nvalue(rb);
382-
if (isfinite(nval))
383-
{
384-
setintvalue(ra, (int32_t)((int64_t)(nval)));
385-
}
386-
else
387-
{
388-
// Matches Mono behavior for non-finite values.
389-
setintvalue(ra, INT32_MIN);
390-
}
382+
setintvalue(ra, lsl_float_to_int(nvalue(rb)));
391383
}
392384
VM_NEXT();
393385
}

autobuild.xml

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -16,11 +16,11 @@
1616
<key>archive</key>
1717
<map>
1818
<key>hash</key>
19-
<string>6e43a13c10f6d42c7fe4c566e463e6cf0bb2c3bb</string>
19+
<string>e08633020943497f1245fcff2068dd7339a4b937</string>
2020
<key>hash_algorithm</key>
2121
<string>sha1</string>
2222
<key>url</key>
23-
<string>https://github.com/secondlife/tailslide/releases/download/v1.5.7/tailslide-1.5.7-darwin64-19723508656.tar.zst</string>
23+
<string>https://github.com/secondlife/tailslide/releases/download/v1.5.8/tailslide-1.5.8-darwin64-20186111034.tar.zst</string>
2424
</map>
2525
<key>name</key>
2626
<string>darwin64</string>
@@ -30,11 +30,11 @@
3030
<key>archive</key>
3131
<map>
3232
<key>hash</key>
33-
<string>54cb38e78944ff0419fe3bda926db0e436700d2c</string>
33+
<string>3a1b998bc65c65d96fee900f8698881d499f64eb</string>
3434
<key>hash_algorithm</key>
3535
<string>sha1</string>
3636
<key>url</key>
37-
<string>https://github.com/secondlife/tailslide/releases/download/v1.5.7/tailslide-1.5.7-linux-19723508656.tar.zst</string>
37+
<string>https://github.com/secondlife/tailslide/releases/download/v1.5.8/tailslide-1.5.8-linux-20186111034.tar.zst</string>
3838
</map>
3939
<key>name</key>
4040
<string>linux</string>
@@ -44,11 +44,11 @@
4444
<key>archive</key>
4545
<map>
4646
<key>hash</key>
47-
<string>b628f3c36ea15605e5ab5afb77b9f46812cd65e1</string>
47+
<string>002c70a435e934b7404da74bf4e529828c3f976f</string>
4848
<key>hash_algorithm</key>
4949
<string>sha1</string>
5050
<key>url</key>
51-
<string>https://github.com/secondlife/tailslide/releases/download/v1.5.7/tailslide-1.5.7-linux64-19723508656.tar.zst</string>
51+
<string>https://github.com/secondlife/tailslide/releases/download/v1.5.8/tailslide-1.5.8-linux64-20186111034.tar.zst</string>
5252
</map>
5353
<key>name</key>
5454
<string>linux64</string>
@@ -58,11 +58,11 @@
5858
<key>archive</key>
5959
<map>
6060
<key>hash</key>
61-
<string>2b054f784df3c442211c94c6717ad0dd557cf95e</string>
61+
<string>afb69ef491aaf2ad29b8def26b518021cffabb80</string>
6262
<key>hash_algorithm</key>
6363
<string>sha1</string>
6464
<key>url</key>
65-
<string>https://github.com/secondlife/tailslide/releases/download/v1.5.7/tailslide-1.5.7-windows64-19723508656.tar.zst</string>
65+
<string>https://github.com/secondlife/tailslide/releases/download/v1.5.8/tailslide-1.5.8-windows64-20186111034.tar.zst</string>
6666
</map>
6767
<key>name</key>
6868
<string>windows64</string>
@@ -75,7 +75,7 @@
7575
<key>copyright</key>
7676
<string>You may copy it</string>
7777
<key>version</key>
78-
<string>1.5.7</string>
78+
<string>1.5.8</string>
7979
<key>use_scm_version</key>
8080
<boolean>true</boolean>
8181
<key>name</key>

tests/conformance/float_precision.lsl

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
integer F32_MAX = 16777216;
55
// Value we expect if the value was truncated to float32 space.
66
integer F32_TRUNCATED = 16777215;
7+
integer INT32_MIN = 0x80000000;
78
float DELTA = 5;
89
float g_f;
910

@@ -102,7 +103,23 @@ default
102103
checkTruth("integer->float cast", (integer)((float)(F32_MAX + 5) - 5) == F32_MAX);
103104

104105
// This is _not_ meant to roundtrip correctly. int->float returns a double but float->int takes a float32.
105-
checkTruth("integer->float->integer cast", ((integer)((float)0x7FffFFfe)) == -2147483648);
106+
checkTruth("integer->float->integer cast", ((integer)((float)0x7FffFFfe)) == INT32_MIN);
107+
108+
// Out-of-range float values should return INT32_MIN (compile-time constant folding)
109+
checkTruth("float->int overflow positive", ((integer)1e20) == INT32_MIN);
110+
checkTruth("float->int overflow negative", ((integer)-1e20) == INT32_MIN);
111+
checkTruth("float->int at boundary", ((integer)2147483648.0) == INT32_MIN);
112+
checkTruth("float->int just under boundary", ((integer)2147483520.0) == 2147483520);
113+
114+
// Test runtime float->int conversion (not constant-folded)
115+
float huge = 1e20;
116+
checkTruth("float->int overflow positive", ((integer)huge) == INT32_MIN);
117+
huge = -1e20;
118+
checkTruth("float->int overflow negative", ((integer)huge) == INT32_MIN);
119+
huge = 2147483648.0;
120+
checkTruth("float->int at boundary", ((integer)huge) == INT32_MIN);
121+
huge = 2147483520.0;
122+
checkTruth("float->int just under boundary", ((integer)huge) == 2147483520);
106123

107124
// This is _also_ not meant to roundtrip correctly. Mono has very janky precision when using the
108125
// F<n> format specifier, we need to replicate it. We've ported some of the Mono NumberFormatter

0 commit comments

Comments
 (0)