algorithm/math help

hi again guys,

this thread will be about math and how math is used in algorithms,so most algorithms are based on math, so it's hard to avoid using simple to complex math when designing algorithms

my main question is the order and how math works in C++

the below code creates particles and moves them around the screen some of the code is abstracted but the code I am focusing on is included,so the code in particular I am focusing on is this directly below

1
2
3
4
5
        int elapsed = SDL_GetTicks();
        red = (unsigned char) ((1 + sin(elapsed * 0.00002)) * 128);
        green = (unsigned char) ((1 + sin(elapsed * 0.0003)) * 128);
        blue = (unsigned char) ((1 + sin(elapsed * 0.0004)) * 128);


note this isn't my code I'm following a tutorial on Udemy,elapsed gets a random number the function sin takes an argument elapsed * 0.00002 1 is added to the result of this and it is then multiplied by 128,also I see a lot of people putting 1 + before Rand() why is this done,I'm guessing it used to specify a range but how so??


then after this he multiplied it by 128 I'm not sure why this is needed? half 256 is 128,there is 256 possible values in a char(byte)so I'm guessing it has something to do with this?

thanks

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
  int SDL_main(int argc,char* argv[])
{


    initScreen();
    srand(time(NULL));

    int ex = ((2.0 * rand())/RAND_MAX)-1;
    cout << ex << endl;


    bool quit = false;
    unsigned char red = 0;
    unsigned char green = 0;
    unsigned char blue = 0;

    for(int y = 0; y < SCREENHEIGHT; y++){

        for(int x = 0; x <SCREENWIDTH; x++){

            setPixel(x,y,0,0,0);
        }
    }

    Swarm swarm;


    while(!quit){

        SDL_PollEvent(&event);

        if(event.type == SDL_QUIT){

            quit = true;
        }

        clear();
        swarm.update();

        int elapsed = SDL_GetTicks();
        red = (unsigned char) ((1 + sin(elapsed * 0.00002)) * 128);
        green = (unsigned char) ((1 + sin(elapsed * 0.0003)) * 128);
        blue = (unsigned char) ((1 + sin(elapsed * 0.0004)) * 128);

        Particle* particles = swarm.getParticles();



        for(int i = 0; i < Swarm::NParticles; i++){

           int x = (particles[i].x + 1) * SCREENWIDTH / 2;
           int y = (particles[i].y + 1) * SCREENHEIGHT / 2;

           setPixel(x,y,red,green,blue);
        }
       update();


    }


    }
sin(x) varies between -1 and 1
so
1 + sin(x) varies between 0 and 2
so
(1+sin(x)) * 128 varies between 0 and 256
which is (sort of) the range of an unsigned char (hoping you never actually get it floating-point-equal to 256). With luck, integer truncation takes it to 0 to 255.

It's not very random, though: the shape of the sine curve means it will spend proportionately longer nearer the two extremes.
Last edited on
thanks lastchance,

that makes perfect sense

elapsed * 0.00002

but why the need to multiply it by 0.00002 I'm guessing this slows it down exponentially?
I mostly agree with lastchance, but I would personally just do round(255.0 * (1.0 + sin(x)) / 2.0); This also would avoid the problem when sin(x) == 1.0.

also I see a lot of people putting 1 + before Rand() why is this done
For things like dice rolling, because rand() delivers a range of [0, RAND_MAX], doing rand() % 6 then delivers a range [0, 5], and +1 is added to make it the actual dice numbers (range 1 to 6, inclusive).

Obligatory note that it's possible for rand() to not be very uniform, and if the % operator is used in combination with rand, that can skew the distribution as well so that it's less uniform (doesn't matter for most simple games, though can matter for scientific simulations).
<random> contains improved C++11 random utilities http://www.cplusplus.com/reference/random/

but why the need to multiply it by 0.00002 I'm guessing this slows it down exponentially?
Well, it would slow it down linearly by 0.00002 :)
That's just a magic number that the author decided to make the frequency feel right. It depends on how often that loop is being processed (60 fps? not capped? etc.).

