JavaScript: Double Bitwise Not (~~) (2010)
j11y.io
j11y.io
Yeah, no thanks. I'll take well named functions over these kind of syntax tricks any day.
Every use of operators can be considered a syntax trick until it becomes common, which is what the author hopes will happen to ~~
That's blatantly false for strict languages, like JavaScript. For a function, all arguments are evaluated before the function is evaluated. For operators this is not true. On the and-case, the second argument is only evaluated if the first one evaluates to something true-ish.
You can only use this supposed and()-function, if you pass closures instead of values and not every language has those.
While at it we should also remove curly braces and go with Basic's if/end if, function/end function and so on.
You could maybe compare `~~` to something like `if (!someString)` but, again, that's a widely used and way more easily understood pattern.
Further you're using a side effect of ~ to get rounding. It isn't being used for its intended purpose.
I get your point and to some extent agree with it.
It's more in the class of
While(foo++ = bar++)
You can do it, but you're to some extent abusing constructs.
If it becomes widely used then it's acceptable, but if not it deemed as bad code. Or clever, depending on your world view.
$ perl -E 'say 1,2,3,4 and 5; say 1,2,3,4 && 5'
1234
1235Also, there are multiple ways to convert to int (~~ is equivalent to truncate). Why leave it ambiguous instead of using Math.trunc / Math.floor / Math.ceil?
Essentially this trick exploits how JS initially implemented bitwise operations while having no integer-only types. It is safe to use---ECMAScript is unlikely to change bitwise operators in future versions---but only useful for limited cases. Those limited cases, like low-level emulations, happen to be cases where such tricks are very convenient though. As long as you don't naively convert `Math.floor(x)` with `x | 0` or `x >>> 0` I'm fine with them, but this article seems to encourgate that.
Edit: I just realized that the post is from 2010. This makes the post even more horrible. In 2010 you had to deal with internet explorer and other very ugly compatibility stuff on the client side. Javascript was an absolute pain and if someone would write code like this on the frontend, I would run as far away as I could.
I wouldn't use this for no reason or just to be fancy, but if you're working in a context that legitimately requires integer division all over the place, you should use syntax for it if it's available.
I'd be annoyed if I had to work with something maths-y that has gigantic lines full of Math.trunc() everywhere.
That aside, I agree it's not ergonomic, but it has nothing to do with readable names.
And converting a string to an integer is not possible with a native JS operator intended for this purpose, so I'd prefer Math.trunc any day.
Regarding the immutable.js thing, the Records/Tuples TC39 proposal aims for a syntax and runtime addition to allow immutable objects with regular dot property access.
Is there a difference between `| 0` and `~~`?
[1] https://tc39.es/ecma262/#sec-numeric-types-number-bitwiseNOT vs. https://tc39.es/ecma262/#sec-numberbitwiseop
ADDED: As per performance, there is a difference, but it is hard to quantify exactly. `Math.floor` with large enough argument won't fit in any integer type, so JIT can't assume that. But it is mostly the case that floating point arithmetics are as fast as integer arithmetics in modern architectures, so unless we completely know how floor outputs are to be used, that argument can't be used to prefer either `Math.floor` or `~~`.
[0] https://www.slideshare.net/madrobby/extreme-javascript-perfo...
[1] https://jsbenchit.org/?src=00e164b878d3c009649d4f8a9de6e7f6
5 => 00000000000000000000000000000101
~5 => 11111111111111111111111111111010
But parseInt("11111111111111111111111111111010", 2) is 4,294,967,290, not -6. (Even if the first bit were interpreted as the sign, shouldn't this be -2,147,483,642?) 5 => 0101
And the only way I can get this to give -6 is if the bitwise NOT operator takes this entire representation (0101), inverts the bits, then creates a new sign bit and inverts that, giving: ~5 => 11010
[1]: https://en.wikipedia.org/wiki/Two's_complementNo, in two-complement (U2) encoding, negative numbers "start from the top". This lets CPUs have same addition and subtraction instructions for signed and unsigned integers, and rely on overflows to get correct value. E.g. (-1)_i8 + 2_i8 = b11111111 + b00000010 = b(1)00000001 = 1
2) this "trick" has problems if you don't understand the caveats it has.
3) This kind of micro optimisation isn't usually were your code is slow. Any css shift will be more expensive then this.
4) IF you want to use this "trick" then make sure everybody who will need to read your code really understands what you are doing here. Dont be that smart pants coder were no body else then you can read your code! This does not make you a good programer!
> This is obviously a fair bit slower than ~~.
I don't think the code posted before this comment is particularly fast, but are we sure this double-not is faster?
Good lord. This takes me back to 2012 when all the cool kids were putting bangs before functions so that you didn’t have to use semicolons anywhere. I’ve basically decided as a career goal that I’ll never write JavaScript for money again, so it’s no skin off my nose, but why does the JavaScript world constantly go through these bizarre contortions of misusing syntax for vague “performance” gains at the expense of communicating intent?
As for performance, if misusing an operator like this is actually faster, then that means there should be an easy patch to V8 that would make it faster without miscommunicating intent. Regardless, just like readability, if Math.ceil even registered as anything in a performance profile you must be doing something very weird. Like implementing Torch in JavaScript weird.
, commas
, first
;
let a = 1, b = 2
~~b
// 2
~~a
// 1
~~(b - a)
// 0