Skip to content

Commit

Permalink
Fixed a number of bugs in division.cpp and multiply.cpp
Browse files Browse the repository at this point in the history
- Fixed incorrect results by testing the results
- Fixed a wrong test case in testDivision.cpp
- Fixed a problem that caused multiply.cpp unable to work with decimals
correctly

Signed-off-by: Andy Zhang <[email protected]>
  • Loading branch information
ZCG-coder committed Feb 4, 2024
1 parent e9de495 commit c1e62f1
Show file tree
Hide file tree
Showing 14 changed files with 187 additions and 45 deletions.
6 changes: 3 additions & 3 deletions .clang-tidy
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
---
Checks: 'clang-diagnostic-*,clang-analyzer-*,modernize-*,-*'
Checks: 'abseil-*,altera-*,bugprone-*,concurrency-*,cppcoreguidelines-*,darwin-*,fuchsia-*,readability-*,performance-*,portability-*,zircon-*,hicpp-*,google-*,cert-*,clang-diagnostic-*,clang-analyzer-*,modernize-*,-*'
WarningsAsErrors: ''
HeaderFilterRegex: ''
AnalyzeTemporaryDtors: false
FormatStyle: Microsoft
User: coder
FormatStyle: Microsoft
User: coder
CheckOptions:
llvm-else-after-return.WarnOnConditionVariables: 'false'
modernize-loop-convert.MinConfidence: reasonable
Expand Down
4 changes: 4 additions & 0 deletions .idea/inspectionProfiles/Project_Default.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

17 changes: 17 additions & 0 deletions .vscode/c_cpp_properties.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"configurations": [
{
"name": "Linux",
"includePath": [
"/**"
],
"defines": [],
"compilerPath": "/usr/bin/clang",
"cStandard": "c17",
"cppStandard": "c++23",
"intelliSenseMode": "linux-clang-arm64",
"configurationProvider": "ms-vscode.cmake-tools"
}
],
"version": 4
}
4 changes: 2 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ function(capitalize IN OUT)
${CAPITALIZED}
PARENT_SCOPE)
endfunction()
set(COMPONENTS abs add subtract multiply decimalConvert comparison power division)
set(COMPONENTS abs add baseConvert subtract multiply decimalConvert comparison power division)
set(TARGETS ${COMPONENTS} util)
set(TEST_TARGETS_TEMP util ${COMPONENTS})

