mirror of
https://github.com/LadybirdBrowser/ladybird.git
synced 2025-06-09 17:44:56 +09:00
Calculator: Use KeypadValue class instead of double
Calculator now uses the KeypadValue class instead of double in its internal calculations. By not constantly converting to double back-and-forth, we do not use precision simply by, for example, negating a number. This fixes #7484.
This commit is contained in:
parent
97d2a5799e
commit
8f552c9979
Notes:
sideshowbarker
2024-07-18 07:32:33 +09:00
Author: https://github.com/creator1creeper1
Commit: 8f552c9979
Pull-request: https://github.com/SerenityOS/serenity/pull/9152
Reviewed-by: https://github.com/alimpfard
Reviewed-by: https://github.com/linusg
6 changed files with 43 additions and 57 deletions
|
@ -5,6 +5,7 @@
|
|||
*/
|
||||
|
||||
#include "Calculator.h"
|
||||
#include "KeypadValue.h"
|
||||
#include <AK/Assertions.h>
|
||||
#include <AK/Math.h>
|
||||
|
||||
|
@ -16,9 +17,9 @@ Calculator::~Calculator()
|
|||
{
|
||||
}
|
||||
|
||||
double Calculator::begin_operation(Operation operation, double argument)
|
||||
KeypadValue Calculator::begin_operation(Operation operation, KeypadValue argument)
|
||||
{
|
||||
double res = 0.0;
|
||||
KeypadValue res = 0;
|
||||
|
||||
switch (operation) {
|
||||
case Operation::None:
|
||||
|
@ -33,30 +34,30 @@ double Calculator::begin_operation(Operation operation, double argument)
|
|||
return argument;
|
||||
|
||||
case Operation::Sqrt:
|
||||
if (argument < 0.0) {
|
||||
if (argument < 0) {
|
||||
m_has_error = true;
|
||||
return argument;
|
||||
}
|
||||
res = AK::sqrt(argument);
|
||||
res = KeypadValue { AK::sqrt((double)argument) };
|
||||
clear_operation();
|
||||
break;
|
||||
case Operation::Inverse:
|
||||
if (argument == 0.0) {
|
||||
if (argument == 0) {
|
||||
m_has_error = true;
|
||||
return argument;
|
||||
}
|
||||
res = 1 / argument;
|
||||
res = KeypadValue { 1.0 / (double)argument };
|
||||
clear_operation();
|
||||
break;
|
||||
case Operation::Percent:
|
||||
res = argument * 0.01;
|
||||
res = argument * KeypadValue { 1, 2 }; // also known as `KeypadValue{0.01}`
|
||||
break;
|
||||
case Operation::ToggleSign:
|
||||
res = -argument;
|
||||
break;
|
||||
|
||||
case Operation::MemClear:
|
||||
m_mem = 0.0;
|
||||
m_mem = 0;
|
||||
res = argument;
|
||||
break;
|
||||
case Operation::MemRecall:
|
||||
|
@ -67,7 +68,7 @@ double Calculator::begin_operation(Operation operation, double argument)
|
|||
res = argument;
|
||||
break;
|
||||
case Operation::MemAdd:
|
||||
m_mem += argument;
|
||||
m_mem = m_mem + argument; //avoids the need for operator+=()
|
||||
res = m_mem;
|
||||
break;
|
||||
}
|
||||
|
@ -75,9 +76,9 @@ double Calculator::begin_operation(Operation operation, double argument)
|
|||
return res;
|
||||
}
|
||||
|
||||
double Calculator::finish_operation(double argument)
|
||||
KeypadValue Calculator::finish_operation(KeypadValue argument)
|
||||
{
|
||||
double res = 0.0;
|
||||
KeypadValue res = 0;
|
||||
|
||||
switch (m_operation_in_progress) {
|
||||
case Operation::None:
|
||||
|
@ -93,11 +94,11 @@ double Calculator::finish_operation(double argument)
|
|||
res = m_saved_argument * argument;
|
||||
break;
|
||||
case Operation::Divide:
|
||||
if (argument == 0.0) {
|
||||
if (argument == 0) {
|
||||
m_has_error = true;
|
||||
return argument;
|
||||
}
|
||||
res = m_saved_argument / argument;
|
||||
res = KeypadValue { (double)m_saved_argument / (double)argument };
|
||||
break;
|
||||
|
||||
case Operation::Sqrt:
|
||||
|
@ -118,6 +119,6 @@ double Calculator::finish_operation(double argument)
|
|||
void Calculator::clear_operation()
|
||||
{
|
||||
m_operation_in_progress = Operation::None;
|
||||
m_saved_argument = 0.0;
|
||||
m_saved_argument = 0;
|
||||
clear_error();
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "KeypadValue.h"
|
||||
|
||||
// This type implements the regular calculator
|
||||
// behavior, such as performing arithmetic
|
||||
// operations and providing a memory cell.
|
||||
|
@ -36,8 +38,8 @@ public:
|
|||
MemAdd
|
||||
};
|
||||
|
||||
double begin_operation(Operation, double);
|
||||
double finish_operation(double);
|
||||
KeypadValue begin_operation(Operation, KeypadValue);
|
||||
KeypadValue finish_operation(KeypadValue);
|
||||
|
||||
bool has_error() const { return m_has_error; }
|
||||
|
||||
|
@ -46,7 +48,7 @@ public:
|
|||
|
||||
private:
|
||||
Operation m_operation_in_progress { Operation::None };
|
||||
double m_saved_argument { 0.0 };
|
||||
double m_mem { 0.0 };
|
||||
KeypadValue m_saved_argument { (i64)0 };
|
||||
KeypadValue m_mem { (i64)0 };
|
||||
bool m_has_error { false };
|
||||
};
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
*/
|
||||
|
||||
#include "CalculatorWidget.h"
|
||||
#include "KeypadValue.h"
|
||||
#include <Applications/Calculator/CalculatorGML.h>
|
||||
#include <LibGUI/Button.h>
|
||||
#include <LibGUI/Label.h>
|
||||
|
@ -96,8 +97,8 @@ CalculatorWidget::CalculatorWidget()
|
|||
|
||||
m_equals_button = *find_descendant_of_type_named<GUI::Button>("equal_button");
|
||||
m_equals_button->on_click = [this](auto) {
|
||||
double argument = m_keypad.value();
|
||||
double res = m_calculator.finish_operation(argument);
|
||||
KeypadValue argument = m_keypad.value();
|
||||
KeypadValue res = m_calculator.finish_operation(argument);
|
||||
m_keypad.set_value(res);
|
||||
update_display();
|
||||
};
|
||||
|
@ -110,8 +111,8 @@ CalculatorWidget::~CalculatorWidget()
|
|||
void CalculatorWidget::add_operation_button(GUI::Button& button, Calculator::Operation operation)
|
||||
{
|
||||
button.on_click = [this, operation](auto) {
|
||||
double argument = m_keypad.value();
|
||||
double res = m_calculator.begin_operation(operation, argument);
|
||||
KeypadValue argument = m_keypad.value();
|
||||
KeypadValue res = m_calculator.begin_operation(operation, argument);
|
||||
m_keypad.set_value(res);
|
||||
update_display();
|
||||
};
|
||||
|
@ -130,7 +131,7 @@ String CalculatorWidget::get_entry()
|
|||
return m_entry->text();
|
||||
}
|
||||
|
||||
void CalculatorWidget::set_entry(double value)
|
||||
void CalculatorWidget::set_entry(KeypadValue value)
|
||||
{
|
||||
m_keypad.set_value(value);
|
||||
update_display();
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#include "Calculator.h"
|
||||
#include "Keypad.h"
|
||||
#include "KeypadValue.h"
|
||||
#include <AK/Vector.h>
|
||||
#include <LibGUI/Widget.h>
|
||||
|
||||
|
@ -17,7 +18,7 @@ class CalculatorWidget final : public GUI::Widget {
|
|||
public:
|
||||
virtual ~CalculatorWidget() override;
|
||||
String get_entry();
|
||||
void set_entry(double);
|
||||
void set_entry(KeypadValue);
|
||||
|
||||
private:
|
||||
CalculatorWidget();
|
||||
|
|
|
@ -6,6 +6,8 @@
|
|||
*/
|
||||
|
||||
#include "Keypad.h"
|
||||
#include "KeypadValue.h"
|
||||
#include <AK/Math.h>
|
||||
#include <AK/StringBuilder.h>
|
||||
|
||||
Keypad::Keypad()
|
||||
|
@ -97,51 +99,29 @@ void Keypad::type_backspace()
|
|||
}
|
||||
}
|
||||
|
||||
double Keypad::value() const
|
||||
KeypadValue Keypad::value() const
|
||||
{
|
||||
double res = 0.0;
|
||||
|
||||
u64 frac = m_frac_value.value();
|
||||
for (int i = 0; i < m_frac_length; i++) {
|
||||
u8 digit = frac % 10;
|
||||
res += digit;
|
||||
res /= 10.0;
|
||||
frac /= 10;
|
||||
}
|
||||
|
||||
res += m_int_value.value();
|
||||
KeypadValue frac_part = { (i64)m_frac_value.value(), m_frac_length };
|
||||
KeypadValue int_part = { (i64)m_int_value.value() };
|
||||
KeypadValue res = int_part + frac_part;
|
||||
if (m_negative)
|
||||
res = -res;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void Keypad::set_value(double value)
|
||||
void Keypad::set_value(KeypadValue value)
|
||||
{
|
||||
m_state = State::External;
|
||||
|
||||
if (value < 0.0) {
|
||||
if (value.m_value < 0) {
|
||||
m_negative = true;
|
||||
value = -value;
|
||||
} else
|
||||
m_negative = false;
|
||||
|
||||
m_int_value = value;
|
||||
value -= m_int_value.value();
|
||||
|
||||
m_frac_value = 0;
|
||||
m_frac_length = 0;
|
||||
while (value != 0) {
|
||||
value *= 10.0;
|
||||
int digit = value;
|
||||
m_frac_value *= 10;
|
||||
m_frac_value += digit;
|
||||
m_frac_length++;
|
||||
value -= digit;
|
||||
|
||||
if (m_frac_length > 6)
|
||||
break;
|
||||
}
|
||||
m_int_value = value.m_value / (u64)AK::pow(10.0, (double)value.m_decimal_places);
|
||||
m_frac_value = value.m_value % (u64)AK::pow(10.0, (double)value.m_decimal_places);
|
||||
m_frac_length = value.m_decimal_places;
|
||||
}
|
||||
|
||||
String Keypad::to_string() const
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
|
||||
#pragma once
|
||||
|
||||
#include "KeypadValue.h"
|
||||
#include <AK/String.h>
|
||||
|
||||
// This type implements number typing and
|
||||
|
@ -22,8 +23,8 @@ public:
|
|||
void type_decimal_point();
|
||||
void type_backspace();
|
||||
|
||||
double value() const;
|
||||
void set_value(double);
|
||||
KeypadValue value() const;
|
||||
void set_value(KeypadValue);
|
||||
|
||||
String to_string() const;
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue