Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Proposal: Binary literals #215

Closed
MadsTorgersen opened this issue Feb 3, 2015 · 40 comments
Closed

Proposal: Binary literals #215

MadsTorgersen opened this issue Feb 3, 2015 · 40 comments

Comments

@MadsTorgersen
Copy link
Contributor

There’s a relatively common request to add binary literals to C# and VB. For bitmasks (e.g. flag enums) this seems genuinely useful, but it would also be great just for educational purposes.

Binary literals would look like this:

int nineteen = 0b10011;

Syntactically and semantically they are identical to hexadecimal literals, except for using b/B instead of x/X, having only digits 0 and 1 and being interpreted in base 2 instead of 16.

There’s little cost to implementing these, and little conceptual overhead to users of the language.

Syntax

The grammar would be as follows:

integer-literal:
  ...
binary-integer-literal

binary-integer-literal:
0bbinary-digitsinteger-type-suffixopt
0Bbinary-digitsinteger-type-suffixopt

binary-digits:
binary-digit
binary-digitsbinary-digit

binary-digit:   one of
0 1

@monoman
Copy link

monoman commented Feb 4, 2015

👍

@AdamSpeight2008
Copy link
Contributor

A think a digit separator would also help. _
0b_10000000_00000000_00000000_0000_0000

@ddevault
Copy link
Contributor

ddevault commented Feb 4, 2015

👍

@alanfo
Copy link

alanfo commented Feb 5, 2015

Good idea and, whilst you're at it, why don't you add octal literals as well?

VB and F# already support these which are still occasionally useful when doing interop with older unmanaged code or translating it to C#.

Perhaps a prefix of '0o' or '0O' followed by the octal digits would be a suitable syntax as this is what F# and Swift use.

@tmat
Copy link
Member

tmat commented Feb 6, 2015

@alanfo I don't think we should add new features like octal literals to C# whose only benefit is to make somewhat convenient to deal with legacy code.

@ddevault
Copy link
Contributor

ddevault commented Feb 6, 2015

That's not the only benefit. I would benefit from this in several modern projects. Anything that uses bitwise math stands to gain a lot from binary literals. Here are a couple of examples:

@ddevault
Copy link
Contributor

ddevault commented Feb 6, 2015

Mask out the lower five bits - & 0xE0 or & 0b11100000?

@tmat
Copy link
Member

tmat commented Feb 6, 2015

@SirCmpwn I meant octal, not binary. Binary literals are certainly useful. My comment perhaps wasn't clear.

@ddevault
Copy link
Contributor

ddevault commented Feb 6, 2015

@tmat no worries. An obvious use case that comes to mind for octal is Unix file permissions.

@paulomorgado
Copy link

@tmat, @SirCmpwn and since that's where C# is heading hard and heavy now, it might make sense to consider the effort of introducing octal numbers.

@tmat
Copy link
Member

tmat commented Feb 6, 2015

@SirCmpwn @paulomorgado Could you give an example of source code that would need to specify permissions in octal?

I would rather specify them using
[Flags]enum Permission { None = 0, Execute = 1, Write = 2, Read = 4}

and then new FilePermission(owner: Permission.Read | Permission.Write, group: Permission.Read, all: Permission.None)

I don't see how I would use octal numbers, other then to obfuscate and write something like int permission = 0o077.

@ddevault
Copy link
Contributor

ddevault commented Feb 6, 2015

Unix permissions are ubiquitous. A very large number of programmers recognize them when written in octal. When I see 0o777, I immediately think "all permissions for all people". 0o755 tells me "full perms for owner and read+execute for others". Which is better?

int permission = (Permission.Read | Permission.Write | Permission.Execute << PermissionGroup.Owner) |
                 (Permission.Read | Permission.Execute) << PermissionGroup.Group |
                 (Permission.Read | Permission.Execute) << PermissionGroup.User;

or...

int permission = 0o777;

@tmat
Copy link
Member

tmat commented Feb 6, 2015

Makes sense - in some contexts you might need to work with Linux permissions often and is advantageous to have a shortcut. However, I'm still not convinced this use case warrants a language feature.

You can write a helper method today that converts from decimal number to octal and use it like so:
int permission = Permission(755);

@alanfo
Copy link

alanfo commented Feb 6, 2015

@tmat

I wouldn't underestimate the value of being able to deal more conveniently with legacy code to some C# developers, particularly as we're only talking about a relatively trivial addition to the language. The effort needed to implement octal literals (using the suggested prefix) would be no more than for binary literals.

Sure, if I'm translating C code to C#, I can write a method to convert octal literals to decimal - let's say:

      public static int FromOctal(int octal)
      {
          bool negative = (octal < 0);
          if (negative) octal = -octal;
          int multiplier = 1;
          int result = 0;
          while(octal > 0)
          {
              int digit = octal % 10;
              result += multiplier * digit;
              octal /= 10;
              multiplier <<= 3;
          }  
         if (negative) result = -result;
         return result;
     }

and then replace 0145 in C, say, with FromOctal(0145).

However, as in the case of Unix permissions, this isn't very convenient when in F# one could simply write 0o145 or in VB &O145.

Moreover, octal literals would have the same educational value as binary literals - they'd help teach students that decimal isn't the only game in town :)

BTW, I certainly wouldn't suggest that we used the same syntax as C/C++/Java for expressing octal literals i.e. simply prefix with them a zero. That wouldn't be possible now in C# anyway as it would be a breaking change (0145 is the same as 145 decimal) and it always was a horrible, confusing syntax in my view.

@ddevault
Copy link
Contributor

ddevault commented Feb 6, 2015

