Valid solution for Javascript sin() and cos()? - javascript

A client insists that sin(Math.PI) and cos(Math.PI / 2) should return zero, not something around 10^-16. He's not happy with the explanation that Math.sin() and Math.cos() are the way they are, not only in Javascript but in all other languages.
One thing I found, is that Math.sin() is insensitive to parameter changes smaller than 2e-16:
Math.sin(Math.PI)
1.2246063538223773e-16
Math.sin(Math.PI + 1e-16)
1.2246063538223773e-16
Math.sin(Math.PI + 2e-16)
1.2246063538223773e-16
Math.sin(Math.PI + 3e-16)
-3.216285744678249e-16
Since sin(x)~=x when sin(x) is near zero, it ocurred to me to cast sin(x) to zero when x is smaller than 2e-16.
Math.cos() is more precise, it is insensitive to changes up to 1.1e16 (EDIT: it happens because base value is smaller: Math.PI/2) so I would cast cos(x) to zero when it is smaller than 1e-16:
Math.cos(Math.PI / 2)
6.123031769111886e-17
Math.cos(Math.PI / 2 + 1e-16)
6.123031769111886e-17
Math.cos(Math.PI / 2 + 1.1e-16)
6.123031769111886e-17
Math.cos(Math.PI / 2 + 1.5e-16)
-1.6081428723391245e-16
Of course, such a cast would ruin the original good precision of sin(x) when x->0:
Math.sin(1e-99)
1e-99
Math.sin(1e-50)
1e-50
Math.sin(1e-40)
1e-40
Math.sin(1e-20)
1e-20
Math.sin(1e-10)
1e-10
Math.sin(1e-5)
0.000009999999999833334
But if the application were using such small angles, it should be using x directly, not sin(x), correct? Since sin(x) tends totally to x in this range.
Considering that the application has 10 digit of UI precision, do you feel my strategy is right?

You can decide on how close to zero you want to return zero-
Number.prototype.rounded= function(i){
i= Math.pow(10, i || 15);
// default
return Math.round(this*i)/i;
}
Math.sin(Math.PI).rounded()
/* returned value: (Number) 0 */
Math.PI.rounded(5)
/* returned value: (Number) 3.14159 */

Related

How to calculate sen on javascript using math tag?

I'm coding a site that calculates the area of ​​regular polygons, and I'm having problems building the formula.
Here is the formula:
((a**2)*b)/(4*tan(π/b)))
a = Size of the side
b = Number of the sides
For exemple:
a = 10
b = 5
Area = 172
i tryed these syntaxs, but the result is not 172:
((10**2) * 5) / (4 * Math.tan(math.PI / 5))
((10**2) * 5) / (4 * Math.tan(3.1415 / 5))
I now that the problem is here: "(4 * Math.tan(3.1415 / 5))", because if i put the directly the value of the "tan(π/5)", which is "0.726542528", the formula works...
((10**2) * 5) / (4 * 0.726542528) = 172
Which is the correct syntax?
If you need ((a**2)b)/(4tan(π/b))) implemented in JS, then the first step is to actually write that out fully instead of omitting implied operations:
((a**2) * b) / (4 * tan(π/b))
And of course, a bunch of parentheses are irrelevant here:
a**2 * b / (4 * tan(π/b))
Then you map that to JS, which it pretty much already is except for that tan and π, both of which we can get from the Math object:
const { PI:π, tan } = Math;
function getPolygonArea(a,b) {
return a**2 * b / (4 * tan(π/b));
}
Done. That formula works now and yields 172.04774005889672 for getPolygonArea(10,5).

What is the easiest way to calculate position of balls on collision?

