Binary and Hex values should 'just work' for signed values. #717
Replies: 31 comments
-
Seems like ambiguities could arise, especially if a literal is copied and pasted. I really like The fix I'd rather see is for |
Beta Was this translation helpful? Give feedback.
-
I don't understand the problem here. If you are treating the number as signed, then it seems to me showing the sign is the most clear thing you could do. If you want to show the full bit pattern for some reason you are not actually treating it as a signed number, so a cast to force you to state your intent doesn't seem like much of a penalty. I'd argue changing of this behavior would lead to more bugs with virtually no benefit. |
Beta Was this translation helpful? Give feedback.
-
@jnm2 do you have an explicit sample of where an ambiguity could arise (it might be too early, but I can't currently think of any scenarios where 0xFFFF0000 and (int)0xFFFF0000 would produce different results). The only places I can really think of right now are |
Beta Was this translation helpful? Give feedback.
-
That being said, I think explicitly allowing (int)hex-literal or (int)binary-literal to always be unchecked would be good to. |
Beta Was this translation helpful? Give feedback.
-
@MgSam, hex and binary values carry and display the sign in their representation, while decimal values do that by displaying the sign (+/-). Traditionally (or at least in all code I've ever looked at), you display -65536 as:
However, you can also write it as:
It's probably worth noting that C/C++ currently supports what I am proposing: int b = 0b11111111111111110000000000000000; // just works
int x = 0xFFFF0000; // just works
int n = -65536; // just works
auto ab = 0b11111111111111110000000000000000; // unsigned int
auto ax = 0xFFFF0000; // unsigned int
auto an = -65536; // int
auto sb = -0b10000000000000000; // int
auto sx = -0x10000; // int
auto sn = -65536; // int |
Beta Was this translation helpful? Give feedback.
-
Yes, it's an accurate representation of the bits, but its confusing for someone reading the code. If it's supposed to be a negative number, then why not put in a negative sign? Eliding it only increases the chances that someone is confused and thinks a) it really should be an unsigned number or b) misreads the number. C# is intentionally designed to be a higher-level language than C/C++, thus disallowing many things that those languages allow that could lead to bugs. |
Beta Was this translation helpful? Give feedback.
-
I'm not so sure its confusing for someone reading in the code. Today in C# code, I see I'm also fairly certain that is why the comment on just allowing |
Beta Was this translation helpful? Give feedback.
-
Yep, say you're looking at this line: Foo(0xFFFF0000); And you want to use the exact same value in Foo(0xFFFF0000);
Bar(0xFFFF0000); Imagine that each method prints its argument. The output is this:
void Foo(int value) => Console.WriteLine(value);
void Bar(long value) => Console.WriteLine(value); I'd much rather the compiler kept its current complaint and required you to write |
Beta Was this translation helpful? Give feedback.
-
You have the same thing today with: void Foo(uint value) => Console.WriteLine(value);
void Bar(long value) => Console.WriteLine(value); I'm not sure that I do understand the reason people would want to keep it explicit though. If this makes it to LDM and they decide to not just allow |
Beta Was this translation helpful? Give feedback.
-
It does because it signals that the number is interpreted as a negative, unlike existing literals with no sign. |
Beta Was this translation helpful? Give feedback.
-
Also, I think my proposal of making literal casts to const-able types unchecked has wider value, and we can always come back to this if it turns out that we really want to go even further. |
Beta Was this translation helpful? Give feedback.
-
Do you have a proposal up on that yet? If not, you should 😄 |
Beta Was this translation helpful? Give feedback.
-
@tannergooding Current status = trying to keep my head above water with work for the next month (or few?)... but I'd be motivated to write it up if it kept you from going ahead with this 😆 |
Beta Was this translation helpful? Give feedback.
-
I'm going to keep this open regardless (because I would prefer this -- it makes interop code so much easier to write), was mostly just wondering if you had a proposal so that LDM will potentially look at it eventually 😄 |
Beta Was this translation helpful? Give feedback.
-
Well no, it just signals that the number is an int and gets whatever sign is appropriate. If I accidentally drop a digit and it becomes I agree that in ambiguous cases a cast would be useful, but I also think that explicit cases such as These would make interop code - particularly Win32 interop code - much nicer. |
Beta Was this translation helpful? Give feedback.
-
Exactly. But given the context, it'll be unusual to use it unless the number is negative. @lachbaer Can I pawn that off on you? 😁 |
Beta Was this translation helpful? Give feedback.
-
@gafter, I was told (by @jaredpar) that you would be a good person to tag here on possible overload resolution consequences. Under my current proposal, a hex or binary literal will be treated as |
Beta Was this translation helpful? Give feedback.
-
@tannergooding This is the sort of thing I'd be concerned about public void M(int i, string o) ...
public void M(uint u, object s) ...
...
M(0xffffffff, null); // used to call M(uint u, object s) Only the second method is applicable in C# 7. With the new conversion from The tiebreaker you suggest would presumably be inserted into the overload resolution algorithm somewhere to prevent this from turning into an ambiguous call (because both methods are applicable). Perhaps this would be added to the "better conversion from expression" section. The problem is that in this example there is a "better conversion from expression" for the second argument that prefers the first overload. If the new rule makes the first argument expression a "better conversion from expression" to the second overload, then this example changes from one that used to work into one for which there is an ambiguity. That would be a breaking change, which is what we hope to avoid. |
Beta Was this translation helpful? Give feedback.
-
I don't see any confusion. If var is used, the number can be assumed to be the shortest positve type, otherwise the programmer should clear his intentions. |
Beta Was this translation helpful? Give feedback.
-
This ssue is related to the same area: |
Beta Was this translation helpful? Give feedback.
-
The confusion is that the existing program (which contains no |
Beta Was this translation helpful? Give feedback.
-
At least give it a new symbole: 0nb1111_1111. |
Beta Was this translation helpful? Give feedback.
-
@MohammadHamdyGhanem I still say |
Beta Was this translation helpful? Give feedback.
-
@jnm2 The fact that constant expressions are evaluated in a |
Beta Was this translation helpful? Give feedback.
-
@gafter A new rule that if a literal (not just any constant) of an unsigned integer type is being explicitly cast to the signed version, unchecked is not required. |
Beta Was this translation helpful? Give feedback.
-
@jnm2 That would not help with |
Beta Was this translation helpful? Give feedback.
-
@gafter I'm actually mildly happy with that outcome. @tannergooding? |
Beta Was this translation helpful? Give feedback.
-
@gafter did you mean |
Beta Was this translation helpful? Give feedback.
-
@yaakov-h Yes. |
Beta Was this translation helpful? Give feedback.
-
Hmm, this discussion seems very stale but it's the only reference I could find. My use case is I'm trying to understand the binary representation of a decimal value from a hex editor. So I copied out the hex values directly. But I had to research about
Because the raw hex values are as-is I didn't want to do the 2's complement, so at a glance I could verify they match. It would have been much simpler figuring this out if I could simply have cast them to |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Currently
int x = 0xFFFF0000
is invalid as0xFFFF0000
is treated asSystem.UInt32
. The same goes for0b11111111111111110000000000000000
.In order to get the above to work (without casting), one has to define
int x = -65536
,int x = -0x10000
, orint x = -0b10000000000000000
. With casting, one would defineint x = unchecked((int)0xFFFF0000)
orint x = unchecked((int)0b11111111111111110000000000000000)
. None of these methods are as readable as just defining the raw value.I propose that binary and hex values should 'just work'. That is, provided the number of bits/hex-digits defined matches the number of bits/hex-digits in the target type, it should be useable without conversion.
byte/sbyte would allow up-to 8 binary or 2 hex-digits.
short/ushort would allow up-to 16 binary or 4 hex-digits
int/uint would allow up-to 32 binary or 8 hex-digits
long/ulong would allow up-to 64 binary or 16 hex-digits
Beta Was this translation helpful? Give feedback.
All reactions