Expand Down Expand Up @@ -93,4 +93,4 @@ if (DEBUG)
WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
COMMENT "Adding header comments"
)
endif ()
endif ()
9 changes: 9 additions & 0 deletions include/fn/basicArithm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,15 @@ std::string decimalConvert(const std::string_view& _inputString, const std::stri
*/
std::string divide(const std::string_view& number, const std::string_view& divisor, int steps = 2, int decimals = 5);

/**
* Calculates the quotient and remainder of dividing the current remainder by the divisor.
*
* @param _currentRemainder The current remainder.
* @param divisor The divisor.
* @return A QuotientRemainder object containing the quotient and remainder.
*/
QuotientRemainder getQuotientRemainder(const auto& _currentRemainder, const auto& divisor);

/**
* @brief Multiplies two string representations of numbers.
*
Expand Down
4 changes: 3 additions & 1 deletion include/internals.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,14 @@

#pragma once

#include <iostream>

namespace internals
{
struct setw
{
unsigned long n;
setw(unsigned long n) : n(n) {}
explicit setw(const unsigned long n) : n(n) {}

friend std::ostream& operator<<(std::ostream& os, const setw& cs)
{
Expand Down
20 changes: 20 additions & 0 deletions include/util.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -381,6 +381,22 @@ auto removeLeadingZeros(const std::vector<int>& vector) -> std::decay_t<decltype
*/
auto removeLeadingZeros(const std::string& string) -> std::decay_t<decltype(string)>;

/**
* @brief Joins a vector of elements into a single string using a delimiter.
*
* @tparam T The type of the vector elements.
* @param vector The vector of strings to join.
* @param delimiter The delimiter to join the strings with.
* @return The joined string.
*/
template<typename T>
auto join(const std::vector<T>& vector, const std::string& delimiter)
{
std::stringstream result;
for (const auto& item : vector)
result << item << delimiter;
return result.str();
}
/**
* @brief Makes the given string wider by adding 2 spaces between each character.
*
Expand All @@ -407,3 +423,7 @@ std::string makeWider(const std::string& orig);
* @see Utf8CodePage
*/
std::string unicodeToUtf8(int unicode);

auto removeTrailingZeros(const std::vector<int>& _vector) -> std::decay_t<decltype(_vector)>;

auto removeTrailingZeros(const std::string& numStr) -> std::decay_t<decltype(numStr)>;
23 changes: 14 additions & 9 deletions src/comparison/comparison.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,18 @@

std::string compare(const std::string_view& a, const std::string_view& b, const int steps)
{
const auto splitNumberResult = splitNumber(a, b, false, true);
const auto result = splitNumberResult.splitNumberArray;
const bool aIsNegative = splitNumberResult.aIsNegative, bIsNegative = splitNumberResult.bIsNegative,
bothNegative = aIsNegative and bIsNegative;
const auto [splitNumberArray, aIsNegative, bIsNegative] = splitNumber(a, b, false, true);
const auto result = splitNumberArray;
const bool bothNegative = aIsNegative and bIsNegative;
const auto& aIntegerReal = result[0];
if (const auto& bIntegerReal = result[2]; aIntegerReal.length() != bIntegerReal.length())
if (const auto& bIntegerReal = result[2];
// a is longer than b and is of different polarities
aIntegerReal.length() != bIntegerReal.length() and aIsNegative == bIsNegative)
{
if (bothNegative)
return reportComparisonAtInteger(a, b, aIntegerReal.length() < bIntegerReal.length(), steps);
return reportComparisonAtInteger(a, b, aIntegerReal.length() > bIntegerReal.length(), steps);
}

// Here, we try to determine whether a or b is greater based on their polarities
// Scenario 1: a and b are both positive
Expand All @@ -53,7 +58,7 @@ std::string compare(const std::string_view& a, const std::string_view& b, const
// Action : Return a < b
if (not aIsNegative and bIsNegative)
return reportComparisonByPolarity(a, b, true, steps);
else if (aIsNegative and not bIsNegative)
if (aIsNegative and not bIsNegative)
return reportComparisonByPolarity(a, b, false, steps);

for (long i = 0; static_cast<size_t>(i) < a.length(); i++)
Expand All @@ -62,11 +67,11 @@ std::string compare(const std::string_view& a, const std::string_view& b, const
continue; // Negative sign, decimal point or equals
if (a[i] > b[i] and not bothNegative)
return reportComparisonByDigit(a, b, i, true, false, steps);
else if (a[i] < b[i] and not bothNegative)
if (a[i] < b[i] and not bothNegative)
return reportComparisonByDigit(a, b, i, false, false, steps);
else if (a[i] > b[i] and bothNegative) // First digit is the negative sign
if (a[i] > b[i] and bothNegative) // First digit is the negative sign
return reportComparisonByDigit(a, b, i - 1, false, false, steps);
else if (a[i] < b[i] and bothNegative)
if (a[i] < b[i] and bothNegative)
return reportComparisonByDigit(a, b, i - 1, true, false, steps);
}

Expand Down
71 changes: 57 additions & 14 deletions src/division/division.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ QuotientRemainder getQuotientRemainder(const auto& _currentRemainder, const auto
return { "1", "0" }; // Equal

int out = 0;
while (compare(currentRemainder, divisor, 0) == "1" or compare(currentRemainder, divisor, 0) == "2")
while (compare(currentRemainder, divisor, 0) != "0")
{
out++;
currentRemainder = subtract(currentRemainder, divisor, 0);
Expand Down Expand Up @@ -74,7 +74,7 @@ std::string divide(const std::string_view& _number,
auto splitNumberResult = splitNumber(_number, _divisor, false, true);
bool numberIsNegative = splitNumberResult.aIsNegative, divisorIsNegative = splitNumberResult.bIsNegative;
auto [numberInteger, numberDecimal, divisorInteger, divisorDecimal] = splitNumberResult.splitNumberArray;
auto numberIntegerOrig = numberInteger, divisorIntegerOrig = divisorInteger;
auto numberIntegerOrig = numberInteger, divisorIntegerOrig = divisorInteger, numberDecimalOrig = numberDecimal;
auto decimals = _decimals;

if (numberIntegerOrig.empty())
Expand Down Expand Up @@ -142,46 +142,89 @@ std::string divide(const std::string_view& _number,
// - Length of integer in output = Length of integers in number - Length of integers in divisor (*)
// - Length of decimals in output = Length of output - Length of integers
// Note: It can be negative!
auto numberIntegers = static_cast<long long>(numberInteger.length() - divisorInteger.length());
auto diffNumberDivisor = subtract(numberIntegerOrig, divisorIntegerOrig, 0);
if (compare(abs(_number, 0), abs(_divisor, 0), 0) != "0" and compare(diffNumberDivisor, "9", 0) != "1")
numberIntegers++;
auto numberIntegers = static_cast<long long>(numberIntegerOrig.length() - divisorIntegerOrig.length());
long long numberDecimals = quotient.length() - numberIntegers;
std::string testedResult;
SplitNumberResult parts;
std::string finalQuotient = quotient;
bool adjusted = false;

// The gotos here are confusing. Here's a breakdown of how it runs:
// - Determine number of decimals
// - rounding: Round the numbers according to the estimated number of integers and decimals. <=========+-+
// |- Is the result correctly adjusted? | | Jump
// |-Yes|-> Jumps to finish, returns the result.----------------------------------------------------+-+----+
// |-No|-> Jumps to testDigits and adjust the numbers accordingly.----------------------------------+-+--+ |
// - testDigits: Adjust the number of digits by multiplying and then see if the result is reasonable.<-+-+--+ |
// |- Is reasonable? | | |
// |-Correct|-> Jumps to finish, returns the result.------------------------------------------------+-+----+
// |-Too many integer places / Not enough decimal places|-> Remove one integer place----------------+ | |
// |-Not enough integer places / Too many decimal places|-> Add one integer place---------------------+ |
// - finish: Returns the correct result, and reports it using the reportDivision function.<===================+
// |-> Ends program.

rounding:
// Scenario 1: No decimal places returned
// Solution : Do nothing
if (static_cast<size_t>(numberIntegers) == quotient.length() - 1)
quotient = quotient.substr(0, quotient.length() - 1);
finalQuotient = quotient.substr(0, quotient.length() - 1);
// Scenario 2: Decimal places more than requested
// Solution : Round to the nearest decimal place
else if (numberDecimals >= decimals and numberIntegers > 0)
{
auto beforeDecimal = quotient.substr(0, numberIntegers),
afterDecimal = quotient.substr(numberIntegers, numberDecimals);
auto beforeDecimal = quotient.substr(0, numberIntegers - 1),
afterDecimal = quotient.substr(numberIntegers - 1, numberDecimals);
if (not afterDecimal.empty() and afterDecimal.back() > '4')
afterDecimal = add(afterDecimal, "1", 0);
if (beforeDecimal.empty())
beforeDecimal = "0";
if (not afterDecimal.empty())
quotient = beforeDecimal + "." + afterDecimal;
finalQuotient = beforeDecimal + "." + afterDecimal;
else
quotient = beforeDecimal;
finalQuotient = beforeDecimal;
}
// Scenario 3: Result is less than one
// Solution : 1. Append "0." to the beginning
// 2. Append appropriate amount of zeros
else if (numberIntegers <= 0)
quotient = "0." + std::string(-numberIntegers, '0') + quotient.substr(0, quotient.length() - 1);
finalQuotient = "0." + std::string(-numberIntegers, '0') + quotient.substr(0, quotient.length() - 1);

// Scenario 4: Decimal places less than requested
// Solution : Pad with trailing zeros
if (numberDecimals < decimals and numberDecimals >= 0)
{
auto difference = decimals - numberDecimals;
quotient += std::string(difference, '0');
finalQuotient += std::string(difference, '0');
}

testDigits:
// Here, we try out the estimated value, and see if it is right.
testedResult = multiply(finalQuotient, _divisor, 0);
parts = splitNumber(testedResult, "0", false, false);

if (parts.splitNumberArray[0].length() == numberIntegerOrig.length() or
parts.splitNumberArray[1].length() == numberDecimalOrig.length())
goto finish;
if (parts.splitNumberArray[0].length() > numberIntegerOrig.length() and
parts.splitNumberArray[1].length() < numberDecimalOrig.length())
{
numberIntegers--;
numberDecimals = finalQuotient.length() - numberIntegers;
adjusted = true;
goto rounding;
}
if (parts.splitNumberArray[0].length() < numberIntegerOrig.length() and
parts.splitNumberArray[1].length() > numberDecimalOrig.length())
{
numberIntegers++;
numberDecimals = finalQuotient.length() - numberIntegers;
adjusted = true;
goto rounding;
}

finish:
return reportDivision(
tempFormattedAns, remainder, quotient, divisor, _divisor, _number, steps, width, resultIsNegative);
tempFormattedAns, remainder, finalQuotient, divisor, _divisor, _number, steps, width, resultIsNegative);
}

#ifndef NO_MAIN
Expand Down
18 changes: 13 additions & 5 deletions src/multiply/multiply.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ std::string multiply(const std::string_view& a, const std::string_view& b, const
if (b == "1")
return static_cast<std::string>(a);

const auto& [splitNumberArray, aIsNegative, bIsNegative] = splitNumber(a, b, false);
const auto& [splitNumberArray, aIsNegative, bIsNegative] = splitNumber(a, b, false, false);
bool resultIsNegative = false;
const auto& [aInteger, aDecimal, bInteger, bDecimal] = splitNumberArray;
const std::string &aStr = aInteger + aDecimal, bStr = bInteger + bDecimal;
Expand Down Expand Up @@ -106,10 +106,18 @@ std::string multiply(const std::string_view& a, const std::string_view& b, const
finalProdDigits[indexDigit] = sum;
}

return reportMultiply(
static_cast<std::string>(a), static_cast<std::string>(b), aStr, bStr, finalProdDigits, finalProdCarries, prodDigitsOut, carries,
resultIsNegative,
steps);
const size_t numberDecimals = aDecimal.length() + bDecimal.length();
return reportMultiply(static_cast<std::string>(a),
static_cast<std::string>(b),
aStr,
bStr,
finalProdDigits,
finalProdCarries,
prodDigitsOut,
carries,
resultIsNegative,
numberDecimals,
steps);
}

#ifndef NO_MAIN
Expand Down
19 changes: 16 additions & 3 deletions src/multiply/multiplyReport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ std::string reportMultiply(const std::string& a,
const std::vector<int>& finalProdCarries,
const std::vector<std::vector<int>>& prodDigitsOut,
const std::vector<std::vector<int>>& carries,
bool resultIsNegative,
const bool resultIsNegative,
const size_t numberDecimals,
const int steps)
{
std::stringstream ss;
Expand Down Expand Up @@ -92,9 +93,21 @@ std::string reportMultiply(const std::string& a,
ss << a << " " MULTIPLY " " << b << " = ";
if (resultIsNegative)
ss << '-';
for (const auto vector = replaceLeadingZeros(finalProdDigits); const int i : vector)
const auto vector = replaceLeadingZeros(finalProdDigits);

std::string out;
for (const int i : vector)
if (i >= 0)
ss << i;
out += std::to_string(i);
if (long long numberIntegers =
static_cast<long long>(out.length()) - numberDecimals; // NOLINT(*-narrowing-conversions)
numberIntegers < 0)
out = std::string(-numberIntegers, '0') + "." + out;
else if (numberIntegers == 0)
out = "0." + out;
else
out.insert(numberIntegers, ".");

ss << out;
return ss.str();
}
1 change: 1 addition & 0 deletions src/multiply/multiplyReport.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ std::string reportMultiply(const std::string& a,
const std::vector<std::vector<int>>& prodDigitsOut,
const std::vector<std::vector<int>>& carries,
bool resultIsNegative = false,
size_t numberDecimals = 0,
int steps = 2);
Loading

0 comments on commit c1e62f1

Please sign in to comment.