Calculator: Use Checked to ensure entered values do not overflow

This replaces the types of m_int_value and m_frac_value with
Checked<u64> which makes it possible to check if the value overflowed
when entering a digit. If that happens, the digit will just be ignored.

This fixes #1263.
This commit is contained in:
Max Wipfli 2021-05-23 21:23:07 +02:00 committed by Linus Groh
parent 7a5407982f
commit 7b53ca5309
Notes: sideshowbarker 2024-07-18 17:29:16 +09:00
2 changed files with 23 additions and 17 deletions

View File

@ -19,6 +19,7 @@ Keypad::~Keypad()
void Keypad::type_digit(int digit)
{
u64 previous_value = 0;
switch (m_state) {
case State::External:
m_state = State::TypingInteger;
@ -28,17 +29,22 @@ void Keypad::type_digit(int digit)
m_frac_length = 0;
break;
case State::TypingInteger:
VERIFY(m_frac_value == 0);
VERIFY(m_frac_value.value() == 0);
VERIFY(m_frac_length == 0);
previous_value = m_int_value.value();
m_int_value *= 10;
m_int_value += digit;
if (m_int_value.has_overflow())
m_int_value = previous_value;
break;
case State::TypingDecimal:
if (m_frac_length > 6)
break;
previous_value = m_frac_value.value();
m_frac_value *= 10;
m_frac_value += digit;
m_frac_length++;
if (m_frac_value.has_overflow())
m_frac_value = previous_value;
else
m_frac_length++;
break;
}
}
@ -53,7 +59,7 @@ void Keypad::type_decimal_point()
m_frac_length = 0;
break;
case State::TypingInteger:
VERIFY(m_frac_value == 0);
VERIFY(m_frac_value.value() == 0);
VERIFY(m_frac_length == 0);
m_state = State::TypingDecimal;
break;
@ -78,14 +84,14 @@ void Keypad::type_backspace()
m_frac_length--;
break;
}
VERIFY(m_frac_value == 0);
VERIFY(m_frac_value.value() == 0);
m_state = State::TypingInteger;
[[fallthrough]];
case State::TypingInteger:
VERIFY(m_frac_value == 0);
VERIFY(m_frac_value.value() == 0);
VERIFY(m_frac_length == 0);
m_int_value /= 10;
if (m_int_value == 0)
if (m_int_value.value() == 0)
m_negative = false;
break;
}
@ -95,15 +101,15 @@ double Keypad::value() const
{
double res = 0.0;
long frac = m_frac_value;
u64 frac = m_frac_value.value();
for (int i = 0; i < m_frac_length; i++) {
int digit = frac % 10;
u8 digit = frac % 10;
res += digit;
res /= 10.0;
frac /= 10;
}
res += m_int_value;
res += m_int_value.value();
if (m_negative)
res = -res;
@ -121,7 +127,7 @@ void Keypad::set_value(double value)
m_negative = false;
m_int_value = value;
value -= m_int_value;
value -= m_int_value.value();
m_frac_value = 0;
m_frac_length = 0;
@ -143,7 +149,7 @@ String Keypad::to_string() const
StringBuilder builder;
if (m_negative)
builder.append("-");
builder.appendff("{}", m_int_value);
builder.appendff("{}", m_int_value.value());
// NOTE: This is so the decimal point appears on screen as soon as you type it.
if (m_frac_length > 0 || m_state == State::TypingDecimal)
@ -152,7 +158,7 @@ String Keypad::to_string() const
if (m_frac_length > 0) {
// FIXME: This disables the compiletime format string check since we can't parse '}}' here correctly.
// remove the 'StringView { }' when that's fixed.
builder.appendff(StringView { "{:0{}}" }, m_frac_value, m_frac_length);
builder.appendff(StringView { "{:0{}}" }, m_frac_value.value(), m_frac_length);
}
return builder.to_string();

View File

@ -30,9 +30,9 @@ public:
private:
// Internal representation of the current decimal value.
bool m_negative { false };
long m_int_value { 0 };
long m_frac_value { 0 };
int m_frac_length { 0 };
Checked<u64> m_int_value { 0 };
Checked<u64> m_frac_value { 0 };
u8 m_frac_length { 0 };
// E.g. for -35.004200,
// m_negative = true
// m_int_value = 35