Odd math behavior in rbx.lua

Regular sin(x) returns 0 when x = pi and x = 0, then why on earth does math.sin(x) return 1.2246467991474e-16 in Roblox when x = pi, and what would the value for x need to be to get math.sin(x) to return 0 except when x = 0?

Edit: This is very odd. If I do math.sin(3.14) then it returns 0.0015926529164868, which is very close to 0, but if I add the two next numbers in pi to that, then math.sin(3.1415) gives me 9.265358966049e-05.

This just sounds like a floating point precision issue. 1e-16 is so small it might as well be treated as 0. Note the negative sign, this is actually 0.0000000000000001.

2 Likes

This is because math.pi isn’t truly pi. All numbers in lua are double-point floating-precision numbers, a subset of real numbers that can not express all real numbers. As such, an operation like 1 + 2 can sometimes be 2.999; ~3. The solution, if need be, might be to have a small range of values you accept as being equal to the integer value of 0.

1 Like

So do you reckon rounding it down would be a fix?

Almost assuredly, yes, that would be my recommendation.

You could but I don’t see this causing many issues on its own if you’re calculating a bunch of things. If you’re comparing you might want to test <= eps or similar instead of ==.

What would be the most accurate way to round it down though? As math.floor rounds down towards the closest full number and the decimals are important in my use-case.

Edit: What I’ve previously done is basically math.floor(number*1000 + .5)/1000 but I don’t know if that’s the best way to go about this.

I did actually not know that. I have never done mathematics where e-16 or e+16 etc. has been used as we use something else for that in school and on Roblox the numbers I usually work with never go that low so that’s good to know. :slight_smile:

It depends on what precision you desire. In essence, you can do the following. Here is an example annotated with Luau type annotations for clarity:

--- Function that rounds the given value to the given precision.
local function round(value: number, precision: number?): number
	local precision = precision or 0;
	local factor = 10 ^ precision;
	assert(precision >= 0, "The given precision must be a positive number or zero!");
	assert(math.floor(precision) == precision, "The given precision must be an integer!");
	return math.floor(value * factor + 0.5) / factor;
end

print(round(20.757)) -- Rounds to the nearest integer
-- Prints "21"

print(round(-20.757, 1)) -- Rounds to the nearest tenth
-- Prints "-20.8"

print(round(20.757, 2)) -- Rounds to the nearest hundredth
-- Prints "20.76"

2 Likes

I see it’s only for numbers above 0? What if the number is <0 ?

Oops! I forgot that case, I’ve updated my last post to reflect this functionality.

Thanks for the effort and help both of you @TheEdgyDev & @Autterfly, I got what I needed.

1 Like

Ehh? Shouldn’t that result in -20.8?

round(20.757, 1) gives result 20.8, so why is there a whole 0.1 in difference when rounding the negative -20.757 to one decimal?

Maybe offset is to always be a positive 0.5 due to using math.floor?

Absolutely! Surprised I didn’t catch that quirk sooner; the original post now reflects the fixed code.

1 Like