cdq copies the sign-bit of EAX into all bits of EDX.
Having its sign bit set is equivalent to EAX being less-than zero.
-1 is all-bits-set in a 2's complement machine like x86.
Another way to describe cdq is sign-extending EAX into EDX:EAX.
The AT&T mnemonic for it is cltd, very easy to confuse with cltq, so I recommend just using the Intel mnemonics for those instructions (cdq vs. cdqe, where the E for "Extend" is a reminder that it's within one register, into RAX.)
In general, you can try asking a compiler to see if you can get them to use a single instruction for something, or look at the instructions they generate.
Or use a superoptimizer which is software that brute-force tries possible instruction sequences to find the best / shortest one to produce a given result, for a given set of inputs. (Only viable for very short sequences, like a handful of instructions.)
By hand / by eye, you can see that mov doesn't set FLAGS according to the value, so that rules out any instructions like cmov. And there aren't any instructions that branch based on EAX (unlike loop for RCX), and you don't want that anyway. For me, the solution came to mind as soon as I saw it was storing EDX into b. The combination of EAX and EDX, along with needing a 0 or -1, reminded me of cdq.