Skip to content

Commit cbb15c3

Browse files
rakudramaCommit Bot
authored and
Commit Bot
committed
[corelib] Fix bug in VM implementation of BigInt.bitLength
Change-Id: I8664013e27c9c43cd71dd9a271c2e61404d37feb Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/244764 Commit-Queue: Stephen Adams <[email protected]> Reviewed-by: Lasse Nielsen <[email protected]>
1 parent c038e3b commit cbb15c3

File tree

4 files changed

+202
-5
lines changed

4 files changed

+202
-5
lines changed

sdk/lib/_internal/js_runtime/lib/core_patch.dart

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2174,8 +2174,24 @@ class _BigIntImpl implements BigInt {
21742174
/// ```
21752175
int get bitLength {
21762176
if (_used == 0) return 0;
2177-
if (_isNegative) return (~this).bitLength;
2178-
return _digitBits * (_used - 1) + _digits[_used - 1].bitLength;
2177+
final highBits = _digits[_used - 1];
2178+
assert(highBits != 0);
2179+
int length = _digitBits * (_used - 1) + highBits.bitLength;
2180+
if (!_isNegative) return length;
2181+
2182+
// `this` is negative, i.e. `-x` for the magnitude `x`. We want to find the
2183+
// bit length of `~this` or equivalently `-this-1`.
2184+
//
2185+
// -this-1 == -(-x)-1 == x-1
2186+
//
2187+
// `x-1` will have the same bit length as `x` unless `x` is power of two
2188+
// (e.g. 0x1000-1 = 0x0FFF). The magnitude is a power of two if the high
2189+
// digit is a power of two and all the other digits are zero.
2190+
if (highBits & (highBits - 1) != 0) return length;
2191+
for (int i = _used - 2; i >= 0; i--) {
2192+
if (_digits[i] != 0) return length;
2193+
}
2194+
return length - 1;
21792195
}
21802196

21812197
/// Truncating division operator.

sdk/lib/_internal/vm/lib/bigint_patch.dart

Lines changed: 18 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1657,9 +1657,24 @@ class _BigIntImpl implements BigInt {
16571657
*/
16581658
int get bitLength {
16591659
if (_used == 0) return 0;
1660-
var highBits = _digits[_used - 1];
1661-
if (_isNegative) highBits -= 1;
1662-
return _digitBits * (_used - 1) + highBits.bitLength;
1660+
final highBits = _digits[_used - 1];
1661+
assert(highBits != 0);
1662+
int length = _digitBits * (_used - 1) + highBits.bitLength;
1663+
if (!_isNegative) return length;
1664+
1665+
// `this` is negative, i.e. `-x` for the magnitude `x`. We want to find the
1666+
// bit length of `~this` or equivalently `-this-1`.
1667+
//
1668+
// -this-1 == -(-x)-1 == x-1
1669+
//
1670+
// `x-1` will have the same bit length as `x` unless `x` is power of two
1671+
// (e.g. 0x1000-1 = 0x0FFF). The magnitude is a power of two if the high
1672+
// digit is a power of two and all the other digits are zero.
1673+
if (highBits & (highBits - 1) != 0) return length;
1674+
for (int i = _used - 2; i >= 0; i--) {
1675+
if (_digits[i] != 0) return length;
1676+
}
1677+
return length - 1;
16631678
}
16641679

16651680
/**
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Testing Bigints with and without intrinsics.
6+
// VMOptions=--intrinsify --no-enable-asserts
7+
// VMOptions=--intrinsify --enable-asserts
8+
// VMOptions=--no-intrinsify --enable-asserts
9+
// VMOptions=--no-intrinsify --no-enable-asserts
10+
11+
import "package:expect/expect.dart";
12+
13+
const debugPrint = bool.fromEnvironment('debugPrint');
14+
15+
void check(int length, BigInt base) {
16+
assert(length >= 5);
17+
assert(base > BigInt.zero);
18+
19+
// Check with slight adjustments. We choose -3..+3 so that the lowest bit in
20+
// the 2's-complement representation is both zero and one for both the postive
21+
// [n] and its negative complement [m] below.
22+
for (int delta = -3; delta <= 3; delta++) {
23+
BigInt n = base + BigInt.from(delta);
24+
assert(n >= BigInt.zero);
25+
26+
// Compute the bitLength by shifting the value into a small integer range
27+
// and adjust the `int.bitLength` value by the shift count.
28+
int shiftCount = length - 5;
29+
int shiftedN = (n >> shiftCount).toInt();
30+
int expectedLength = shiftCount + shiftedN.bitLength;
31+
32+
int nLength = n.bitLength;
33+
Expect.equals(expectedLength, nLength);
34+
35+
// Use identity `x.bitLength == (-x-1).bitLength` to check negative values.
36+
BigInt m = -n - BigInt.one;
37+
int mLength = m.bitLength;
38+
39+
if (debugPrint) {
40+
final printLength = length + 4;
41+
final nDigits =
42+
n.toUnsigned(printLength).toRadixString(2).padLeft(printLength);
43+
final mDigits = m.toUnsigned(printLength).toRadixString(2);
44+
print('$nDigits: $nLength');
45+
print('$mDigits: $mLength');
46+
}
47+
48+
Expect.equals(nLength, mLength);
49+
}
50+
}
51+
52+
void main() {
53+
// For small values, [BigInt.bitLength] should be the same as [int.bitLength].
54+
for (int i = 0; i <= 64; i++) {
55+
Expect.equals(i.bitLength, BigInt.from(i).bitLength);
56+
// Note: This is not quite redundant for `i==0` since on the web platform
57+
// `-i` is negative zero and not the same as `0-i`.
58+
Expect.equals((-i).bitLength, BigInt.from(-i).bitLength);
59+
}
60+
61+
// Test x.bitLength for a large variety of lengths.
62+
for (int length = 5; length <= 512; length++) {
63+
BigInt base = BigInt.one << (length - 1);
64+
Expect.equals(length, base.bitLength);
65+
66+
// Power of two.
67+
check(length, base);
68+
69+
// Two high bits set.
70+
check(length, base | base >> 1);
71+
72+
// Check for values with an additional bit set near a potential internal
73+
// digit boundary.
74+
for (int i1 = 16; i1 < length; i1 += 16) {
75+
for (int i2 = -1; i2 <= 1; i2++) {
76+
int i = i1 + i2;
77+
if (i < length - 1) {
78+
check(length, base | BigInt.one << (i - 1));
79+
}
80+
}
81+
}
82+
}
83+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
// Copyright (c) 2022, the Dart project authors. Please see the AUTHORS file
2+
// for details. All rights reserved. Use of this source code is governed by a
3+
// BSD-style license that can be found in the LICENSE file.
4+
5+
// Testing Bigints with and without intrinsics.
6+
// VMOptions=--intrinsify --no-enable-asserts
7+
// VMOptions=--intrinsify --enable-asserts
8+
// VMOptions=--no-intrinsify --enable-asserts
9+
// VMOptions=--no-intrinsify --no-enable-asserts
10+
11+
import "package:expect/expect.dart";
12+
13+
const debugPrint = bool.fromEnvironment('debugPrint');
14+
15+
void check(int length, BigInt base) {
16+
assert(length >= 5);
17+
assert(base > BigInt.zero);
18+
19+
// Check with slight adjustments. We choose -3..+3 so that the lowest bit in
20+
// the 2's-complement representation is both zero and one for both the postive
21+
// [n] and its negative complement [m] below.
22+
for (int delta = -3; delta <= 3; delta++) {
23+
BigInt n = base + BigInt.from(delta);
24+
assert(n >= BigInt.zero);
25+
26+
// Compute the bitLength by shifting the value into a small integer range
27+
// and adjust the `int.bitLength` value by the shift count.
28+
int shiftCount = length - 5;
29+
int shiftedN = (n >> shiftCount).toInt();
30+
int expectedLength = shiftCount + shiftedN.bitLength;
31+
32+
int nLength = n.bitLength;
33+
Expect.equals(expectedLength, nLength);
34+
35+
// Use identity `x.bitLength == (-x-1).bitLength` to check negative values.
36+
BigInt m = -n - BigInt.one;
37+
int mLength = m.bitLength;
38+
39+
if (debugPrint) {
40+
final printLength = length + 4;
41+
final nDigits =
42+
n.toUnsigned(printLength).toRadixString(2).padLeft(printLength);
43+
final mDigits = m.toUnsigned(printLength).toRadixString(2);
44+
print('$nDigits: $nLength');
45+
print('$mDigits: $mLength');
46+
}
47+
48+
Expect.equals(nLength, mLength);
49+
}
50+
}
51+
52+
void main() {
53+
// For small values, [BigInt.bitLength] should be the same as [int.bitLength].
54+
for (int i = 0; i <= 64; i++) {
55+
Expect.equals(i.bitLength, BigInt.from(i).bitLength);
56+
// Note: This is not quite redundant for `i==0` since on the web platform
57+
// `-i` is negative zero and not the same as `0-i`.
58+
Expect.equals((-i).bitLength, BigInt.from(-i).bitLength);
59+
}
60+
61+
// Test x.bitLength for a large variety of lengths.
62+
for (int length = 5; length <= 512; length++) {
63+
BigInt base = BigInt.one << (length - 1);
64+
Expect.equals(length, base.bitLength);
65+
66+
// Power of two.
67+
check(length, base);
68+
69+
// Two high bits set.
70+
check(length, base | base >> 1);
71+
72+
// Check for values with an additional bit set near a potential internal
73+
// digit boundary.
74+
for (int i1 = 16; i1 < length; i1 += 16) {
75+
for (int i2 = -1; i2 <= 1; i2++) {
76+
int i = i1 + i2;
77+
if (i < length - 1) {
78+
check(length, base | BigInt.one << (i - 1));
79+
}
80+
}
81+
}
82+
}
83+
}

0 commit comments

Comments
 (0)