How does IntValue.Value actually cast out of range doubles to int64_t?

I experimented on how IntValue casts double to int64_t and the results were interesting and different to the double to int64_t casting on some Luau standard library functions (%d in string.format for example).

local intv ="IntValue")

local function int_value_print(v)
	intv.Value = v
	print(string.format("double: %.17g, int64_t: %.0f", v, intv.Value))

int_value_print((string.unpack("<d", "\x00\x00\x00\x00\x00\x00\xF8\x7F")))
int_value_print(2 ^ 63)
int_value_print(2 ^ 63 + 2 ^ (63 - 52))
int_value_print(-(2 ^ 63))
int_value_print(-(2 ^ 63 + 2 ^ (63 - 52)))

The output is (on x86-64, Windows 10, Studio)

double: nan, int64_t: 0
double: 9.2233720368547758e+18, int64_t: -9223372036854775808
double: 9.2233720368547779e+18, int64_t: 0
double: 1.2345678900000001e+21, int64_t: 0
double: -9.2233720368547758e+18, int64_t: -9223372036854775808
double: -9.2233720368547779e+18, int64_t: 0
double: -1.2345678900000001e+21, int64_t: 0
double: inf, int64_t: 0
double: -inf, int64_t: 0

The output is interesting:

  • It shows that IntValue .Value does not only use just cvttsd2si on x86-64 as many values that is out of the range limits of signed quadword integer does not return the indefinite integer (80000000_00000000H) so it does not only use just luai_num2int.
  • but (+/-)0x1p63 does return the x86 indefinite integer (or has wrapped around but I have a strong feeling that it’s the former).

So how does IntValue.Value = cast double that is out of range to int64_t? Does it cast all out of range doubles except 0x1p63 to 0 and use cvttsd2si on other values plus (+/-)0x1p63 on x86-64? I’m asking this because there might be out-of-range doubles other than (+/-)0x1p63 as I’m not certain as I haven’t tested the all values, haven’t reversed engineered this and cannot look at the source code as the Roblox Engine is proprietary.

I am aware that values that cannot be implicitly casted to a number will also return 0 but that does not explain why many doubles out of the signed quadword range limits also returns 0 with the exception of (+/-)0x1p63.

To add to this, is this platform dependant? Would the result be different if IntValue.Value casts out of range doubles to int64_t on aarch64? I don’t have any aarch64 devices that can run Roblox so I can’t answer this exactly.

…and is there any reason why doubles out of 64-bit integer range limit are casted differently to cvttsd2si in x86-64 for IntValues? Is it due to backwards compatibility?

1 Like