Developer Hub erroneously reports that math.noise should be in 0.5 to -0.5 range

the values for math.noise actually range from 0.75304806232452 to -0.60881221294403

repo: run this in the command line

local biggest = 0
local smallest = 0
for i = 1, 5000000 do
	local noise = math.noise(i/100000, i/100000, i/100000)
	biggest = math.max(biggest, noise)
	smallest = math.min(smallest, noise)
end
print(biggest, smallest)
4 Likes

Seems to be that someone on the Developer Hub took a bit too much liberty with their assumptions and just put down on the page that it should be between -.5 and .5. As far as I know, this guarantee was never given on the old wiki.

See this article: (also on why your test may not be representative of the true range)

Perlin noise should not generate values between -.5 and .5. It is a Gaussian, values |x| > .5 are just much less likely than values closer to 0.

I’m going to move this over from Engine Bugs to Documentation Requests.

EDIT: Article link: https://www.robloxdev.com/articles/Lua-Libraries/math#math.noise

2 Likes

iirc it was listed as -0.5 to 0.5, somewhere at least. i brought this up with urist in the roblox IRC towards the end of 2015

i think it’s fairly arbitary based on the implementation of the noise function, although i really don’t think that a range of -0.608… to 0.753… was the intended behavior when implementing the function

all in all, i really think this should stay in bug reports… updating the documentation on the wiki is a decent bandaid fix but i think having the noise function throw out a weird value range is not intended behavior, nor is it desirable for development

It is possible to get up to 1. For example:

print(math.noise(0.92074228588735, 550891/60, 550891/100000))
>> 0.99338990449905

The maximum/minimum values you get do appear to depend on the ratio of the rate that your inputs are changing, which is a bit odd

Could you run another test with random samples in the full 3d range? Right now you’re taking a diagonal through the space (i.e. x=y=z) which is at a regular interval, it might occlude some of the range due to the nature of perlin noise.

2 Likes
local biggest = 0
local smallest = 0
local noise = math.noise
local rand = math.random
for i = 1, 10000000 do
	local n = noise(rand(0,1000000)/10000, rand(0,1000000)/10000, rand(0,1000000)/10000)
	biggest = math.max(biggest, n)
	smallest = math.min(smallest, n)
end
print(biggest, smallest)

you’re right, this test returned values between -0.99… and 0.99…, i think the documentation should be updated as well to include information about the gaussian nature of the value distribution and how to potentially handle it: “since values outside [-0.7,0.7] are extremely rare, one can quite safely divide the noise function by 0.7, and clip away any extreme values.”

You can actually sometimes get values slightly outside of the range [-1,1], so if the interval is critical to you, you should use math.clamp(noise, -1, 1) on the output. Try this variant, and see what you get:

local biggest = -math.huge
local smallest = math.huge
local rng = Random.new(0)
for i = 1, 10000000 do
    local noise = math.noise(rng:NextNumber()*1000000, rng:NextNumber()*1000000, rng:NextNumber()*10000000)
    biggest = math.max(biggest, noise)
    smallest = math.min(smallest, noise)
end
print(biggest, smallest)

I got values as high as 1.012939453125

3 Likes

The documentation for math.noise states that it will return a number between -0.5 and 0.5. However, this is not true. Sometimes, the result will be slightly outside of those bounds. For instance, math.noise(9.9, 9.6) will return -0.50271731615067. Clearly, that’s out of bounds.

Now this issue was brought up 4 years ago. @zeuxcg’s response to the thread makes it sound like these bounds are not correctly documented.

So what should the bounds be? I don’t know.


This is important to document, because developers need to know that they need to explicitly clamp the noise value between -0.5 and 0.5 to properly use the value for clean data.

14 Likes

I did literally run into this issue not too long ago. We need ranges to be documented so that we can get a clear understanding of math.noise()

1 Like

The documentation for math.noise states

Returns a perlin noise value between -1 and 1.

and

For fractional values of x, y and z, the return value will gradually fluctuate between -0.5 and 0.5.

Albeit unlikely, it’s possible for math.noise to return a number outside of those ranges.

Example:

print(math.noise(146588.46920057799434289336,463921.38527622632682323456,939101.59364105493295937777))
print(math.noise(828431.51955171884037554264,796509.57584820687770843506,67208.56080836096953134984))

Output:

1.0202153921127
-1.0242896080017

I’m not sure sure exactly what the range is, but it’s certainly not (-1,1) or [-0.5,0.5].

6 Likes

I dug into this tonight and here are my findings.

1D noise is bounded by [-0.5, 0.5].
2D noise is bounded by (-1, 1)
3D noise is bounded by ???

I have probably done over a hundred million random samples for each dimension of noise. The 3D bounds are unknown without the exact implementation, but the 1D and 2D results are significant enough imo to warrant documentation.

Methodology

1D

for i = 1, 1e6 do
	local a = math.random() * 1e4
	if math.abs(math.noise(a)) >= 0.5 then
		print(math.noise(a), a)
	end
end
print("DONE")

2D (might take a few tries)

for i = 1, 1e6 do
	local a, b = math.random() * 1e4, math.random() * 1e4
	if math.abs(math.noise(a, b)) > 0.98 then
		print(math.noise(a, b), a, b)
	end
end
print("DONE")

3D (might take a few tries)

for i = 1, 10e6 do
	local a, b, c = math.random() * 1e4, math.random() * 1e4, math.random() * 1e4
	if math.abs(math.noise(a, b, c)) > 1 then
		print(math.noise(a, b, c), a, b, c)
	end
end
print("DONE")
5 Likes

Did you scroll up in the thread? None of your findings are correct, this has been tested before. It is possible for values to occur (at very low frequency) even outside those ranges.

The range of the noise function is different depending on how many inputs you give it. The posts above focus on 3D whereas I’ve included bounds for 1D and 2D. You can try my tests for yourself.

EDIT: Well sleitnick did mention 2D but his bounds don’t contradict mine.

2 Likes

The point is that it’s like a bell curve. Values outside of the “regular range” are totally possible but infrequent. It doesn’t make sense at all to document an unbounded probability function of values as “bounded” between X and Y. The DevHub should describe what is actually returned so that developers know to clamp and not hit obscure bugs that are hard to debug just because they didn’t account for it going out of bounds since the devhub didn’t tell them to.

1 Like

My claim is that this isn’t true.

This article gives a proof for the range of Perlin noise being [-sqrt(N)/2, sqrt(N)/2] with N being the number of dimensions. That is,

3D: ~0.866
2D: ~0.707
1D: 0.5

Roblox’s Perlin noise function doesn’t adhere to the ranges for 3D and 2D and I don’t know why.

I set up this game today to distribute calculations among multiple clients. At one point we had about 20 people each doing a million random samples every 10 frames. The largest values we calculated are

3D: 1.0336
2D: 1
1D: 0.5

Obviously this isn’t definitive proof of the range, but I believe it warrants looking into the algorithm to actually calculate it. Without the algorithm, the best I can do is numerical estimates.

2 Likes

Looks like I misread your dimensions, I skimmed and thought you came to the same conclusions as someone earlier in the thread, but your ranges are higher than what the others mentioned.

Going back to the original point I was trying to make: the dev hub should just describe the nature of the distribution more and not just say “it’s between X and Y” because that isn’t necessarily all of the information an uninformed reader would need to know to figure out what they need to do to make sure the noise is in a range suitable for their use case (they might want to clamp more/less or otherwise transform the data depending on the kind of distribution/range they want). And as you mention, for the actual bounds themselves, doing massive amounts of experiments is an indicator but not an actual proof on the implementation.

2 Likes

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.