I've been mostly playing devil's advocate for octal here, since I don't really have a use-case. That being said, I don't see any reason not to implement it - the actual implementation would be pretty simple and the side effects are minimal, if they exist at all. If there are use-cases, why not implement octal?

@paulomorgado
Copy link

@tmat,

I also don't have a use-case. Probably the teams working on porting .NET to *nix systems have some experience by now.

Your DCO (Decimal-Coded Octal - as opposed to BCD) seems good enough for me to handle permissions.

@dsaf
Copy link

dsaf commented May 23, 2015

@MadsTorgersen Please consider #263 for C# 7 as well. It's currently closed due to absence of lexical grammar, but I have just submitted a draft one.

@wedge905
Copy link

wedge905 commented Jul 8, 2015

Yes, please implement binary literals! I was sad to see it got cut from C#6.
This would be extremely useful in NetMF!

@Tizana
Copy link

Tizana commented Jul 12, 2015

As embedded engineer and making C# code for Windows IOT on Rasbperry pi and working a lot on binary register and bitwise. it was so sad for me to know that no Binary literals are available inside this language 👎

hope from the bottom of my heart this will be add in the near future. 👍

@gafter gafter added this to the C# 7 and VB 15 milestone Aug 26, 2015
@DerpMcDerp
Copy link

The only precedent I know of is a toy C like language I wrote. It had more advanced things like repeating digits:

0x30{FE}30 produced 30FEFEFE...FEFEFE30

which was extremely useful for being able to specify the MSBs:

0b11{0}1 produced 110000...0001 for signed types and 11000...0001 for unsigned types
1b11{0}1 produced 011000...0001 for signed types and 11000...0001 for unsigned types

The way this was made to work was that there was no way to infer the type of an integer literal and compile time integer arithmetic required tricky case analysis to support various combinations. C# integer literals are required to be able to infer a type so none of the fancy things like the above can be supported, only the simple leading 1s feature can.

@ddevault
Copy link
Contributor

I'm going to give a 👎 to that, then. Seems complicated and doesn't have prior art to justify it.

@gafter gafter modified the milestones: 1.2, 2.0 (RTM) Mar 22, 2016
@giggio
Copy link

giggio commented Apr 18, 2016

This should be tagged New Language Feature - Binary Literals.

@shaggygi
Copy link
Contributor

I noticed comments above related to Binary Literal strings, but is it finalized (in another issue or design notes) how strings will work? For example, are there any examples how string interpolation would look like compared to hexadecimal... $"{value:X2}";

@svick
Copy link
Contributor

svick commented Aug 29, 2016

@shaggygi String interpolation is defined in terms of string.Format(), so I think that adding support for binary to numeric format strings is a question for corefx, not Roslyn.

@shaggygi
Copy link
Contributor

@svick Okay. I'll try to ping that repo and reference this topic.

@ghost
Copy link

ghost commented Aug 29, 2016

Regarding the octal, see dotnet/corefx#2993 (comment). F# and VB has octal cocktail, why can't C# has it? The number theory guys would be happy too. :)

@strohlaj
Copy link

strohlaj commented Nov 23, 2016

What would be the best way to represent negative numbers using the binary literal syntax?
The logical negation '~'? Is there a way to include the signed bit in the binary literal syntax like so:

int maxIntValue = 0b0_1111111_11111111_11111111_11111111; //int.MaxValue;
int two = 0b0_0000000_00000000_00000000_00000010; //2
int one = 0b0_0000000_00000000_00000000_00000001; //1
int zero = 0b0_0000000_00000000_00000000_00000000; //0
int negOne = 0b1_1111111_11111111_11111111_11111111; //-1
int negTwo = 0b1_1111111_11111111_11111111_11111110; //-2
int minIntValue = 0b1_0000000_000000000_00000000_00000000; //int.MinValue;

This might only work if we know what type the binary literal is being assigned.

In the current preview build the last 3 require a cast to uint thereby no longer being negative numbers. Perhaps I missed a better way to represent negative numbers using the binary literal syntax.

@CyrusNajmabadi
Copy link
Member

@strohlaj:

What about:

int negOne = - 0b1.

C# does not have negative literals. It has negative constant expressions. So this just fits with that.

@strohlaj
Copy link

I like it and that definitely works. My only real complaint (and this is strictly an opinion) is that it seems to hide the binary representation of the negative number.

@paulomorgado
Copy link

@strohlaj

Do you really usually care that 0b1_1111111_11111111_11111111_11111110 is decimal -2?

@GeirGrusom
Copy link

unchecked((sbyte)0b11111111)

@strohlaj
Copy link

@paulomorgado
I've tried thinking of a hypothetical situation where it would make more sense but nothing comes immediately to my mind. I will say that it's nice from an educational perspective. For students to visually see the negative integers in binary form as they actually are would be nice for teaching how ones compliment works.

@wedge905
Copy link

When dealing with hex or binary literals, it's wrong to think of them in terms of numbers. It's more accurate to think of them in terms of bits and bytes. They are just arbitrary data, which is meaningless by itself. The meaning is assigned to the data by the reader.
Example:
What number is this? 0b11110100
If you assume that those bits represent an 8-bit signed int, then it's -12
but if you assume an 8-bit unsigned int, then it's 244
even if you assume 16-bit signed, then it's still 244

It's all in how you read it, which is why @GeirGrusom's answer is the best, because that's exactly what he's doing there, changing how you read the number.

@gafter
Copy link
Member

gafter commented Mar 27, 2017

This has been implemented in C# 7.0. See also dotnet/csharplang#55

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests