How to limit a number to a range - java

I have this function to limit a rotation to the range from 0.0 to 360.0:
private float ClampRotation( float rotation ) {
while( rotation < 0.0f ) rotation += 360.0f;
while( rotation >= 360.0f ) rotation -= 360.0f;
return rotation;
}
This functions works great and it probably can't be more efficient, but I'm just wondering if there are a native Java function that can do the same?
The closest I get is Math.min/max, but it doesn't work as this. A rotation of -10.0 should output 350.0 and not 0.0 as min/max would do.

% (modulus) works on floating point values so use rotation % 360.0f (you will need to add 360.0 afterwards to negative numbers)

Use the modulus operator then account for values less than 0;
private float ClampRotation( float rotation ) {
rotation = rotation % 360f;
if (rotation < 0f) rotation += 360f;
return rotation;
}

it's just math.. you can do it like this:
private float ClampRotation( float rotation ) {
return rotation+360.0f*Math.ceil(-rotation/360.0f);
}
i'm pretty sure it's ok

You have the traditional implementation of wrapping angles which are less than an order of magnitude away the desired range.
Modulus is a bit weird for floating point - it returns negative for negative, so you still have to have a branch, and it involves a division, which is slower on some machines ( as in I've not found a machine where % is significantly less expensive than going round a loop with a couple of subtractions two or three times ).
If your values are within say -1000 to +1000, then your version is both clearer and faster. If your values are wider than that, go for a modulus based version. If it's very important, test both on your hardware with the value ranges you are going to use.

Related

Sine approximation error in Java

I'm a bit annoyed with a method I wrote to approximate sine function in Java. Here it is, it's based on Taylor's series.
static double PI = 3.14159265358979323846;
static double eps = 0.0000000000000000001;
static void sin(double x) {
x = x % (2 * PI);
double term = 1.0;
double res = 0.0;
for (int i = 1; term > eps; i++) {
term = term * (x / i);
if (i % 4 == 1) res += term;
if (i % 4 == 3) res -= term;
}
System.out.println(sum);
}
For little values, I got very good approximation of sine, but for large values (e.g pow(10,22)), results seems very very wrong.
Here are the results :
sin(pow(10,22)) // 0.8740280612007599
Math.sin(pow(10,22)) // -0.8522008497671888
Does someone have an idea ? Thank you !
Best regards,
Be reassured that the Java sin function will be off too.
You problem is that the Taylor expansion for sin has a small radius of convergence and convergence is slow even if you're within that radius.
There are floating point considerations too: a floating point double gives you about 15 significant figures of accuracy.
So for large arguments for sin, the accuracy will deteriorate significantly especially given that sin is a periodic function:
sin(x + 2 * pi * n) = sin(x) for any integer n.
Your answer is incorrect for big numbers because you accumulate a lot of rounding errors due to double presentation. When the number is big, then your for loop will iterate a lot before the term becomes smaller than epsilon. In each iteration, a rounding error is accumulated. The result is a very big error in the final value. Read some nice reference on "Numerical Analysis". Anyway, Tylor's series approximate sin near 0, by definition. So, it is normal not to be correct for very big numbers.
The difference actually has nothing to do with the radius of convergence of the Taylor Series and has to do with double precision not being accurate enough to hold the precision required for such big numbers. The radius of the Taylor series for the sine function is infinity.
10^22 is approximately 2^73. Since the mantissa for a double precision number is 52 bits, consecutive values that can be stored with double precision format will be 2^21 apart from each other. Since an evaluation of the sine function requires more resolution than that, you won't be able to reliably get an answer.

Calculating Cos inverse to find angle degree

So, right now I am trying to calculate the angles of a right triangle using the inverse of Cosine. However, I don't really know how to do it. I know the equation, just not how to convert into code. The equation would be:: Cos-1(A/C); However, that does not seem to work in Java. I also tried
angleX = (int) Math.acos(sideC / sideA);
If sideC and sideA were integers, one would have integer division (2 / 3 == 0).
If you do not expect a result in radians, but degrees, a conversion is needed.
As double is an approximation, use round too.
if (sideA == 0) { ... }
angleX = (int) Math.round(
Math.toDegrees(Math.acos(((double)sideC) / sideA)));

How to clamp a value from 0 to infinite to a value from 0 to 1?

I have a program in Java that generates a float value aggressiveness that can be from 0 to infinite. What I need to do is that the higher this float is, the higher there are chances the program fires a function attackPawn().
I already found out that I need the function Math.random(), which gives a random value between 0 and 1. If Math.random() is lower than aggressiveness transformed into a float between 0 and 1, I call the function attackPawn().
But now I am stuck, I can't figure out how I can transform aggressiveness from 0 to infinite to a float which is from 0 to 1, 1 meaning "infinite" aggressiveness and 0 meaning absence of anger.
Any ideas or math equation?
You want a monotonic function that maps [0...infinity] to [0..1]. There are many options:
y=Math.atan(x)/(Math.PI/2);
y=x/(1+x);
y=1-Math.exp(-x);
There are more. And each of those functions can be scaled arbitrarily, given a positive constant k>0:
y=Math.atan(k*x)/(Math.PI/2);
y=x/(k+x);
y=1-Math.exp(-k*x);
There is an infinite number of options. Just pick one that suits your needs.
It is possible to map [0,infinity) to [0,1), but this won't be linear. An example function would be:
y = tan(x * pi / 2);
The problem with this function is that you can't make a correct computer program from that since it is not possible (or easy) to first compute a real big number like 10^5000 and map it down to [0,1).
But a better solution would be to change your definition to something like that:
0 = no aggression
1 = maximum aggression
With this you don't have to map the numbers
Try something like this:
// aggressiveness is a float with a value between 0 and Float.MAX_VALUE or a value of Float.POSITIVE_INFINITY
if (aggressiveness == Float.POSITIVE_INFINITY) {
aggressiveness = 1f;
} else {
aggressiveness = aggressiveness / Float.MAX_VALUE;
}
// aggressiveness is now between 0 and 1 (inclusive)
Though Double class supports infinite value double d=Double.POSITIVE_INFINITY but i dont think you can use it for your arithmatic purpose. Better you define a maximum value and treat it as infinity.
double Min=0;
double Max= Double.MAX_VALUE;
double aggresiveness= Min + (Math.random() * ((Max - Min) + 1));
ps: you can also take aggresiveness as long or int if you don't want it be a double
Try to transform aggressiveness with a function like:
public float function(float aggressiveness) {
if(aggressiveness > 0F) {
return 1 - (1 / aggressiveness);
}
return 0F;
}
This will map your value to the range of [0, 1);

Mathematical issue (Increasing and deceasing two variables inside one loop)

I have a for loop and inside that integer x will increase from 533 to 813. That means 280 increments. In side the same loop I want to decrease y's value from 300 to 200 when above happens. Which means when x is 533 y must be 300 and when x is 813 y must be 200. I know this can do by decrease y's value by 100/280 in each iteration. But both are integers.
Here are some code sample i used but it is not working.:
for(int i = 0; i < b.getSize(); i++) {
x = b.getCar(i).getPosX();
b.getCar(i).setPosX(++x);
if(x >= ((getWidth() / 2) - 140) && x < ((getWidth() / 2) + 140)){
y = b.getCar(i).getPosY();
y = (double)(y - (10.0f / 28.0f));
b.getCar(i).setPosY((int)y);
}
}
How can I possibly do this. Thanks!
There are two solutions, a simple and a complex one. The simple one:
y = yoff + (int)( (double) height * x / width )
where yoff = 300, height = -100, width = 813-533. Basically, you do a floating point operation and then you round the result.
Alternatively, the same can be done using pure integer math using the Bresenham line algorithm but that would take a lot more code.
y must be a double or a float and you need to round its value when you want to use it.
If you wanna do animation, just have a look at interpolators. You can abstract the logic of computing the values in between your extremas.
Basically at the beginning, you give your interpolator the start and end values.
Then, you give the interpolator a time and it gives you back the value between start and end for that time value.
Bonus: it will allow you to change your animation look & feel without touching the rest of the code. (what you are trying to do is in fact a linear interpolation, but it will look much nicer using a sine function for instance, or some other algorithm such as bouncing, quadratic, ...)
It looks like the logic for drawing a line. The Bresenham's algorithm should be the best option.
Keep a helping variable double dy that keeps track of the precise value for y. At each iteration, update dy using your formula, then update y by taking the rounded/truncated value of dy.
NOt sure what you like to do, but your current solution sufers from rounding of floats to integers. To avoid this, calculate with floats / doubles and convert them to integer whensetting positions.

noise() doesn't give me a wide range of values

I was trying to generate a pseudo-random angle in processing today using noise but it is not working as I would have hoped.
float xoff = 0;
float inc = 0.01;
void draw(){
float vx = cos( noise(xoff) * 2 * PI));
xoff += inc;
}
This is the important part of my code. What I thought would happen was that vx would be a random float between -1 and 1 but it is almost always negative. What seems to be the problem is that the noise(xoff) is outputting a limited range of values. Only between 0.3 and 0.7. For vx to be positive it needs to be lower than 0.3 and higher than 0.7, but this never almost never happens.
What is going wrong here?
You might adjust the noiseDetail() to include more than 4 octaves or to use a falloff below 0.5.

Categories