Skip to content

Commit 768ae5d

Browse files
committed
Allow faster decimal power
- Faster decimal power through `exp` and `ln` - More accurate `exp` by fixing a rounding bug - [DEV] Allow building all tests with `make tests` - Allow `decimal` option in `power`
1 parent 86e6f78 commit 768ae5d

File tree

13 files changed

+65
-50
lines changed

13 files changed

+65
-50
lines changed

include/fn/calc.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -163,9 +163,10 @@ namespace steppable::__internals::calc
163163
* @param[in] _number The string representation of the number.
164164
* @param[in] raiseTo The string representation of the power to raise the number to.
165165
* @param[in] steps The number of steps to perform the power operation.
166+
* @param[in] decimals The number of decimals to output from the power operation.
166167
* @return The result of the power operation as a string.
167168
*/
168-
std::string power(const std::string& _number, const std::string& raiseTo, int steps = 2);
169+
std::string power(const std::string& _number, const std::string& raiseTo, int steps = 2, int decimals = 8);
169170

170171
/**
171172
* @brief Calculates e^x. Shorthand of power(x, E, 0);

res/translations/en-US/power.stp_localized

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

res/translations/power.stp_strings

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

res/translations/zh-HK/power.stp_localized

Lines changed: 1 addition & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/calc/hyp/hyp.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ namespace steppable::__internals::calc
6868
checkDecimalArg(&decimals);
6969

7070
const auto& twoX = multiply(x, "2", 0, decimals + 2);
71-
const auto& eTwoX = roundOff(power(static_cast<std::string>(constants::E), twoX, 0), decimals + 2);
72-
const auto& eX = roundOff(power(static_cast<std::string>(constants::E), x, 0), decimals + 2);
71+
const auto& eTwoX = exp(twoX, decimals + 2);
72+
const auto& eX = exp(x, decimals + 2);
7373

7474
const auto& numerator = add(eTwoX, "1", 0);
7575
const auto& denominator = multiply("2", eX, 0, decimals + 2);

src/calc/log/log.cpp

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,10 +65,10 @@ namespace steppable::__internals::calc
6565
// ln(x) = ----------------------- = --------------
6666
// 2 2
6767
// x + 4x + 1 x + 4x + 1
68-
const auto& x2 = power(x, "2", 0);
68+
const auto& x2 = multiply(x, x, 0, static_cast<int>(decimals) + 2);
6969
const auto& x2Minus1 = subtract(x2, "1", 0);
70-
const auto& numerator = multiply("3", x2Minus1, 0, static_cast<int>(decimals));
71-
const auto& fourX = multiply(x, "4", 0, static_cast<int>(decimals));
70+
const auto& numerator = multiply("3", x2Minus1, 0, static_cast<int>(decimals) + 2);
71+
const auto& fourX = multiply(x, "4", 0, static_cast<int>(decimals) + 2);
7272

7373
auto denominator = add(x2, fourX, 0);
7474
denominator = add(denominator, "1", 0);
@@ -84,7 +84,7 @@ namespace steppable::__internals::calc
8484
// n + 1
8585

8686
const auto& epsilon = "0." + std::string(_decimals + 1, '0') + "1";
87-
auto yn = divide(numerator, denominator, 0, static_cast<int>(decimals));
87+
auto yn = divide(numerator, denominator, 0, static_cast<int>(decimals) + 2);
8888
auto yn1 = yn;
8989

9090
auto error = abs(subtract(yn, yn1, 0), 0);

src/calc/power/power.cpp

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -59,23 +59,20 @@ namespace steppable::prettyPrint::printers
5959

6060
namespace steppable::__internals::calc
6161
{
62-
std::string power(const std::string& _number, const std::string& _raiseTo, const int steps)
62+
std::string power(const std::string& _number, const std::string& _raiseTo, const int steps, const int decimals)
6363
{
6464
std::string raiseTo = static_cast<std::string>(_raiseTo);
6565
std::string number = static_cast<std::string>(_number);
6666

6767
if (isDecimal(raiseTo))
6868
{
6969
// Raising to decimal power.
70-
// Steps:
71-
// 1. Convert the decimal to a fraction.
72-
// 2. Raise the number to the numerator of the fraction.
73-
// 3. Take the root of the number to the denominator of the fraction.
74-
const auto& fraction = Fraction(raiseTo);
75-
const auto& [top, bottom] = fraction.asArray();
76-
const auto& powerResult = power(_number, top, 0);
77-
const auto rootResult = root(powerResult, bottom, 8);
78-
return reportPowerRoot(number, raiseTo, fraction, rootResult, steps);
70+
// b b ln(a)
71+
// a = e
72+
const auto lnNumber = ln(_number, decimals + 2);
73+
const auto bLnNumber = multiply(lnNumber, raiseTo, 0, decimals + 2);
74+
// return reportPowerRoot(number, raiseTo, fraction, rootResult, steps);
75+
return exp(bLnNumber, decimals);
7976
}
8077

8178
// Here, we attempt to give a quick answer, instead of doing pointless iterations.
@@ -118,26 +115,21 @@ namespace steppable::__internals::calc
118115
raiseTo = raiseTo.substr(1);
119116
negativePower = true;
120117
}
121-
return reportPower(number, raiseTo, numberTrailingZeros, negativePower, steps);
118+
return reportPower(number, raiseTo, numberTrailingZeros, negativePower, steps, decimals);
122119
}
123120

124121
std::string exp(const std::string& x, const size_t decimals)
125122
{
126-
if (compare(x, "20", 0) == "0")
123+
constexpr size_t iter = 50;
124+
std::string sum = "1";
125+
std::string term = "1";
126+
for (size_t i = 1; i < iter; i++)
127127
{
128-
constexpr size_t iter = 200;
129-
std::string sum = "1";
130-
std::string term = "1";
131-
for (size_t i = 1; i < iter; i++)
132-
{
133-
std::string frac = divide(x, std::to_string(i), 0, static_cast<int>(decimals));
134-
term = multiply(term, frac, 0, static_cast<int>(decimals));
135-
sum = add(sum, term, 0);
136-
}
137-
return sum;
128+
std::string frac = divide(x, std::to_string(i), 0, static_cast<int>(decimals) + 2);
129+
term = multiply(term, frac, 0, static_cast<int>(decimals) + 2);
130+
sum = add(sum, term, 0);
138131
}
139-
140-
return power(exp(divide(x, "4", 0, static_cast<int>(decimals))), "4", 0);
132+
return roundOff(sum, decimals);
141133
}
142134
} // namespace steppable::__internals::calc
143135

@@ -149,14 +141,22 @@ int main(const int _argc, const char* _argv[])
149141
program.addPosArg('a', $("power", "4252ac37-a36b-4605-9ec1-d69e70b91b46"));
150142
program.addPosArg('b', $("power", "1fefffaf-7731-430b-989f-42e74017a2eb"));
151143
program.addKeywordArg("steps", 2, $("power", "cb935566-6125-49ce-9ebc-e157410a3005"));
144+
program.addKeywordArg("decimals", 2, $("power", "03c15572-a5aa-4b1c-a705-105770999741"));
152145
program.addSwitch("profile", false, $("power", "e5d48237-e161-494d-940b-e2457411fcfb"));
153146
program.parseArgs();
154147

155148
int steps = program.getKeywordArgument("steps");
149+
int decimals = program.getKeywordArgument("decimals");
156150
bool profile = program.getSwitch("profile");
157151
const auto& aStr = program.getPosArg(0);
158152
const auto& bStr = program.getPosArg(1);
159153

154+
if (steps == 475)
155+
{
156+
std::cout << steppable::__internals::calc::exp(aStr, decimals) << std::endl;
157+
return 0;
158+
}
159+
160160
if (profile)
161161
{
162162
TIC(Power)

src/calc/power/powerReport.cpp

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
#include "fn/calc.hpp"
3535
#include "fraction.hpp"
3636
#include "getString.hpp"
37+
#include "rounding.hpp"
3738
#include "symbols.hpp"
3839
#include "util.hpp"
3940

@@ -71,7 +72,8 @@ std::string reportPower(const std::string& _number,
7172
const std::string& raiseTo,
7273
const size_t numberTrailingZeros,
7374
const bool negativePower,
74-
const int steps)
75+
const int steps,
76+
const int decimals)
7577
{
7678
std::stringstream ss;
7779
auto result = "1"s;
@@ -91,9 +93,9 @@ std::string reportPower(const std::string& _number,
9193

9294
loop(raiseTo, [&](const auto& i) {
9395
if (not negativePower)
94-
result = multiply(result, _number, 0);
96+
result = multiply(result, _number, 0, decimals + 1);
9597
else
96-
result = divide("1", result, 0);
98+
result = divide("1", result, 0, decimals + 1);
9799
auto currentPower = add(i, "1", 0);
98100
if (steps == 2)
99101
{
@@ -110,14 +112,15 @@ std::string reportPower(const std::string& _number,
110112

111113
finish:
112114
result = numUtils::standardizeNumber(result);
115+
result = numUtils::roundOff(result, decimals);
113116

114117
if (negativePower)
115118
{
116119
if (steps == 2)
117-
ss << BECAUSE << " " << divide("1", result, 1) << '\n';
120+
ss << BECAUSE << " " << divide("1", result, 1, decimals) << '\n';
118121
else if (steps == 1)
119122
{
120-
const auto& divisionResult = divide("1", result, 0);
123+
const auto& divisionResult = divide("1", result, 0, decimals);
121124
ss << _number << makeSuperscript('-') << makeSuperscript(static_cast<std::string>(raiseTo)) << " = "
122125
<< divisionResult;
123126
}
@@ -130,7 +133,7 @@ std::string reportPower(const std::string& _number,
130133
else if (steps == 0)
131134
ss << result;
132135

133-
loop(multiply(raiseTo, std::to_string(numberTrailingZeros), 0), [&](const auto& _) { ss << "0"; });
136+
loop(multiply(raiseTo, std::to_string(numberTrailingZeros), 0, 0), [&](const auto& _) { ss << "0"; });
134137

135138
return ss.str();
136139
}

src/calc/power/powerReport.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,4 +62,5 @@ std::string reportPower(const std::string& _number,
6262
const std::string& raiseTo,
6363
size_t numberTrailingZeros,
6464
bool negativePower,
65-
int steps);
65+
int steps,
66+
int decimals);

src/calc/trig/trig.cpp

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ namespace steppable::__internals::calc
133133
auto result = _cos(standardizeNumber(divide(x, "4", 0, decimals + 1)), decimals + 2);
134134
auto result2 = roundOff(multiply(result, result, 0, decimals + 2), static_cast<long>(decimals) + 2);
135135
// return 8 * C2 * (C2 - 1) + 1;
136-
return standardizeNumber(add(multiply("8", multiply(result2, subtract(result2, "1", 0), 0, decimals), 0), "1", 0));
136+
return standardizeNumber(
137+
add(multiply("8", multiply(result2, subtract(result2, "1", 0), 0, decimals), 0), "1", 0));
137138
}
138139

139140
std::string cos(const std::string& x, const int decimals, const int mode)
@@ -320,7 +321,7 @@ namespace steppable::__internals::calc
320321
{
321322
isReduced = true;
322323
// Reduce x to a small number
323-
x = divide("1", x, 0, decimals * 2);
324+
x = divide("1", x, 0, decimals + 2);
324325
}
325326
// Otherwise, use integration.
326327
// 1 / x
@@ -329,11 +330,11 @@ namespace steppable::__internals::calc
329330
// dx x + 1 | 2
330331
// / 0 t + 1
331332
auto fx = [&](const std::string& y) {
332-
const auto& y2 = power(y, "2", 0);
333+
const auto& y2 = multiply(y, y, 0);
333334
const auto& denominator = add(y2, "1", 0);
334-
return divide("1", denominator, 0, decimals + 1);
335+
return divide("1", denominator, 0, decimals * 2);
335336
};
336-
auto result = calculus::romberg(fx, "0", x, 10, decimals + 1);
337+
auto result = calculus::romberg(fx, "0", x, 10, decimals + 2);
337338
if (isReduced)
338339
{
339340
// If x was reduced, use the identity

src/rounding.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ namespace steppable::__internals::numUtils
7272
auto decimal = splitNumberArray[1];
7373
bool isNegative = splitNumberResult.aIsNegative;
7474

75+
if (decimal.length() < digits)
76+
return _number;
77+
7578
// Preserve one digit after the rounded digit
7679
decimal = decimal.substr(0, digits + 1);
7780
// Modify the integer part if the digit is greater than 5

tests/CMakeLists.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,6 @@ FOREACH(ITEM IN LISTS TEST_TARGETS)
2828
ADD_TEST(NAME ${ITEM} COMMAND ${CMAKE_BINARY_DIR}/tests/${ITEM})
2929
MESSAGE(TRACE "Added test case: ${ITEM}: ${ITEM}.cpp")
3030
ENDFOREACH()
31+
32+
ADD_CUSTOM_TARGET(tests)
33+
ADD_DEPENDENCIES(tests ${TEST_TARGETS})

tests/testPower.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -37,31 +37,31 @@ using namespace steppable::__internals::calc;
3737
SECTION(Power)
3838
const std::string number = "47";
3939
const std::string raiseTo = "10";
40-
const auto& result = power(number, raiseTo, 0);
40+
const auto& result = power(number, raiseTo, 0, 8);
4141

4242
_.assertIsEqual(result, "52599132235830049");
4343
SECTION_END()
4444

4545
SECTION(Power with Decimals)
4646
const std::string number = "47.5";
4747
const std::string raiseTo = "10";
48-
const auto& result = power(number, raiseTo, 0);
48+
const auto& result = power(number, raiseTo, 0, 8);
4949

50-
_.assertIsEqual(result, "58470404222497940.0634765625");
50+
_.assertIsEqual(result, "58470404222497940.06347656");
5151
SECTION_END()
5252

5353
SECTION(Power with Decimals)
5454
const std::string number = "0.5";
5555
const std::string raiseTo = "10";
56-
const auto& result = power(number, raiseTo, 0);
56+
const auto& result = power(number, raiseTo, 0, 8);
5757

58-
_.assertIsEqual(result, "0.0009765625");
58+
_.assertIsEqual(result, "0.00097656");
5959
SECTION_END()
6060

6161
SECTION(Power with Decimal Exponents)
6262
const std::string number = "4";
6363
const std::string raiseTo = "0.5";
64-
const auto& result = power(number, raiseTo, 0);
64+
const auto& result = power(number, raiseTo, 0, 2);
6565

6666
_.assertIsEqual(numUtils::roundOff(result), "2");
6767
SECTION_END()

0 commit comments

Comments
 (0)