Live data from Hacker News

Bit Twiddling Hacks

graphics.stanford.edu

31–38 of 38 posts

Re: Bit Twiddling Hacks

#31

If you're into this sort of thing and like print books, "Hacker's Delight" by Henry S. Warren, Jr. is great.

Came here to say this. "Hacker's Delight" is essential reading if you are an embedded programmer working in small spaces.

And by small, I mean writing code to fit on a credit card, transit ticket, or SIM card. (Yes <2KB ROM budgets still exist.)

Re: Bit Twiddling Hacks

#32
post #30
post #17

Earlier quoted context omitted.

Indeed it's better to use intrinsics in new code rather than rely on compiler magic https://godbolt.org/z/n3z8nz Portability is an issue though, that works on GCC/Clang but you need a different intrinsic for MSVC

Any idea why this hasn't been addressed in the C standard? Like C21 could say "math.h shall have a popcnt() function with these semantics", and then the compiler treats that like the intrinsic?

I don't know why C doesn't have it yet, but TIL they did already standardize it in C++20

https://en.cppreference.com/w/cpp/numeric/popcount

Re: Bit Twiddling Hacks

#33
post #20

Earlier quoted context omitted.

I’ve had that question twice in google phone screen interviews, and iirc, the best answer has changed from the hash table thing to just use the hardware instruction.

I don't believe companies want to hire for knowing that popcnt exists. The interviewer should follow up with the hypothetical situation when either the language or the CPU doesn't have the instruction. Nobody should ask this anyway, this has become the fizzbuzz type of question.

Long before leetcode, I used to ask an "algorithm" question in interviews. I don't remember it exactly, but IIRC it was about determining quickly if a number was 2^n-1 for any n. (Don't shoot me, this was a decade ago.)

There were three broad ways candidates answered it:

1) Hack out a for-loop-style bit count. This was good, because even though it wouldn't be optimized, it demonstrated they could understand the problem and at least conceptualize a solution.

2) Give the "leetcode" best-answer (it was some bitwise math trick). I'd then ask if they'd seen the problem before, and the answer was always yes. This was a mark in their favor—but no better than (1)—and also a signal I needed to ask them a follow-up question that actually made them think.

3) Code a bit, but not quite arrive at a solution. I'd then probe them about their thought process. Not an automatic fail, sometimes our brains just don't walk down the corridors we want them to at a given time, especially during an interview. (One candidate couldn't hold the marker steady because his hands were shaking too much! Poor guy.)

4) Give up and say they had no idea. Obviously the worst case for the interviewee.

The purpose was not to check a candidate's recall and test-taking abilities, but rather to watch them reason through a novel problem, appropriately limited in scope for the interview timeframe. Case (2) served as a great short-circuit to block test-preppers lacking actual experience.

Re: Bit Twiddling Hacks

#34
post #17
post #15

Earlier quoted context omitted.

This of course refers to the POPCNT instruction, that is still quite tricky to get compilers to generate reliably, and which RISC-V specifications and all implementations to date mysteriously lack. Compilers failing to generate POPCNT cause sometimes 10x slowdowns in key operations.

Indeed it's better to use intrinsics in new code rather than rely on compiler magic https://godbolt.org/z/n3z8nz Portability is an issue though, that works on GCC/Clang but you need a different intrinsic for MSVC

Odd that GCC is emitting an xor eax, eax before popcount (in both the intrinsic and pattern cases!):

  popcnt(unsigned int):
          xor     eax, eax
          popcnt  eax, edi
          ret

Re: Bit Twiddling Hacks

#35
post #17

Earlier quoted context omitted.

Indeed it's better to use intrinsics in new code rather than rely on compiler magic https://godbolt.org/z/n3z8nz Portability is an issue though, that works on GCC/Clang but you need a different intrinsic for MSVC

Odd that GCC is emitting an xor eax, eax before popcount (in both the intrinsic and pattern cases!): popcnt(unsigned int): xor eax, eax popcnt eax, edi ret

It's a workaround for a bug in Intel CPUs: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62011

Re: Bit Twiddling Hacks

#36
post #35

Earlier quoted context omitted.

Odd that GCC is emitting an xor eax, eax before popcount (in both the intrinsic and pattern cases!): popcnt(unsigned int): xor eax, eax popcnt eax, edi ret

It's a workaround for a bug in Intel CPUs: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=62011

Interesting, thanks for the pointer.

Re: Bit Twiddling Hacks

#37
post #30
post #17

Earlier quoted context omitted.

Indeed it's better to use intrinsics in new code rather than rely on compiler magic https://godbolt.org/z/n3z8nz Portability is an issue though, that works on GCC/Clang but you need a different intrinsic for MSVC

Any idea why this hasn't been addressed in the C standard? Like C21 could say "math.h shall have a popcnt() function with these semantics", and then the compiler treats that like the intrinsic?

The difficult part is not expressing it. Rather, it is persuading the compiler that the target implements the instruction. gcc and clang have to be convinced they are generating code for an amd64 chip manufactured since 2002 (for which "-mpopcnt" usually works). Common Linux distributions don't turn on any such flags. MSVC offers no way to persuade it, but it will issue the instruction anyway if you use its intrinsic, _popcntN(x), where N is 16, 32, or 64. Shipping Microsoft code doesn't; it is all supposed to work on the oldest amd64 chips ever made.

C++98 gave us std::bitset(x).count(). With C++20 we have std::popcount(x). But getting the compiler to produce the instruction reliably and portably is no easier than before.

Re: Bit Twiddling Hacks

#38
TIL these two lines (to get an integer absolute value without branching) are _patented_:

  int const mask = v >> sizeof(int) * CHAR_BIT - 1;
  r = (v ^ mask) - mask;
Post reply on HN