I'm trying to make some simple pool game in java script. I have made it but I do not love way of checking if two balls will collide in next frame. I would like to have more easier way to calculate coordinates of balls when collision occurs. I found lot of answers base on collision kinematics, how to handle velocities and directions after collision, but no calculating a position when collision occurs.
As you can see in sample diagram, gold ball is moving slower than a blue ball, and with distance that each ball will have to move on next frame will not be considered as collision. But, as you can see, they should collide (dashed lines).
In that cause I have divided each movement into sectors and calculating if distance between the points is equal or smaller than ball diameter, which is slowing down process when many balls (like in snooker) have to be calculated in each frame, plus that way is not always 100% accurate and balls can go in inaccurate angles after hit (not a big difference, but important in snooker).
Is there any easier way to calculate those (XAC,YAC) and (XBC,YBC) values with knowing start positions and velocities of each ball without dividing ball paths into sectors and calculating many times to find a proper distance?
It is worth to precalculate collision event only once (this approach works well with reliable number of balls, because we have to treat all ~n^2 pairs of balls).
The first ball position is A0, velocity vector is VA.
The second ball position is B0, velocity vector is VB.
To simplify calculations, we can use Halileo principle - use moving coordinate system connected with the first ball. In that system position and velocity of the first ball are always zero. The second ball position against time is :
B'(t) = (B0 - A0) + (VB - VA) * t = B0' + V'*t
and we just need to find solution of quadratic equation for collision distance=2R:
(B0'.X + V'.X*t)^2 + (B0'.X + V'.Y*t)^2 = 4*R^2
Solving this equation for unknown time t, we might get cases: no solutions (no collision), single solution (only touch event), two solutions - in this case smaller t value corresponds to the physical moment of collision.
Example (sorry, in Python, ** is power operator):
def collision(ax, ay, bx, by, vax, vay, vbx, vby, r):
dx = bx - ax
dy = by - ay
vx = vbx - vax
vy = vby - vay
#(dx + vx*t)**2 + (dy + vy*t)**2 == 4*r*r solve this equation
#coefficients
a = vx**2 + vy**2
b = 2*(vx*dx + vy*dy)
c = dx**2+dy**2 - 4*r**2
dis = b*b - 4*a*c
if dis<0:
return None
else:
t = 0.5*(-b - dis**0.5)/a ##includes case of touch when dis=0
return [(ax + t * vax, ay + t * vay), (bx + t * vbx, by + t * vby)]
print(collision(0,0,100,0,50,50,-50,50,10)) #collision
print(collision(0,0,100,0,50,50,-50,80,10)) #miss
print(collision(0,0,100,0,100,0,99,0,10)) #long lasting chase along OX axis
[(40.0, 40.0), (60.0, 40.0)]
None
[(8000.0, 0.0), (8020.0, 0.0)]
Regarding to MBo's solution, here is a function in java script that will calculate coordinates of balls on collision and time in which collision will happen:
calcCollisionBallCoordinates(ball1_x, ball1_y, ball2_x, ball2_y, ball1_vx, ball1_vy, ball2_vx, ball2_vy, r) {
let dx = ball2_x - ball1_x,
dy = ball2_y - ball1_y,
vx = ball2_vx - ball1_vx,
vy = ball2_vy - ball1_vy,
a = Math.pow(vx, 2) + Math.pow(vy, 2),
b = 2 * (vx * dx + vy * dy),
c = Math.pow(dx, 2) + Math.pow(dy, 2) - 4 * Math.pow(r, 2),
dis = Math.pow(b, 2) - 4 * a * c;
if (dis < 0) {
//no collision
return false;
} else {
let t1 = 0.5 * (-b - Math.sqrt(dis)) / a,
t2 = 0.5 * (-b + Math.sqrt(dis)) / a,
t = Math.min(t1, t2);
if (t < 0) {
//time cannot be smaller than zero
return false;
}
return {
ball1: {x: ball1_x + t * ball1_vx, y: ball1_y + t * ball1_vy},
ball2: {x: ball2_x + t * ball2_vx, y: ball2_y + t * ball2_vy},
time: t
};
}
}

Calculating π using a Monte Carlo Simulation limitations

