summaryrefslogtreecommitdiff
path: root/Userland/Applications/Calculator
diff options
context:
space:
mode:
authorLucas CHOLLET <lucas.chollet@free.fr>2022-01-01 21:49:30 +0100
committerAndreas Kling <kling@serenityos.org>2022-01-02 15:42:13 +0100
commit7532ef78ad9ddc65c93f71813d1574a7d7237b50 (patch)
tree11ba836ca10bf1fd0fc8433c0c381a2e8308ccfd /Userland/Applications/Calculator
parent939bf3e864b4b49debc39678152b99d7fb0b4992 (diff)
downloadserenity-7532ef78ad9ddc65c93f71813d1574a7d7237b50.zip
Calculator: Round small number to prevent crash
Small numbers (smaller than 1e-19) can't be displayed in the calculator. They provoke a division by zero in Keypad::set_value(), as 10^20 overflows.
Diffstat (limited to 'Userland/Applications/Calculator')
-rw-r--r--Userland/Applications/Calculator/Calculator.cpp34
-rw-r--r--Userland/Applications/Calculator/Calculator.h16
-rw-r--r--Userland/Applications/Calculator/KeypadValue.h1
3 files changed, 50 insertions, 1 deletions
diff --git a/Userland/Applications/Calculator/Calculator.cpp b/Userland/Applications/Calculator/Calculator.cpp
index 7d065d6029..5af39b6d61 100644
--- a/Userland/Applications/Calculator/Calculator.cpp
+++ b/Userland/Applications/Calculator/Calculator.cpp
@@ -68,11 +68,14 @@ KeypadValue Calculator::begin_operation(Operation operation, KeypadValue argumen
res = argument;
break;
case Operation::MemAdd:
- m_mem = m_mem + argument; //avoids the need for operator+=()
+ m_mem = m_mem + argument; // avoids the need for operator+=()
res = m_mem;
break;
}
+ if (should_be_rounded(res))
+ round(res);
+
return res;
}
@@ -112,6 +115,9 @@ KeypadValue Calculator::finish_operation(KeypadValue argument)
VERIFY_NOT_REACHED();
}
+ if (should_be_rounded(res))
+ round(res);
+
clear_operation();
return res;
}
@@ -122,3 +128,29 @@ void Calculator::clear_operation()
m_saved_argument = 0;
clear_error();
}
+
+bool Calculator::should_be_rounded(KeypadValue value)
+{
+ // We check if pow(10, value.m_decimal_places) overflow.
+ // If it does, the value can't be displayed (and provoke a division by zero), see Keypad::set_value()
+ // For u64, the threshold is 19
+ return value.m_decimal_places > rounding_threshold;
+}
+
+void Calculator::round(KeypadValue& value)
+{
+ while (value.m_decimal_places > rounding_threshold) {
+ bool const need_increment = value.m_value % 10 > 4;
+
+ value.m_value /= 10;
+ if (need_increment)
+ value.m_value++;
+
+ value.m_decimal_places--;
+
+ if (value.m_value == 0) {
+ value = 0;
+ return;
+ }
+ }
+}
diff --git a/Userland/Applications/Calculator/Calculator.h b/Userland/Applications/Calculator/Calculator.h
index 1ca9fca5c0..91f6c21eff 100644
--- a/Userland/Applications/Calculator/Calculator.h
+++ b/Userland/Applications/Calculator/Calculator.h
@@ -47,6 +47,22 @@ public:
void clear_error() { m_has_error = false; }
private:
+ static bool should_be_rounded(KeypadValue);
+ static void round(KeypadValue&);
+
+ static constexpr auto rounding_threshold = []() consteval
+ {
+ using used_type = u64;
+
+ auto count = 1;
+ used_type res = 10;
+ while (!__builtin_mul_overflow(res, (used_type)10, &res)) {
+ count++;
+ }
+ return count;
+ }
+ ();
+
Operation m_operation_in_progress { Operation::None };
KeypadValue m_saved_argument { (i64)0 };
KeypadValue m_mem { (i64)0 };
diff --git a/Userland/Applications/Calculator/KeypadValue.h b/Userland/Applications/Calculator/KeypadValue.h
index 3e2ed586fc..3d63e0f27f 100644
--- a/Userland/Applications/Calculator/KeypadValue.h
+++ b/Userland/Applications/Calculator/KeypadValue.h
@@ -12,6 +12,7 @@
class KeypadValue {
friend class Keypad;
+ friend class Calculator;
public:
KeypadValue(i64, u8);