mirror of
https://github.com/dart-lang/sdk
synced 2024-09-16 00:39:49 +00:00
[corelib] Fix truncation errors in int.modPow
Use BigInt arithmetic when intermediate products might overflow. Web versions range-check inputs to avoid working on truncated inputs. Bug: 37469 Change-Id: I27d2da2fff7901ce6dbfa5929c6998e87e8808e2 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/109360 Reviewed-by: Lasse R.H. Nielsen <lrn@google.com> Commit-Queue: Stephen Adams <sra@google.com>
This commit is contained in:
parent
f77093145a
commit
41d3971e83
|
@ -360,6 +360,14 @@ abstract class _IntegerImplementation implements int {
|
|||
if (e < 0) throw new RangeError.range(e, 0, null, "exponent");
|
||||
if (m <= 0) throw new RangeError.range(m, 1, null, "modulus");
|
||||
if (e == 0) return 1;
|
||||
|
||||
// This is floor(sqrt(2^63)).
|
||||
const int maxValueThatCanBeSquaredWithoutTruncation = 3037000499;
|
||||
if (m > maxValueThatCanBeSquaredWithoutTruncation) {
|
||||
// Use BigInt version to avoid truncation in multiplications below.
|
||||
return BigInt.from(this).modPow(BigInt.from(e), BigInt.from(m)).toInt();
|
||||
}
|
||||
|
||||
int b = this;
|
||||
if (b < 0 || b > m) {
|
||||
b %= m;
|
||||
|
|
|
@ -437,6 +437,30 @@ class JSNumber extends Interceptor implements int, double {
|
|||
if (e < 0) throw RangeError.range(e, 0, null, "exponent");
|
||||
if (m <= 0) throw RangeError.range(m, 1, null, "modulus");
|
||||
if (e == 0) return 1;
|
||||
|
||||
const int maxPreciseInteger = 9007199254740991;
|
||||
|
||||
// Reject inputs that are outside the range of integer values that can be
|
||||
// represented precisely as a Number (double).
|
||||
if (this < -maxPreciseInteger || this > maxPreciseInteger) {
|
||||
throw RangeError.range(
|
||||
this, -maxPreciseInteger, maxPreciseInteger, 'receiver');
|
||||
}
|
||||
if (e > maxPreciseInteger) {
|
||||
throw RangeError.range(e, 0, maxPreciseInteger, 'exponent');
|
||||
}
|
||||
if (m > maxPreciseInteger) {
|
||||
throw RangeError.range(e, 1, maxPreciseInteger, 'modulus');
|
||||
}
|
||||
|
||||
// This is floor(sqrt(maxPreciseInteger)).
|
||||
const int maxValueThatCanBeSquaredWithoutTruncation = 94906265;
|
||||
if (m > maxValueThatCanBeSquaredWithoutTruncation) {
|
||||
// Use BigInt version to avoid truncation in multiplications below. The
|
||||
// 'maxPreciseInteger' check on [m] ensures that toInt() does not round.
|
||||
return BigInt.from(this).modPow(BigInt.from(e), BigInt.from(m)).toInt();
|
||||
}
|
||||
|
||||
int b = this;
|
||||
if (b < 0 || b > m) {
|
||||
b %= m;
|
||||
|
|
|
@ -503,14 +503,38 @@ class JSInt extends JSNumber implements int {
|
|||
// Returns pow(this, e) % m.
|
||||
int modPow(int e, int m) {
|
||||
if (e is! int) {
|
||||
throw new ArgumentError.value(e, 'exponent', 'not an integer');
|
||||
throw ArgumentError.value(e, 'exponent', 'not an integer');
|
||||
}
|
||||
if (m is! int) {
|
||||
throw new ArgumentError.value(m, 'modulus', 'not an integer');
|
||||
throw ArgumentError.value(m, 'modulus', 'not an integer');
|
||||
}
|
||||
if (e < 0) throw new RangeError.range(e, 0, null, 'exponent');
|
||||
if (m <= 0) throw new RangeError.range(m, 1, null, 'modulus');
|
||||
if (e < 0) throw RangeError.range(e, 0, null, 'exponent');
|
||||
if (m <= 0) throw RangeError.range(m, 1, null, 'modulus');
|
||||
if (e == 0) return 1;
|
||||
|
||||
const int maxPreciseInteger = 9007199254740991;
|
||||
|
||||
// Reject inputs that are outside the range of integer values that can be
|
||||
// represented precisely as a Number (double).
|
||||
if (this < -maxPreciseInteger || this > maxPreciseInteger) {
|
||||
throw RangeError.range(
|
||||
this, -maxPreciseInteger, maxPreciseInteger, 'receiver');
|
||||
}
|
||||
if (e > maxPreciseInteger) {
|
||||
throw RangeError.range(e, 0, maxPreciseInteger, 'exponent');
|
||||
}
|
||||
if (m > maxPreciseInteger) {
|
||||
throw RangeError.range(e, 1, maxPreciseInteger, 'modulus');
|
||||
}
|
||||
|
||||
// This is floor(sqrt(maxPreciseInteger)).
|
||||
const int maxValueThatCanBeSquaredWithoutTruncation = 94906265;
|
||||
if (m > maxValueThatCanBeSquaredWithoutTruncation) {
|
||||
// Use BigInt version to avoid truncation in multiplications below. The
|
||||
// 'maxPreciseInteger' check on [m] ensures that toInt() does not round.
|
||||
return BigInt.from(this).modPow(BigInt.from(e), BigInt.from(m)).toInt();
|
||||
}
|
||||
|
||||
int b = this;
|
||||
if (b < 0 || b > m) {
|
||||
b %= m;
|
||||
|
|
Loading…
Reference in a new issue