diff options
author | Lucas CHOLLET <lucas.chollet@free.fr> | 2022-01-01 21:49:30 +0100 |
---|---|---|
committer | Andreas Kling <kling@serenityos.org> | 2022-01-02 15:42:13 +0100 |
commit | 7532ef78ad9ddc65c93f71813d1574a7d7237b50 (patch) | |
tree | 11ba836ca10bf1fd0fc8433c0c381a2e8308ccfd /Userland/Applications/Calculator | |
parent | 939bf3e864b4b49debc39678152b99d7fb0b4992 (diff) | |
download | serenity-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.cpp | 34 | ||||
-rw-r--r-- | Userland/Applications/Calculator/Calculator.h | 16 | ||||
-rw-r--r-- | Userland/Applications/Calculator/KeypadValue.h | 1 |
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); |