> it’s faster and takes up less characters. It’s not quite as readable though, but I’m hoping that ~~ will slowly become a very well-known technique in the JS arena, so we can all use it without fearing accusations.
Yeah, no thanks. I'll take well named functions over these kind of syntax tricks any day.
Do you use a well named function like and(), or do you use the double ampersand (&&) syntax trick? What about a not() function, or the exclamation mark syntax trick (!)? :)
Every use of operators can be considered a syntax trick until it becomes common, which is what the author hopes will happen to ~~
This operator is not currently common for intended audience of my code. And also, it's intended meaning is so much less often useful than meaning of other operators, that I don't think it ever becomes common.
> Every use of operators can be considered a syntax trick
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.
Then we should consider these reserved keywords and run a transpiler on our code that converts those to the unholy && and || syntax that makes the code really hard to read. You shouldn't expect every junior programmer to invest the necessary time to learn the meaning of all this archaic symbol salad.
While at it we should also remove curly braces and go with Basic's if/end if, function/end function and so on.
`&&` and `||` are very well defined (for the better part, there's languages where `||` does more than just Logical OR.), this double-not hack is just a hack, and can be achieved in many more ways. Stop trying to be obfuscatingly clever, the right time and place for that is outside of professional environments. You don't go around applying tricks from Hacker's Delight without explaining them, do you?
But using the '&&' operator is common enough, while using something like the Javascript void operator would not be, and so would need in a shared codebase a reason for usage other than to try to look smart.
Well, surely that comparison is a bit unfair. :) In my books, using `~~` is closer to shit like abusing `[]+[]` to get an empty string, than using `&&` which is its own separate, widely used operator in the language.
You could maybe compare `~~` to something like `if (!someString)` but, again, that's a widely used and way more easily understood pattern.
This is not the same at all. What OP does is a *hack* to convert some arbitrary value to a number. This should be very explicit, to avoid having 9999999999999999999999999999999999 and 1 more way to do the same thing.
The issue is that it's a JS only syntax trick. Things like x || y, or even !!x work in most languages, so would be readable to someone who has never used JS before and is trying to read your code base. ~~x would be int32(x) or int(x) in most other languages, so it's not going to be something that's universally learned.
Also, 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?
I would hate reading code that uses stuff like this. Give things readable names, please! I'm a dumb programmer, don't make my job even harder.
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.
Nothing is more unreadable than "readable" names. I absolutely detest working with code that uses for example stuff like ImmutableJS, that turns every property access into `.get('')` instead of a simple `.` operator.
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.
I don't think having to use .get() for property access is equatable to "readable names". It is also not the reason for this in the library you mentioned AFAIK, although I haven't used it so far. I think the reason for the .get() is that it is not achievable to have deep immutability in JS with regular property access (Object.freeze is not "deep" either).
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.
To be fair early JS development was full of “one weird tricks” that made code unreadable. Thankfully jslint came along and started cleaning up this sort of stuff. Now ESLint and its plugins target a lot more unreadable code, like the `no-array-reduce` rule.
Because `Math.floor` is correct for all numbers but `~~` will be incorrect outside of int32 ranges and non-numbers (`Math.floor("asdf")` is NaN but `~~"asdf"` is 0). Basically it is a shorthand for limited cases, but it is so convenient for those cases that I think it is a worthy trick to know with caveats.
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 `~~`.
Bitwise operators only work on the first 32-bits, so it isn't as easy as `instead of doing the Math.floor operations, check for edge cases and just the same operation as ~~`
1) Add the year! This is ~~2010! Is this even still valid today? Is it really faster in 2023?
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!
I've mentioned in other comments, but it should be noted that `~~x` or `x | 0` is not same as `Math.floor(x)`. It is rather a shorthand for `Math.trunc(x)` that is almost correct for x between -2^31 to 2^31 - 1. (Almost, because `Math.trunc(-0.5)` should return -0 while `~~(-0.5)` is +0.) There is also a variant `x >>> 0` which works for x between 0 to 2^32 - 1.
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.
I wanted to comment the same thing but then I realized that this post predates Math.trunc by 4 years, hence it makes sense why it’s described in terms of floor and ceil instead :-)
Nice, I don't recall I ever saw `x >>> 0`, bit it reminds me of the infamous "x goes to zero" operator `x --> 0` [0] and its even more expressive sibling, the "x slides to zero" operator [1].
> ~~‘s flooring capabilities make it a better alternative to Math.floor if you know you’re dealing with positives — it’s faster and takes up less characters.
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?
I think the post makes a good argument in favor of it. It's shorter, meaning it's cleaner, less visual noise. You just have to learn what it does. That's part of the job. That's what it means to be a skilled developer. It's absolutely not hard to remember that this is a "round to zero, or make it zero if it's something weird", and that functionality seems very useful.
It's not "less noise" just because it's shorter. Code golf is extremely hard to read exactly because it's so terse. If Math.ceil is making your code hard to read, there's probably something else going on. Being a good coder means elegantly expressing intent with code, not memorizing hacks.
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.
It's just a matter of internalizing the "intent" of ~~. And then suddenly the code is "clean" again. Just like `!!` is a socially acceptable way of casting to bool.
The slides from 2009 that the article links to are full of severely outdated advice[0]. I just gave the "parseInt" trick a try, for example, and the built-in wins or is equal in performance[1] (although I'm mildly disappointed that `parseInt` isn't significantly faster).
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?)
OK here's how I think it works: from the wikipedia page on two's complement[1], the sign bit is the bit directly to the left of the most significant bit (not the first bit in the 32 bit representation), giving:
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:
> (Even if the first bit were interpreted as the sign, shouldn't this be -2,147,483,642?)
No, 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
Do we really need syntactic sugar for bitwise operators in a language like Javascript? I would rather have longer keywords for that and free those tokens for more widely used use cases (as having just | VS |> for the proposed pipe operator).
46 comments
[ 4.1 ms ] story [ 90.6 ms ] threadYeah, 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.
Also, 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?
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 `~~`.
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!
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.
[0] https://stackoverflow.com/q/1642028/493553
[1] https://stackoverflow.com/a/8909176/493553
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
;
[0] https://www.slideshare.net/madrobby/extreme-javascript-perfo...
[1] https://jsbenchit.org/?src=00e164b878d3c009649d4f8a9de6e7f6
No, 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
> 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?