mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 07:47:37 +00:00
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.
This commit is contained in:
parent
939bf3e864
commit
7532ef78ad
3 changed files with 50 additions and 1 deletions
|
@ -68,11 +68,14 @@ KeypadValue Calculator::begin_operation(Operation operation, KeypadValue argumen
|
||||||
res = argument;
|
res = argument;
|
||||||
break;
|
break;
|
||||||
case Operation::MemAdd:
|
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;
|
res = m_mem;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (should_be_rounded(res))
|
||||||
|
round(res);
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -112,6 +115,9 @@ KeypadValue Calculator::finish_operation(KeypadValue argument)
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (should_be_rounded(res))
|
||||||
|
round(res);
|
||||||
|
|
||||||
clear_operation();
|
clear_operation();
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
@ -122,3 +128,29 @@ void Calculator::clear_operation()
|
||||||
m_saved_argument = 0;
|
m_saved_argument = 0;
|
||||||
clear_error();
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -47,6 +47,22 @@ public:
|
||||||
void clear_error() { m_has_error = false; }
|
void clear_error() { m_has_error = false; }
|
||||||
|
|
||||||
private:
|
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 };
|
Operation m_operation_in_progress { Operation::None };
|
||||||
KeypadValue m_saved_argument { (i64)0 };
|
KeypadValue m_saved_argument { (i64)0 };
|
||||||
KeypadValue m_mem { (i64)0 };
|
KeypadValue m_mem { (i64)0 };
|
||||||
|
|
|
@ -12,6 +12,7 @@
|
||||||
|
|
||||||
class KeypadValue {
|
class KeypadValue {
|
||||||
friend class Keypad;
|
friend class Keypad;
|
||||||
|
friend class Calculator;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
KeypadValue(i64, u8);
|
KeypadValue(i64, u8);
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue