-
Notifications
You must be signed in to change notification settings - Fork 131
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
Optimise binary arithmetic operators for powers of 10 #23
Conversation
Codecov Report
@@ Coverage Diff @@
## master #23 +/- ##
=====================================
Coverage 100% 100%
=====================================
Files 13 13
Lines 517 536 +19
=====================================
+ Hits 517 536 +19
Continue to review full report at Codecov.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for optimising BigInt! Following are some suggestions to make it even better:
include/functions/utility.hpp
Outdated
Checks whether a string-represented integer is a power of 10. | ||
*/ | ||
|
||
bool is_power_of_10(std::string num){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Accept string argument as a constant reference (const std::string& num
).
include/functions/utility.hpp
Outdated
if(num == "1" or num == "10") return 1; | ||
if (num.front() != '1') return 0; | ||
while( num.back() == '0'){ | ||
num.pop_back(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need to modify the string by popping characters. First check that the first digit is '1'
, then just traverse over the other digits, and if a digit is not found to be '0'
, return false
. At the end (outside the loop) return true
.
include/functions/utility.hpp
Outdated
Checks whether a string-represented integer is a power of 10. | ||
*/ | ||
|
||
size_t get_power_of_10(std::string num){ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This function is not needed. See the following comments for alternatives.
@@ -156,6 +156,14 @@ BigInt BigInt::operator*(const BigInt& num) const { | |||
BigInt product; | |||
if (abs(*this) <= FLOOR_SQRT_LLONG_MAX and abs(num) <= FLOOR_SQRT_LLONG_MAX) | |||
product = std::stoll(this->value) * std::stoll(num.value); | |||
else if (is_power_of_10(this->value)){ // if LHS is a power of 10 do optimised operation | |||
product = num; | |||
add_trailing_zeroes(product.value, get_power_of_10(this->value)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Instead of adding trailing zeroes equal to the power of 10, use the following (it's much more efficient):
product.value.append(this->value.begin() + 1, this->value.end());
@@ -156,6 +156,14 @@ BigInt BigInt::operator*(const BigInt& num) const { | |||
BigInt product; | |||
if (abs(*this) <= FLOOR_SQRT_LLONG_MAX and abs(num) <= FLOOR_SQRT_LLONG_MAX) | |||
product = std::stoll(this->value) * std::stoll(num.value); | |||
else if (is_power_of_10(this->value)){ // if LHS is a power of 10 do optimised operation | |||
product = num; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following will be much faster:
product.value = num.value;
(product.sign
is taken care of before returning).
@@ -314,6 +325,9 @@ BigInt BigInt::operator%(const BigInt& num) const { | |||
remainder = std::stoll(abs_dividend.value) % std::stoll(abs_divisor.value); | |||
else if (abs_dividend < abs_divisor) | |||
remainder = abs_dividend; | |||
else if (is_power_of_10(num.value)){ // if num is a power of 10 use optimised calculation | |||
remainder = abs_dividend.value.substr(abs_dividend.value.size() - get_power_of_10(num.value)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assign to remainder.value
instead of remainder
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also, as done for division, no need to use get_power_of_10()
here.
} | ||
else if (is_power_of_10(num.value)){ | ||
product = *this; | ||
add_trailing_zeroes(product.value, get_power_of_10(num.value)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Similar changes need to be made here as mentioned above.
@@ -253,6 +261,9 @@ BigInt BigInt::operator/(const BigInt& num) const { | |||
quotient = std::stoll(abs_dividend.value) / std::stoll(abs_divisor.value); | |||
else if (abs_dividend == abs_divisor) | |||
quotient = 1; | |||
else if (is_power_of_10(abs_divisor.value)) { // if divisor is a power of 10 do optimised calculation | |||
quotient.value = abs_dividend.value.substr(0, abs_dividend.value.size() - get_power_of_10(abs_divisor.value)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No need of using get_power_of_10()
here. Instead, you can do:
size_t digits_in_quotient = abs_dividend.value.size() - abs_divisor.value.size() + 1;
quotient.value = abs_dividend.value.substr(0, digits_in_quotient);
I made the changes |
Travis CI seems to have failed from network problems on their part. So I guess it's all good... fixed |
include/functions/utility.hpp
Outdated
if (num == "1") return true; | ||
if (num.front() == '1'){ | ||
for (char c : num.substr(1)){ | ||
if (c != '0') return false; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The following is much simpler and more efficient:
if (num[0] != '1')
return false;
for (size_t i = 1; i < num.size(); i++)
if (num[i] != '0')
return false;
return true; // first digit is 1 and the following digits are all 0
Here's why:
- it has less branching
- has less number of
return
statements - doesn't create a substring, which means it doesn't take up extra memory or the extra time to create the substring
|
||
num1 = big_pow10(19876) + 1; | ||
num2 = big_pow10(23450); | ||
REQUIRE(num1 * num2 == big_pow10(43326) + big_pow10(23450)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This test seems confusing at first glance. We can have tests like this when +
or -
operators are optimised. For now, you may remove it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So should I leave it without a test for this case?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can remove these 4 lines. The code coverage won't drop as tests with powers of 10 already exist.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Coverage does drop without the last commit.
There are tests multiplying powers of 10 but then it uses only the LHS(with *this ). So the RHS(with num ) is unused and coverage drops to 99.1%.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You're right. Let it be then.
All done. |
@MagmaCode Thank you for contributing, and congratulations on your first open source contribution! 🥇 |
I gave a try to optimising the binary operations with powers of 10. #18
Hope it is good enough. First open source contribution. 👍