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

Optimise binary arithmetic operators for powers of 10 #23

Merged
merged 8 commits into from
Feb 8, 2018

Conversation

MagmaCode
Copy link
Contributor

I gave a try to optimising the binary operations with powers of 10. #18
Hope it is good enough. First open source contribution. 👍

@codecov
Copy link

codecov bot commented Feb 5, 2018

Codecov Report

Merging #23 into master will not change coverage.
The diff coverage is 100%.

Impacted file tree graph

@@          Coverage Diff          @@
##           master    #23   +/-   ##
=====================================
  Coverage     100%   100%           
=====================================
  Files          13     13           
  Lines         517    536   +19     
=====================================
+ Hits          517    536   +19
Impacted Files Coverage Δ
include/operators/binary_arithmetic.hpp 100% <100%> (ø) ⬆️
include/functions/utility.hpp 100% <100%> (ø) ⬆️

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 00f3910...62e2e67. Read the comment docs.

Copy link
Owner

@faheel faheel left a 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:

Checks whether a string-represented integer is a power of 10.
*/

bool is_power_of_10(std::string num){
Copy link
Owner

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).

if(num == "1" or num == "10") return 1;
if (num.front() != '1') return 0;
while( num.back() == '0'){
num.pop_back();
Copy link
Owner

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.

Checks whether a string-represented integer is a power of 10.
*/

size_t get_power_of_10(std::string num){
Copy link
Owner

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));
Copy link
Owner

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;
Copy link
Owner

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));
Copy link
Owner

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.

Copy link
Owner

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));
Copy link
Owner

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));
Copy link
Owner

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);

@faheel faheel changed the title Optimised Powers of 10. Optimise binary arithmetic operators for powers of 10 Feb 6, 2018
@faheel faheel added enhancement New feature or request operators: binary arithmetic Binary arithmetic operators labels Feb 6, 2018
@MagmaCode
Copy link
Contributor Author

I made the changes

@MagmaCode
Copy link
Contributor Author

MagmaCode commented Feb 6, 2018

Travis CI seems to have failed from network problems on their part. So I guess it's all good... fixed

@MagmaCode MagmaCode closed this Feb 6, 2018
@MagmaCode MagmaCode reopened this Feb 6, 2018
if (num == "1") return true;
if (num.front() == '1'){
for (char c : num.substr(1)){
if (c != '0') return false;
Copy link
Owner

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));
Copy link
Owner

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.

Copy link
Contributor Author

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?

Copy link
Owner

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.

Copy link
Contributor Author

@MagmaCode MagmaCode Feb 7, 2018

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%.

Copy link
Owner

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.

@MagmaCode
Copy link
Contributor Author

All done.

@faheel faheel merged commit 7bc46cd into faheel:master Feb 8, 2018
@faheel
Copy link
Owner

faheel commented Feb 8, 2018

@MagmaCode Thank you for contributing, and congratulations on your first open source contribution! 🥇

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request operators: binary arithmetic Binary arithmetic operators
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants