Skip to content

Commit 5973d9e

Browse files
authored
Implement SIP-42 Support for binary integer literals (#20246)
Closes #19695 Cherry-picked without changes from reverted PR #19405
2 parents 05114dd + 49dfd52 commit 5973d9e

File tree

7 files changed

+105
-3
lines changed

7 files changed

+105
-3
lines changed

compiler/src/dotty/tools/dotc/parsing/Scanners.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -884,7 +884,7 @@ object Scanners {
884884
nextChar()
885885
ch match {
886886
case 'x' | 'X' => base = 16 ; nextChar()
887-
//case 'b' | 'B' => base = 2 ; nextChar()
887+
case 'b' | 'B' => base = 2 ; nextChar()
888888
case _ => base = 10 ; putChar('0')
889889
}
890890
if (base != 10 && !isNumberSeparator(ch) && digit2int(ch, base) < 0)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
---
2+
layout: doc-page
3+
title: "Binary Integer Literals"
4+
nightlyOf: https://docs.scala-lang.org/scala3/reference/changed-features/binary-integer-literals.html
5+
---
6+
7+
A new syntax for integer literals has been added, it is now possible to do the following:
8+
```scala
9+
val bitmask = 0b0010_0000 // equivalent to 32, 0x20
10+
```
11+
12+
Binary integer literals behave similarly to hex integer literals (`0x...`), for example:
13+
* Both `0b...` and `0B...` are allowed
14+
* `0b`/`0B` on its own is disallowed, possible alternatives: `0`, `0b0`, `0B0`
15+
* Only `0` and `1` are allowed after the b (`b`/`B`)
16+
* Underscores `_` are allowed anywhere between digits, and are ignored: `0b__1 == 0b1`
17+
18+
19+
Note: This change has been backported to Scala 2.13.13, it is therefore not technically a changed feature

docs/_spec/01-lexical-syntax.md

+3-2
Original file line numberDiff line numberDiff line change
@@ -332,9 +332,10 @@ Literal ::= [‘-’] integerLiteral
332332
### Integer Literals
333333

334334
```ebnf
335-
integerLiteral ::= (decimalNumeral | hexNumeral) [‘L’ | ‘l’]
335+
integerLiteral ::= (decimalNumeral | hexNumeral | binaryNumeral) [‘L’ | ‘l’]
336336
decimalNumeral ::= ‘0’ | digit [{digit | ‘_’} digit]
337337
hexNumeral ::= ‘0’ (‘x’ | ‘X’) hexDigit [{hexDigit | ‘_’} hexDigit]
338+
binaryNumeral ::= ‘0’ (‘b’ | ‘B’) binaryDigit [{binaryDigit | ‘_’} binaryDigit]
338339
```
339340

340341
Values of type `Int` are all integer numbers between $-2\^{31}$ and $2\^{31}-1$, inclusive.
@@ -357,7 +358,7 @@ The numeric ranges given by these types are:
357358
The digits of a numeric literal may be separated by arbitrarily many underscores for purposes of legibility.
358359

359360
> ```scala
360-
> 0 21_000 0x7F -42L 0xFFFF_FFFF
361+
> 0 21_000 0x7F -42L 0xFFFF_FFFF 0b0100_0010
361362
> ```
362363
363364
### Floating Point Literals

docs/sidebar.yml

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ subsection:
8181
- page: reference/other-new-features/safe-initialization.md
8282
- page: reference/other-new-features/type-test.md
8383
- page: reference/other-new-features/experimental-defs.md
84+
- page: reference/other-new-features/binary-literals.md
8485
- title: Other Changed Features
8586
directory: changed-features
8687
index: reference/changed-features/changed-features.md

project/resources/referenceReplacements/sidebar.yml

+1
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ subsection:
7777
- page: reference/other-new-features/safe-initialization.md
7878
- page: reference/other-new-features/type-test.md
7979
- page: reference/other-new-features/experimental-defs.md
80+
- page: reference/other-new-features/binary-literals.md
8081
- title: Other Changed Features
8182
directory: changed-features
8283
index: reference/changed-features/changed-features.md

tests/neg/binaryLiterals.scala

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
object Test:
3+
val x = 0b1__0000_0000_0000_0000__0000_0000_0000_0000 // error: number too large
4+
val X = 0B1__0000_0000_0000_0000__0000_0000_0000_0000 // error: number too large
5+
val y = 0b1__0000_0000_0000_0000__0000_0000_0000_0000__0000_0000_0000_0000__0000_0000_0000_0000L // error: number too large
6+
val Y = 0B1__0000_0000_0000_0000__0000_0000_0000_0000__0000_0000_0000_0000__0000_0000_0000_0000L // error: number too large
7+
0b // error: invalid literal number
8+
0b2 // error: invalid literal number

tests/run/binaryLiterals.scala

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
@main
2+
def Test =
3+
val kenobi = 0b1
4+
5+
assert(kenobi == 1)
6+
7+
assert(0B0000 == 0)
8+
assert(0B0001 == 1)
9+
assert(0B0010 == 2)
10+
assert(0B0100 == 4)
11+
assert(0B1000 == 8)
12+
13+
assert(0b0000 == 0)
14+
assert(0b0001 == 1)
15+
assert(0b0010 == 2)
16+
assert(0b0100 == 4)
17+
assert(0b1000 == 8)
18+
19+
assert(0b0001_0000 == 16)
20+
assert(0b0010_0000 == 32)
21+
assert(0b0100_0000 == 64)
22+
assert(0b1000_0000 == 128)
23+
24+
assert(0b0001_0000_0000 == 256)
25+
assert(0b0010_0000_0000 == 512)
26+
assert(0b0100_0000_0000 == 1024)
27+
assert(0b1000_0000_0000 == 2048)
28+
29+
assert(0b0001_0000_0000_0000 == 4096)
30+
assert(0b0010_0000_0000_0000 == 8192)
31+
assert(0b0100_0000_0000_0000 == 16384)
32+
assert(0b1000_0000_0000_0000 == 32768)
33+
34+
assert(0b0001__0000_0000_0000_0000 == 65536)
35+
assert(0b0010__0000_0000_0000_0000 == 131072)
36+
assert(0b0100__0000_0000_0000_0000 == 262144)
37+
assert(0b1000__0000_0000_0000_0000 == 524288)
38+
39+
assert(0b0001_0000__0000_0000_0000_0000 == 1048576)
40+
assert(0b0010_0000__0000_0000_0000_0000 == 2097152)
41+
assert(0b0100_0000__0000_0000_0000_0000 == 4194304)
42+
assert(0b1000_0000__0000_0000_0000_0000 == 8388608)
43+
44+
assert(0b0001_0000_0000__0000_0000_0000_0000 == 16777216)
45+
assert(0b0010_0000_0000__0000_0000_0000_0000 == 33554432)
46+
assert(0b0100_0000_0000__0000_0000_0000_0000 == 67108864)
47+
assert(0b1000_0000_0000__0000_0000_0000_0000 == 134217728)
48+
49+
assert(0b0001_0000_0000_0000__0000_0000_0000_0000 == 268435456)
50+
assert(0b0010_0000_0000_0000__0000_0000_0000_0000 == 536870912)
51+
assert(0b0100_0000_0000_0000__0000_0000_0000_0000 == 1073741824)
52+
assert(0b1000_0000_0000_0000__0000_0000_0000_0000L == 2147483648L)
53+
54+
assert(0b1000_0000_0000_0000__0000_0000_0000_0000 == -2147483648) // Signed !
55+
assert(0b1111_1111_1111_1111__1111_1111_1111_1111 == -1)
56+
57+
// Randomly generated using https://numbergenerator.org/random-32-bit-binary-number#!numbers=10&length=32&addfilters=
58+
// Converted to signed decimal using https://onlinetoolz.net/unsigned-signed#base=2&bits=32
59+
assert(0b0110_1000_1100_0101_0010_1100_0100_0011 == 1757752387)
60+
assert(0b1111_0101_0100_1011_0101_1000_0011_0110 == -179611594)
61+
assert(0b0000_0011_0000_1010_1010_0011_0000_0000 == 51028736)
62+
assert(0b0101_0010_1111_1001_0100_0101_1101_1011 == 1392068059)
63+
assert(0b1001_0000_1111_1001_1011_1101_1100_1111 == -1862681137)
64+
65+
assert(0B0000_0111_1110_1100_0111_1100_1000_0010 == 132938882)
66+
assert(0B0000_1011_0111_1011_0001_1010_1010_1000 == 192617128)
67+
assert(0B1100_1100_1000_1010_1111_0111_0100_1101 == -863307955)
68+
assert(0B1000_0000_0001_0010_0001_1001_0101_1110 == -2146297506)
69+
assert(0B1110_0000_0110_1100_0111_0110_1100_1111 == -529762609)
70+
71+
assert(0b0010_1001_0101_1001__1010_0100_1000_1010__1001_1000_0011_0111__1100_1011_0111_0101L == 2979593543648529269L)
72+
assert(0b1101_1110_0100_1000__0010_1101_1010_0010__0111_1000_1111_1001__1010_1001_0101_1000L == -2429641823128802984L)

0 commit comments

Comments
 (0)