Once again, I welcome everyone to my traditional category. Today, you will learn what happened in this special way on December 31, 1969, exactly a millisecond before midnight. More precisely, you will find out not only this, but only for this example I was able to pick up a picture, and an entertaining article without pictures is nonsense.
Recently, I have been teaching a little bit. In order to expand the consciousness of the student, I asked him the following problem:
Write the function sub (a, b), which will find the difference of the numbers a and b. However, the text of the function should not contain the character "-".
Now it’s time for the inquisitive reader 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 while the clock is twelve beating.
')
In formulating the task, I hinted at one particular way related to the topic that we recently went through. But after that, I wondered: what ways still exist in this language rich in non-obvious possibilities? I would like to share with you the results of several hours of reflection on this topic.
General considerations
The easiest and glitch-free 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, we are in for pain. First of all, we will deliver it with special values ​​(Infinity, NaN), and secondly, a possible loss of accuracy with 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.
Most obvious
The first way that, in my mind, should come to mind to a beginner is to use
Array # indexOf . Of course, this is not the first suitable thing that you can stumble upon if you read Flanagana methodically in order. However, a newcomer does not need to read Flanagan in order, as he quickly drowns in an abundance of unnecessary information. Array # indexOf successfully combines simplicity and practical utility, because I tend to believe this is 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 riffle. For example:
function sub(a, b){ let minusOne = ~0; return a + b * minusOne; }
The tilde in javascript symbolizes bitwise negation. Because of the
peculiarities of the internal representation of negative numbers, the bitwise negation of zero magically turns out to be a minus one. Incidentally, the reverse is also true, which is why some people have a habit of writing the condition for an element to be included in an 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 should come to mind math. The methods of the global Math object provide many ways. For example:
function sub(a, b){ let minusOne = Math.cos(Math.PI); return a + b * minusOne; }
Or alternative ways:
let minusOne = Math.log(1/Math.E);
By the way, the method with logarithm makes it possible to subtract the numbers "directly", without first obtaining 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”.
Strings
There are many ways to get the "-" string. Perhaps the most obvious one:
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
; they fell to hell :
let minusChar = "\u002d";
In addition, this symbol can be pulled out of the line that already contains it. For example:
let minusChar = 0.5.toExponential()[2];
By the way, if we get a minus symbol, we don’t have to get minus one. 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 in the next life as a ringworm, 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’s another way to get a minus one:
let minusOne = Date.UTC(1969, 11, 31, 23, 59, 59, 999);
The fact is that javascript dates “under the hood” contain so-called
Unix time is the number of milliseconds that have elapsed since midnight on January 1, 1970. Accordingly, on December 31, 1969, at 1159:59 and 999 milliseconds, this value was exactly -1.
Do not repeat at home
Finally, I will give a couple of complex 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 an integer. Accordingly,
a - b == a % b + b * (n - 1)
, which means that by adding b to the remainder, we will sooner or later get the desired value.
If you think carefully, you can get rid of the cycle. Indeed, the 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 with them very strangely), with the subtracted more decreasing and with special values.
And finally (drum roll, fanfare), in the best traditions of computational mathematics, we can calculate the difference by the method of half division:
function sub(a, b){ var d = 1;
Again, this method works only if a> = b, and if none of the numbers is infinity or NaN.
On this I finish. If you managed to come up with a method that is significantly different from those given in the article, be sure to write about it in the comments. Good Friday to you!