M module-apps/application-calculator/data/CalculatorUtility.cpp => module-apps/application-calculator/data/CalculatorUtility.cpp +70 -0
@@ 15,6 15,9 @@ Result Calculator::calculate(std::string source)
double result = te_interp(source.c_str(), &error);
if (error == 0 && !std::isinf(result) && !std::isnan(result)) {
auto output = utils::to_string(result);
+ if (output.length() > CalculatorConstants::maxStringLength) {
+ output = getValueThatFitsOnScreen(result);
+ }
if (utils::localize.get("app_calculator_decimal_separator") == style::calculator::symbols::strings::comma) {
output.replace(output.find(style::calculator::symbols::strings::full_stop),
std::size(std::string_view(style::calculator::symbols::strings::full_stop)),
@@ 48,3 51,70 @@ std::string Calculator::replaceAllOccurrences(std::string input, const std::stri
}
return input;
}
+
+std::string Calculator::getValueThatFitsOnScreen(double result)
+{
+ auto base = static_cast<long long>(result);
+ auto length = utils::to_string(base).length();
+ if (base < 0) {
+ length -= 1;
+ }
+ if (length > CalculatorConstants::expLength + 1) {
+ return convertToNumberWithPositiveExponent(result, length - 1);
+ }
+ else if (length == CalculatorConstants::expLength + 1) {
+ if (result < 0) {
+ return utils::to_string(getCoefficient(result, CalculatorConstants::veryLowPrecision));
+ }
+ return utils::to_string(getCoefficient(result, CalculatorConstants::lowPrecision));
+ }
+ else if (length == 1 && result < -1) {
+ return utils::to_string(getCoefficient(result, CalculatorConstants::lowPrecision));
+ }
+ else if (result > 1) {
+ return utils::to_string(getCoefficient(result, CalculatorConstants::precision));
+ }
+ else {
+ return convertToNumberWithNegativeExponent(result, base);
+ }
+}
+
+std::string Calculator::convertToNumberWithPositiveExponent(double result, uint32_t exponent)
+{
+ result /= pow(10, exponent);
+ auto exponentLength = utils::to_string(exponent).length();
+ auto decimalPlace = CalculatorConstants::precision - exponentLength - CalculatorConstants::expLength;
+ if (result < 0) {
+ decimalPlace -= 1;
+ }
+ return utils::to_string(getCoefficient(result, decimalPlace)) + "e" + utils::to_string(exponent);
+}
+
+std::string Calculator::convertToNumberWithNegativeExponent(double result, long long base)
+{
+ double frac = (result - base) * pow(10, CalculatorConstants::highPrecision);
+ if (result < 0) {
+ frac *= -1;
+ }
+ auto fractionalPart = static_cast<unsigned long int>(round(frac));
+ auto fracLength = utils::to_string(fractionalPart).length();
+ auto exponent = CalculatorConstants::highPrecision - fracLength + 1;
+ if (exponent > CalculatorConstants::minusExpLength + 1) {
+ result *= pow(10, exponent);
+ auto exponentLength = utils::to_string(exponent).length();
+ auto decimalPlace = CalculatorConstants::precision - exponentLength - CalculatorConstants::minusExpLength;
+ if (result < 0) {
+ decimalPlace -= 1;
+ }
+ return utils::to_string(getCoefficient(result, decimalPlace)) + "e-" + utils::to_string(exponent);
+ }
+ else if (result < 0) {
+ return utils::to_string(getCoefficient(result, CalculatorConstants::lowPrecision));
+ }
+ return utils::to_string(getCoefficient(result, CalculatorConstants::precision));
+}
+
+long double Calculator::getCoefficient(double result, uint32_t precision)
+{
+ return std::roundl(result * pow(10, precision)) / pow(10, precision);
+}
M module-apps/application-calculator/data/CalculatorUtility.hpp => module-apps/application-calculator/data/CalculatorUtility.hpp +15 -0
@@ 4,6 4,17 @@
#pragma once
#include <string>
+namespace CalculatorConstants
+{
+ inline constexpr auto veryLowPrecision = 4;
+ inline constexpr auto lowPrecision = 5;
+ inline constexpr auto precision = 6;
+ inline constexpr auto highPrecision = 8;
+ inline constexpr auto expLength = 1;
+ inline constexpr auto minusExpLength = 2;
+ inline constexpr auto maxStringLength = 7;
+} // namespace CalculatorConstants
+
struct Result
{
std::string equation;
@@ 18,6 29,10 @@ class Calculator
/// @return: new string with replaced all occurrences of given string to the new one in whole input
std::string replaceAllOccurrences(std::string input, const std::string &from, const std::string &to);
std::string prepareEquationForParser(std::string input);
+ std::string getValueThatFitsOnScreen(double result);
+ long double getCoefficient(double result, uint32_t precision);
+ std::string convertToNumberWithPositiveExponent(double result, uint32_t exponent);
+ std::string convertToNumberWithNegativeExponent(double result, long long base);
public:
Result calculate(std::string source);
M module-apps/application-calculator/tests/CalculatorUtility_tests.cpp => module-apps/application-calculator/tests/CalculatorUtility_tests.cpp +47 -0
@@ 103,4 103,51 @@ TEST_CASE("Calculator utilities")
REQUIRE(result.equation == "1.79769e+308*2");
REQUIRE(result.isError);
}
+
+ SECTION("Round to fit in screen")
+ {
+ auto result = calculator.calculate("1.1234512345");
+ REQUIRE(result.value == "1.123451");
+ REQUIRE(!result.isError);
+
+ result = calculator.calculate("0.0567891");
+ REQUIRE(result.value == "0.056789");
+ REQUIRE(!result.isError);
+
+ result = calculator.calculate("-0.056789");
+ REQUIRE(result.value == "-0.05679");
+ REQUIRE(!result.isError);
+
+ result = calculator.calculate("15.556789");
+ REQUIRE(result.value == "15.55679");
+ REQUIRE(!result.isError);
+ }
+
+ SECTION("Change to scientific notation (number > 0)")
+ {
+ auto result = calculator.calculate("12345.55555");
+ REQUIRE(result.value == "1.2346e4");
+ REQUIRE(!result.isError);
+ }
+
+ SECTION("Change to scientific notation (number < 0)")
+ {
+ auto result = calculator.calculate("-12345.55555");
+ REQUIRE(result.value == "-1.235e4");
+ REQUIRE(!result.isError);
+ }
+
+ SECTION("Change to scientific notation (0 < number < 1)")
+ {
+ auto result = calculator.calculate("0.000456712");
+ REQUIRE(result.value == "4.567e-4");
+ REQUIRE(!result.isError);
+ }
+
+ SECTION("Change to scientific notation (-1 < number < 0)")
+ {
+ auto result = calculator.calculate("-0.000456712");
+ REQUIRE(result.value == "-4.57e-4");
+ REQUIRE(!result.isError);
+ }
}
M module-apps/application-calculator/widgets/CalculatorStyle.hpp => module-apps/application-calculator/widgets/CalculatorStyle.hpp +1 -0
@@ 34,6 34,7 @@ namespace style::calculator
inline constexpr auto full_stop = 0x002E;
inline constexpr auto comma = 0x002C;
inline constexpr auto equals = 0x003D;
+ inline constexpr auto zero = 0x0030;
} // namespace codes
namespace strings
M module-apps/application-calculator/windows/CalculatorMainWindow.cpp => module-apps/application-calculator/windows/CalculatorMainWindow.cpp +42 -7
@@ 63,8 63,15 @@ namespace gui
if (!event.isShortPress()) {
return false;
}
+ if (event.is(gui::KeyCode::KEY_0) && mathOperationInput->getText() == "0") {
+ return true;
+ }
auto lastChar = mathOperationInput->getText()[mathOperationInput->getText().length() - 1];
bool lastCharIsSymbol = isSymbol(lastChar);
+ if (lastChar == style::calculator::symbols::codes::zero && isSymbol(getPenultimate()) &&
+ !isDecimalSeparator(getPenultimate()) && event.is(gui::KeyCode::KEY_0)) {
+ return true;
+ }
if (event.keyCode == gui::KeyCode::KEY_UP) {
writeEquation(lastCharIsSymbol, style::calculator::symbols::strings::plus);
return true;
@@ 89,6 96,15 @@ namespace gui
}
return true;
}
+ if (lastChar == style::calculator::symbols::codes::zero && isSymbol(getPenultimate()) &&
+ !isDecimalSeparator(getPenultimate()) && !event.is(gui::KeyCode::KEY_0) &&
+ !event.is(gui::KeyCode::KEY_PND) && !event.is(gui::KeyCode::KEY_ENTER)) {
+ mathOperationInput->removeChar();
+ return false;
+ }
+ if (!event.is(gui::KeyCode::KEY_0) && mathOperationInput->getText() == "0") {
+ mathOperationInput->clear();
+ }
return false;
};
}
@@ 103,21 119,36 @@ namespace gui
character == style::calculator::symbols::codes::full_stop;
}
+ bool CalculatorMainWindow::isDecimalSeparator(uint32_t character)
+ {
+ return character == style::calculator::symbols::codes::comma ||
+ character == style::calculator::symbols::codes::full_stop;
+ }
+
+ uint32_t CalculatorMainWindow::getPenultimate()
+ {
+ if (mathOperationInput->getText().length() > 1) {
+ return mathOperationInput->getText()[mathOperationInput->getText().length() - 2];
+ }
+ return 0;
+ }
+
void CalculatorMainWindow::writeEquation(bool lastCharIsSymbol, const UTF8 &symbol)
{
if (!mathOperationInput->getText().empty()) {
if (lastCharIsSymbol && symbol != style::calculator::symbols::strings::minus) {
- mathOperationInput->setRichText(
- std::string(mathOperationInput->getText()).erase(mathOperationInput->getText().length() - 1) +
- symbol.c_str());
+ if (!isSymbol(getPenultimate()) && mathOperationInput->getText().length() > 1) {
+ mathOperationInput->removeChar();
+ mathOperationInput->addText(symbol);
+ }
}
else {
- mathOperationInput->setRichText(mathOperationInput->getText() + symbol);
+ mathOperationInput->addText(symbol);
}
}
else if (symbol == style::calculator::symbols::strings::minus) {
- mathOperationInput->setRichText(mathOperationInput->getText() + symbol);
+ mathOperationInput->addText(symbol);
}
}
@@ 126,7 157,11 @@ namespace gui
if (!mathOperationInput->getText().empty()) {
std::vector<int> symbolsIndexes;
auto input = std::string(mathOperationInput->getText()).erase(mathOperationInput->getText().length() - 1);
- symbolsIndexes.push_back(input.find_last_of(style::calculator::symbols::strings::minus));
+ auto exponentIndex = input.find_last_of('e');
+ auto minusIndex = input.find_last_of(style::calculator::symbols::strings::minus);
+ if (minusIndex != exponentIndex + 1) {
+ symbolsIndexes.push_back(minusIndex);
+ }
symbolsIndexes.push_back(input.find_last_of(style::calculator::symbols::strings::plus));
symbolsIndexes.push_back(input.find_last_of(style::calculator::symbols::strings::division));
symbolsIndexes.push_back(input.find_last_of(style::calculator::symbols::strings::multiplication));
@@ 156,7 191,7 @@ namespace gui
if (inputEvent.keyCode == gui::KeyCode::KEY_ENTER) {
auto result = Calculator().calculate(std::string(mathOperationInput->getText()));
- mathOperationInput->setRichText(result.value);
+ mathOperationInput->setText(result.value);
clearInput = result.isError;
return true;
}
M module-apps/application-calculator/windows/CalculatorMainWindow.hpp => module-apps/application-calculator/windows/CalculatorMainWindow.hpp +2 -0
@@ 22,6 22,8 @@ namespace gui
void applyInputCallback();
bool isPreviousNumberDecimal();
bool isSymbol(uint32_t character);
+ bool isDecimalSeparator(uint32_t character);
+ uint32_t getPenultimate();
public:
CalculatorMainWindow(app::Application *app, std::string name);
M module-utils/Utils.hpp => module-utils/Utils.hpp +7 -0
@@ 122,7 122,14 @@ namespace utils
baseAsStr = "-0";
}
}
+
auto fractionalPart = static_cast<unsigned long int>(roundl(frac));
+ auto fractionalPartLength = std::to_string(fractionalPart).length();
+ if (fractionalPartLength > precision) {
+ base += 1;
+ baseAsStr = std::to_string(base);
+ fractionalPart = 0;
+ }
if (fractionalPart == 0) {
if (baseAsStr == "-0") {
return "0";