
Friday JS: minus no minus
Once again, I salute everyone in my traditional heading. Today you will learn what was so special about December 31, 1969, exactly one millisecond before midnight. More precisely, you will learn not only this, but only for this example I could pick up a picture, and an entertaining article without pictures is nonsense.

I’ve been teaching a bit lately. In order to expand the student’s consciousness, I asked him the following task:

In formulating the problem, I hinted at one specific way related to the topic that we recently went through. But after that I thought: what methods still exist in this language rich in non-obvious possibilities? The results of several hours of reflection on this subject, I would like to share with you.
The easiest and most glitchless way to do subtraction without subtraction is to somehow get the value “minus one”, and then write:
If you somehow get the string "-", you can simply turn it into a minus one:
If we want to do without these little tricks, pain awaits us. It will be delivered to us, firstly, by special values (Infinity, NaN), and secondly, a possible loss of accuracy during less trivial operations on numbers. But this does not mean that we do not need to try. Everything that does not kill us makes us stronger.
The first way, which, in my opinion, should come to the mind of a beginner is to use Array # indexOf . Of course, this is not the first suitable thing that you can stumble upon if you systematically read Flanagan in order. However, a beginner does not need to read Flanagan in order, as he will quickly drown in an abundance of unnecessary information. Array # indexOf successfully combines simplicity and practical utility, therefore I am inclined to consider this the most obvious solution.
The indexOf method, as its name implies, returns the index of the element in the array. If there is no such element in the array, the special value -1 is returned. Very handy.
And this is the first thing that should have occurred to some stern sishnik. For example, like this:
Tilde in javascript symbolizes bitwise negation. Due to the peculiarities of the internal representation of negative numbers bitwise negation of zero magically turns minus one. By the way, the opposite is also true, because of which some people have the habit of writing the condition for an element to enter the array as follows:
Now, with the advent of Array # includes , this hack is becoming less relevant.
Also, minus one can be obtained in more sophisticated ways. For example, a bitwise shift:
And this is the first thing that mathematics should come to mind. The methods of the global Math object provide many ways. For instance:
Or alternative ways:
By the way, the method with a logarithm makes it possible to subtract numbers “directly”, without first getting minus one:
However, I already wrote about the problems of this approach in “general considerations”.
There are many ways to get the string "-". Perhaps the most obvious one is this:
You can also take advantage of the wonderful features of Unicodethey failed in hell :
In addition, this character can be pulled from a string that already contains it. For example, like this:
By the way, if we got a minus sign, we don’t have to get minus one. It can be done as follows:
For this, of course, you will have to be born a ringworm in your next life, but if you read this article before the current sentence, obviously you have nothing to lose.
And since we are talking about dates, here is another way to get minus one:
The fact is that javascript dates “under the hood” contain the so-called Unix time - the number of milliseconds that have passed since midnight on January 1, 1970. Accordingly, on the thirty-first of December 1969, at 23:59:59 and 999 milliseconds, this value was exactly -1.
Finally, I’ll give a couple of complicated and poorly working methods.
If both numbers are positive, finite and the first is greater than the second, you can use division with the remainder.
This will work due to the fact that
If you think carefully, you can get rid of the cycle. Indeed, a cycle passes more than zero iterations only if b fits into a more than once. This can be avoided as follows:
However, this method still does not work correctly with negative numbers (due to the fact that the operator "%" works very strange with them), with a subtracted greater than a decremented one and with special values.
And finally (drum roll, fanfare), in the best traditions of computational mathematics, we can calculate the difference using the half division method:
Again, this method only works if a> = b, and if none of the numbers is infinity or NaN.
This is where I end. If you managed to come up with a method that is significantly different from the ones given in the article, be sure to write about it in the comments. Have a good Friday!

I’ve been teaching a bit lately. In order to expand the student’s consciousness, I asked him the following task:
Write a sub (a, b) function that will find the difference between the numbers a and b. However, the text of the function should not contain a “-" character.Now for the curious reader, the time has come to postpone reading the article and try to solve the problem on their own. Therefore, so that he does not accidentally see one of the solutions below, I will insert a picture with a snowflake that does not melt until the clock strikes twelve.

In formulating the problem, I hinted at one specific way related to the topic that we recently went through. But after that I thought: what methods still exist in this language rich in non-obvious possibilities? The results of several hours of reflection on this subject, I would like to share with you.
General considerations
The easiest and most glitchless way to do subtraction without subtraction is to somehow get the value “minus one”, and then write:
return a + b * minusOne;
If you somehow get the string "-", you can simply turn it into a minus one:
let minusOne = (minusChar + 1) | 0;
If we want to do without these little tricks, pain awaits us. It will be delivered to us, firstly, by special values (Infinity, NaN), and secondly, a possible loss of accuracy during less trivial operations on numbers. But this does not mean that we do not need to try. Everything that does not kill us makes us stronger.
The most obvious
The first way, which, in my opinion, should come to the mind of a beginner is to use Array # indexOf . Of course, this is not the first suitable thing that you can stumble upon if you systematically read Flanagan in order. However, a beginner does not need to read Flanagan in order, as he will quickly drown in an abundance of unnecessary information. Array # indexOf successfully combines simplicity and practical utility, therefore I am inclined to consider this the most obvious solution.
function sub(a, b){
let minusOne = [].indexOf(0);
return a + b * minusOne;
}
The indexOf method, as its name implies, returns the index of the element in the array. If there is no such element in the array, the special value -1 is returned. Very handy.
Bit operations
And this is the first thing that should have occurred to some stern sishnik. For example, like this:
function sub(a, b){
let minusOne = ~0;
return a + b * minusOne;
}
Tilde in javascript symbolizes bitwise negation. Due to the peculiarities of the internal representation of negative numbers bitwise negation of zero magically turns minus one. By the way, the opposite is also true, because of which some people have the habit of writing the condition for an element to enter the array as follows:
if(~arr.indexOf(elem)){ //...
Now, with the advent of Array # includes , this hack is becoming less relevant.
Also, minus one can be obtained in more sophisticated ways. For example, a bitwise shift:
let minusOne = 1 << 31 >> 31;
Math
And this is the first thing that mathematics should come to mind. The methods of the global Math object provide many ways. For instance:
function sub(a, b){
let minusOne = Math.cos(Math.PI);
return a + b * minusOne;
}
Or alternative ways:
let minusOne = Math.log(1/Math.E);
//или даже так
minusOne = Math.sign(Number.NEGATIVE_INFINITY);
By the way, the method with a logarithm makes it possible to subtract numbers “directly”, without first getting minus one:
function sub(a, b){
return Math.log( Math.E ** a / Math.E ** b);
}
However, I already wrote about the problems of this approach in “general considerations”.
Lines
There are many ways to get the string "-". Perhaps the most obvious one is this:
function sub(a, b){
let minusChar = String.fromCharCode(45);
let minusOne = (minusChar + 1) | 0;
return a + b * minusOne;
}
You can also take advantage of the wonderful features of Unicode
let minusChar = "\u002d";
In addition, this character can be pulled from a string that already contains it. For example, like this:
let minusChar = 0.5.toExponential()[2];
// 0.5.toExponential() == "5e-1"
minusChar = (new Date(0)).toISOString()[4].
//(new Date(0)).toISOString() == "1970-01-01T00:00:00.000Z"
By the way, if we got a minus sign, we don’t have to get minus one. It can be done as follows:
function sub(a, b){
let minusChar = "\u002d";
return eval("(" + a + ")" + minusChar + "(" + b + ")");
}
For this, of course, you will have to be born a ringworm in your next life, but if you read this article before the current sentence, obviously you have nothing to lose.
When the year comes young
And since we are talking about dates, here is another way to get minus one:
let minusOne = Date.UTC(1969, 11, 31, 23, 59, 59, 999);
The fact is that javascript dates “under the hood” contain the so-called Unix time - the number of milliseconds that have passed since midnight on January 1, 1970. Accordingly, on the thirty-first of December 1969, at 23:59:59 and 999 milliseconds, this value was exactly -1.
Do not repeat at home
Finally, I’ll give a couple of complicated and poorly working methods.
If both numbers are positive, finite and the first is greater than the second, you can use division with the remainder.
function sub(a, b){
let r = a % b;
while(r + b < a){
r += b;
}
return r;
}
This will work due to the fact that
a == a % b + b * n
, where n is some integer. Accordingly, a - b == a % b + b * (n - 1)
and, therefore, adding to the remainder of b, we will sooner or later get the desired value. If you think carefully, you can get rid of the cycle. Indeed, a cycle passes more than zero iterations only if b fits into a more than once. This can be avoided as follows:
function sub(a, b){
return (a + a) % (a + b);
}
However, this method still does not work correctly with negative numbers (due to the fact that the operator "%" works very strange with them), with a subtracted greater than a decremented one and with special values.
And finally (drum roll, fanfare), in the best traditions of computational mathematics, we can calculate the difference using the half division method:
function sub(a, b){
var d = 1; // дельта. то, что мы будем пытаться прибавить к b так, чтобы получилось не более чем a
var r = 0; // наш будущий результат вычитания
//сначала находим d, превышающее разность.
while(b + d < a){
d *= 2;
}
//далее последовательно прибавляем его к r, при необходимости уменьшая вдвое
while(b + r < a){
if(b + r + d > a){
d /= 2;
}else{
r += d;
}
}
//в силу конечной точности представления чисел в js этот процесс когда-нибудь закончится
return r;
}
Again, this method only works if a> = b, and if none of the numbers is infinity or NaN.
This is where I end. If you managed to come up with a method that is significantly different from the ones given in the article, be sure to write about it in the comments. Have a good Friday!