Negative numbers in binary and hexa


The conversion of -1 to binary or hexadecimal is returning -1, is this normal?
val = -1
val.to_s(2) or val.to_s(16) is returning -1, while everywhere I look they use the 2-complement (for instance -1 in the windows calc gives FFFF FFFF FFFF FFFF‬)

Ruby also gives -1. I think FFFF FFFF FFFF FFFF‬ meaning both -1 and 18446744073709551615 is confusing. I guess that if you want negative numbers to be converted like that you can do it yourself. The question is: how many "F"s do you want? Of course it depends on the type… but again, I think it’s not clear that -1 should be FFFF FFFF FFFF FFFF‬ because it’s not bijective: if you want to go back to the original value from that you have no idea which value was it.

1 Like

base 16 and binary representation are different things. might help if you want to deal with the later.

Personally I think it is a bit strange that (-1).to_s(2) does not give the binary representation of -1 but if Ruby is doing the same then it is better to stick with this.
For how many ‘F’ I want, it would depend on the type of -1 (int32 or int64 for instance).
I can get what I want by doing this: (0_u32 -1).to_s(16), but it would be better to have something that works for any integer type.

You’re actually mixing up two completely different concepts.

A binary number is just a number expressed in base-2 notation. Apart from the digits, everything else is exactly the same as in other number systems like decimal or hexadecimal. For example, negative numbers are obviously prefixed by a minus sign.
The decimal number -1 expressed as binary number is also just -1. The hexadecimal value FFFF FFFF FFFF FFFF is just a simple number, equivalent to decimal 18446744073709551615.
These conversions are all handled by .parse and #to_s methods on Crystal’s number types.

When you expect FFFF FFFF FFFF FFFF to mean -1, you’re actually talking about a integer data type representation which uses the two’s complement to encode a signed number. This is just a specific data format used to represent arbitrary values (in this case: a signed integer) as binary data.
This encoding is usually handled by Crystal internally and you don’t actually see it, but it can be explicitly used with the IO::ByteFormat type.