When it comes to games, something called a timestep can be very important, to make sure the game's logic doesn't speed up or slow down depending on your framerate -- with many older games (like '90s N64), the game itself would slow down when rendering a large number of items. This doesn't usually happen in modern games, though.
https://gafferongames.com/post/fix_your_timestep/
Last edited on
thanks Ganado,

in the tutorial the presenter says that the sine function will always return a number ranging from -1 to +1 same with the cosine function,

but I don't understand why this would be the case,it's probably some simple math I'm missing I watched a couple of videos on khanacademy but can't seem to get where the function always returns a number in the range of -1 to +1?


thanks
... ok ...
look at what the sine function IS. the input is an angle. angles, of course, go from 0 to 360 degrees. but looking at that 360 degrees and 0 degrees are the SAME angle on an X/Y plane. 361 and 1 degrees are the SAME as well, and so on. This means that the input to the function is effectively circles back every time you cross that 0/360/720 etc mark. This means the input is effectively a circle, so you want to study the 'unit circle' online at an introductory math site.

as to its limits, it has to do with basic triangle side ratios. The sides of triangles relate to the angles, and right triangles specifically are what you want to look at here. The sine is the opposite side's length over the hypotenuse length. Drawn in X/Y coordinates from zero, try to draw a right triangle where the ratio opposite/hypotenuse is not in the range -1 to 1. You cannot do it --- this ratio always falls into that range! Since the sine is derived from that ratio, it also follows that range.

combine the two ideas and you will see that the sine function's inputs circle around on the degrees and the range is -1 to +1 and it forms a never-ending wave when you plot this.

There are math forums and online sites that explain this in any depth you want; asking on a c++ code forum is probably not your best resource. **Note that in c++ sine wants radians not degrees, but these are just units and you can directly convert. At higher levels of math you will always want to use radians, but from your question, you are not there yet and it was just one more thing to explain.
Last edited on
thanks Jonnin that makes sense :)


just a side note

1
2
3
4

int elapsed = SDL_GetTicks();
        red = (unsigned char) ((1 + sin(elapsed * 0.002)) * 128);


this code will transition from light red to dark red at a smooth consistency , but

1
2
3
4
5

int elapsed = SDL_GetTicks();
        red = (unsigned char) (1 + sin(elapsed * 0.002)) * 128;



when I remove one set of brackets like above the colour changes are erratic,Why would just adding these set of brackets have such a drastic change on the outcome ?

when casting do we need to put both the object and type in brackets ??

such as (unsigned int) (doubleNumber)? I thought you could just emit to brackets (unsigned int) doubleNumber and get the same result?


thanks
C-style casts have a precedence higher than *, +, /, - operations.

With parentheses, it forces it to evaluate the complete inside, ((1 + sin(elapsed * 0.002)) * 128), and then cast it.

Without parenthesis, it follows the precedence rules and casts (1 + sin(elapsed * 0.002)) to an unsigned char, and then multiplies that result by 128.

As discussed above, since the range of sin(x) is limited to [-1, 1], that means that 1 + sin(x) is limited to [0, 2]. That means, after casting it to unsigned char, it can only be a 0, 1, or 2.
After that,
0 * 128 = 0
1 * 128 = 128
2 * 128 = 256 = 0 (mod 256, because we're dealing with unsigned chars).

So instead of gently transitioning, you will immediately go from 0 to 128 back to 0.

I would use static_cast<unsigned char>( <equation> ) because it (1.) makes it easier to search for a cast, and (2.) forces you to put the code you are actually casting inside parentheses.

Last edited on
thanks Ganado,

so
1
2
int elapsed = SDL_GetTicks();
        red = (unsigned char) ((1 + sin(elapsed * 0.002)) * 128);


so first sin(elapsed * 0.002) returns a double between -1 to +1 and we add +1 so we get a range from 0 to 2, we then multiply this value each time by 128 and then cast it to an unsigned char,I am guessing the parts past the decimal point are truncated right? so 127.871 would be rounded to 128?


1
2
int elapsed = SDL_GetTicks();
        red = (unsigned char) (1 + sin(elapsed * 0.002)) * 128;


where as in this example as you said would get the value of sin(elapsed * 0.002) + 1 giving us a range of 0 - 2 and this will straight away be cast to an unsigned char then multiplied by 2?

thanks

I am guessing the parts past the decimal point are truncated right? so 127.871 would be rounded to 128?

You are correct that they are being truncated, but this means 127.871 becomes 127. Not rounded to 128.

giving us a range of 0 - 2 and this will straight away be cast to an unsigned char then multiplied by 2?

Correct.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <cmath>
using namespace std;

int main() {
	
	for (int elapsed = 0; elapsed < 2000; elapsed += 151) // just a sampling of possible elapsed values
	{
		// (1)
		//int red = static_cast<unsigned char>( (1 + sin(elapsed * 0.002)) * 128 );

		// (2)
		int red = (unsigned char) (1 + sin(elapsed * 0.002)) * 128;
		cout << red << '\n';	
	}
}


(1)
Success	#stdin #stdout 0s 15232KB
128
166
200
228
247
255
252
237
212
180
143
105
68
37


(2)
Success	#stdin #stdout 0s 15232KB
128
128
128
128
128
128
128
128
128
128
128
0
0
0
Last edited on
Topic archived. No new replies allowed.