I have asked a question very similar to this so I will mention the previous solutions at the end, I have a website that calculates π with the client's CPU while storing it on a server, so far I've got:
'701.766.448.388' points inside the circle, and '893.547.800.000' in total, these numbers are calculated using this code. (working example at: https://jsfiddle.net/d47zwvh5/2/)
let inside = 0;
let size = 500;
for (let i = 0; i < iterations; i++) {
var Xpos = Math.random() * size;
var Ypos = Math.random() * size;
var dist = Math.hypot(Xpos - size / 2, Ypos - size / 2);
if (dist < size / 2) {
inside++;
}
}
The problem
(4 * 701.766.448.388) / 893.547.800.000 = 3,141483638
This is the result we get, which is correct until the fourth digit, 4 should be 5.
Previous problems:
I messed up the distance calculation.
I placed the circle's from 0...499 which should be 0...500
I didn't use float, which decreased the 'resolution'
Disclamer
It might just be that I've reached a limit but this demonstration used 1 million points and got 3.16. considering I've got about 900 billion I think it could be more precisely.
I do understand that if I want to calculate π this isn't the right way to go about it, but I just want to make sure that everything is right so I was hoping anyone could spot something wrong or do I just need more 'dots'.
EDIT: There are quite a few mentions about how unrealistic the numbers where, these mentions where correct and I have now updated them to be correct.
You could easily estimate what kind of error (error bars) you should get, that's the beauty of the Monte Carlo. For this, you have to compute second momentum and estimate variance and std.deviation. Good thing is that collected value would be the same as what you collect for mean, because you just added up 1 after 1 after 1.
Then you could get estimation of the simulation sigma, and error bars for desired value. Sorry, I don't know enough Javascript, so code here is in C#:
using System;
namespace Pi
{
class Program
{
static void Main(string[] args)
{
ulong N = 1_000_000_000UL; // number of samples
var rng = new Random(312345); // RNG
ulong v = 0UL; // collecting mean values here
ulong v2 = 0UL; // collecting squares, should be the same as mean
for (ulong k = 0; k != N; ++k) {
double x = rng.NextDouble();
double y = rng.NextDouble();
var r = (x * x + y * y < 1.0) ? 1UL : 0UL;
v += r;
v2 += r * r;
}
var mean = (double)v / (double)N;
var varc = ((double)v2 / (double)N - mean * mean ) * ((double)N/(N-1UL)); // variance
var stdd = Math.Sqrt(varc); // std.dev, should be sqrt(Pi/4 (1-Pi/4))
var errr = stdd / Math.Sqrt(N);
Console.WriteLine($"Mean = {mean}, StdDev = {stdd}, Err = {errr}");
mean *= 4.0;
errr *= 4.0;
Console.WriteLine($"PI (1 sigma) = {mean - 1.0 * errr}...{mean + 1.0 * errr}");
Console.WriteLine($"PI (2 sigma) = {mean - 2.0 * errr}...{mean + 2.0 * errr}");
Console.WriteLine($"PI (3 sigma) = {mean - 3.0 * errr}...{mean + 3.0 * errr}");
}
}
}
After 109 samples I've got
Mean = 0.785405665, StdDev = 0.410540627166729, Err = 1.29824345388086E-05
PI (1 sigma) = 3.14157073026184...3.14167458973816
PI (2 sigma) = 3.14151880052369...3.14172651947631
PI (3 sigma) = 3.14146687078553...3.14177844921447
which looks about right. It is easy to see that in ideal case variance would be equal to (Pi/4)*(1-Pi/4). It is really not necessary to compute v2, just set it to v after simulation.
I, frankly, don't know why you're getting not what's expected. Precision loss in summation might be the answer, or what I suspect, you simulation is not producing independent samples due to seeding and overlapping sequences (so actual N is a lot lower than 900 trillion).
But using this method you control error and check how computation is going.
UPDATE
I've plugged in your numbers to show that you're clearly underestimating the value. Code
N = 893_547_800_000UL;
v = 701_766_448_388UL;
v2 = v;
var mean = (double)v / (double)N;
var varc = ((double)v2 / (double)N - mean * mean ) * ((double)N/(N-1UL));
var stdd = Math.Sqrt(varc); // should be sqrt(Pi/4 (1-Pi/4))
var errr = stdd / Math.Sqrt(N);
Console.WriteLine($"Mean = {mean}, StdDev = {stdd}, Err = {errr}");
mean *= 4.0;
errr *= 4.0;
Console.WriteLine($"PI (1 sigma) = {mean - 1.0 * errr}...{mean + 1.0 * errr}");
Console.WriteLine($"PI (2 sigma) = {mean - 2.0 * errr}...{mean + 2.0 * errr}");
Console.WriteLine($"PI (3 sigma) = {mean - 3.0 * errr}...{mean + 3.0 * errr}");
And output
Mean = 0.785370909522692, StdDev = 0.410564786603016, Err = 4.34332975349809E-07
PI (1 sigma) = 3.14148190075886...3.14148537542267
PI (2 sigma) = 3.14148016342696...3.14148711275457
PI (3 sigma) = 3.14147842609506...3.14148885008647
So, clearly you have problem somewhere (code? accuracy lost in representation? accuracy lost in summation? repeated/non-independent sampling?)
any FPU operation will decrease your accuracy. Why not do something like this:
let inside = 0;
for (let i = 0; i < iterations; i++)
{
var X = Math.random();
var Y = Math.random();
if ( X*X + Y*Y <= 1.0 ) inside+=4;
}
if we probe first quadrant of unit circle we do not need to change the dynamic range by size and also we can test the distances in powered by 2 form which get rid of the sqrt. These changes should increase the precision and also the speed.
Not a JAVASCRIPT coder so I do not know what datatypes you use but you need to be sure you do not cross its precision. In such case you need to add more counter variables to ease up the load on it. For more info see: [edit1] integration precision.
As your numbers are rather big I bet you crossed the boundary already (there should be no fraction part and trailing zeros are also suspicious) For example 32bit float can store only integers up to
2^23 = 8388608
and your 698,565,481,000,000 is way above that so even a ++ operation on such variable will cause precision loss and when the exponent is too big it even stop adding...
On integers is this not a problem but once you cross the boundary depending on internal format the value wraps around zero or negates ... But I doubd that is the case as then the result would be way off from PI.

Implementing an accurate cbrt() function without extra precision

In JavaScript, there is no native cbrt method available. In theory, you could use a method like this:
function cbrt(x) {
return Math.pow(x, 1 / 3);
}
However, this fails because identities in mathematics don't necessarily apply to floating point arithmetic. For example, 1/3 cannot be accurately represented using a binary floating point format.
An example of when this fails is the following:
cbrt(Math.pow(4, 3)); // 3.9999999999999996
This gets worse as the number gets larger:
cbrt(Math.pow(165140, 3)); // 165139.99999999988
Is there any algorithm which is able to calculate a cube root value to within a few ULP (preferably 1 ULP if possible)?
This question is similar to Computing a correctly rounded / an almost correctly rounded floating-point cubic root, but keep in mind that JavaScript doesn't have any higher-precision number types to work with (there is only one number type in JavaScript), nor is there a built-in cbrt function to begin with.
You can port an existing implementation, like this one in C, to Javascript. That code has two variants, an iterative one that is more accurate and a non-interative one.
Ken Turkowski's implementation relies on splitting up the radicand into mantissa and exponent and then reassembling it, but this is only used to bring it into the range between 1/8 and 1 for the first approximation by enforcing a binary exponent between -2 and 0. In Javascript, you can do this by repeatedly dividing or multiplying by 8, which should not affect accuracy, because it is just an exponent shift.
The implementation as shown in the paper is accurate for single-precision floating-point numbers, but Javascript uses double-precision numbers. Adding two more Newton iterations yields good accuracy.
Here's the Javascript port of the described cbrt algorithm:
Math.cbrt = function(x)
{
if (x == 0) return 0;
if (x < 0) return -Math.cbrt(-x);
var r = x;
var ex = 0;
while (r < 0.125) { r *= 8; ex--; }
while (r > 1.0) { r *= 0.125; ex++; }
r = (-0.46946116 * r + 1.072302) * r + 0.3812513;
while (ex < 0) { r *= 0.5; ex++; }
while (ex > 0) { r *= 2; ex--; }
r = (2.0 / 3.0) * r + (1.0 / 3.0) * x / (r * r);
r = (2.0 / 3.0) * r + (1.0 / 3.0) * x / (r * r);
r = (2.0 / 3.0) * r + (1.0 / 3.0) * x / (r * r);
r = (2.0 / 3.0) * r + (1.0 / 3.0) * x / (r * r);
return r;
}
I haven't tested it extensively, especially not in badly defined corner cases, but the tests and comparisons with pow I have done look okay. Performance is probably not so great.
Math.cbrt has been added to ES6 / ES2015 specification so at least first check to see if it defined. It can be used like:
Math.cbrt(64); //4
instead of
Math.pow(64, 1/3); // 3.9999999999999996
You can use formula for pow computation
x^y = exp2(y*log2(x))
x^(1/3) = exp2(log2(x)*1/3)
= exp2(log2(x)/3)
base for log,exp can be any but 2 is directly implemented on most FPU's
now you divide by 3 ... and 3.0 is represented by FP accurately.
or you can use bit search
find the exponent of output (e= ~1/3 of integer part bit count of x)
create appropriate fixed number y (mantissa=0 and exponent=e)
start bin search from MSB bit of y
toggle bit to one
if (y*y*y>x) toggle bit back to zero
loop #3 with next bit (stop after LSB)
The result of binary search is as precise as it can be (no other method can beat it) ... you need mantissa-bit-count iterations for this. You have to use FP for computation so conversion of your y to float is just copying mantissa bits and set exponent.
See pow on integer arithmetics in C++

How do you compare radians in a circle?

I'm thinking of a wheel of fortune type of thing where you have to check if a marker lands on a slot. Visually, we can see 0, 2pi, 4pi, etc... are in the same position. How do I check if 0 is equal to 2pi/4pi programmatically? Assuming I have the radius as well, I thought, maybe I have to convert it to cartesian coordinates first and then compare it. However, is there a better way to do this?
Edit: Also, I should make clear that the mark can land anywhere in between a slot. For example, the marker could be anywhere in between 0 to pi/6.
Is this what you want?
var smallestEquivalentValueInRadians = originalValueInRadians % (2 * Math.PI);
If you want to compare, you can do:
a % (2 * Math.PI) === b % (2 * Math.PI)
% is the modulo operator, basically it subtracts the second operand from the first, as many times as it can (assuming you are dealing with positive numbers).
To cater for negative values:
function normalizeRadian(a) {
var circle = Math.PI * 2;
a = a % circle;
if (a < 0) {
a += circle;
}
return a;
}
Also when comparing floats, it's a good idea to have some "fuzzyness", since opperations on them can be imprecise.
function fuzzyEqual(a, b) {
var fuzz = 0.001;
return a < b + fuzz && a > b - fuzz;
}
So the complete solution is:
function fuzzyEqualRadians(a, b) {
return fuzzyEqual(normalizeRadian(a), normalizeRadian(b));
}

Categories