diff options
author | Lucas CHOLLET <lucas.chollet@free.fr> | 2022-01-12 11:05:03 +0100 |
---|---|---|
committer | Sam Atkins <atkinssj@gmail.com> | 2022-09-15 14:08:21 +0100 |
commit | 53eb35caba9ea5ffb21bc7117636adf938fa54c0 (patch) | |
tree | d7419768c8f429bdabd30ecd403c3fe77ff349b8 /Userland | |
parent | 4ab8ad2ed2817dd7bf92d819f58ed324654b6db7 (diff) | |
download | serenity-53eb35caba9ea5ffb21bc7117636adf938fa54c0.zip |
Calculator: Change internal representation to support perfect division
The purpose of this patch is to support addition, subtraction,
multiplication and division without using conversion to double. To this
end, we use the BigFraction class of LibCrypto. With this solution, we
can store values without any losses and forward rounding as the last
step before displaying.
Diffstat (limited to 'Userland')
-rw-r--r-- | Userland/Applications/Calculator/CMakeLists.txt | 1 | ||||
-rw-r--r-- | Userland/Applications/Calculator/Calculator.cpp | 54 | ||||
-rw-r--r-- | Userland/Applications/Calculator/Calculator.h | 26 | ||||
-rw-r--r-- | Userland/Applications/Calculator/CalculatorWidget.cpp | 24 | ||||
-rw-r--r-- | Userland/Applications/Calculator/CalculatorWidget.h | 4 | ||||
-rw-r--r-- | Userland/Applications/Calculator/Keypad.cpp | 122 | ||||
-rw-r--r-- | Userland/Applications/Calculator/Keypad.h | 19 | ||||
-rw-r--r-- | Userland/Applications/Calculator/KeypadValue.cpp | 162 | ||||
-rw-r--r-- | Userland/Applications/Calculator/KeypadValue.h | 48 | ||||
-rw-r--r-- | Userland/Applications/Calculator/main.cpp | 11 |
10 files changed, 110 insertions, 361 deletions
diff --git a/Userland/Applications/Calculator/CMakeLists.txt b/Userland/Applications/Calculator/CMakeLists.txt index 74a3d9a58c..8584cd905b 100644 --- a/Userland/Applications/Calculator/CMakeLists.txt +++ b/Userland/Applications/Calculator/CMakeLists.txt @@ -10,7 +10,6 @@ set(SOURCES Calculator.cpp CalculatorWidget.cpp Keypad.cpp - KeypadValue.cpp CalculatorGML.h ) diff --git a/Userland/Applications/Calculator/Calculator.cpp b/Userland/Applications/Calculator/Calculator.cpp index e5865d94f9..451aa5ea01 100644 --- a/Userland/Applications/Calculator/Calculator.cpp +++ b/Userland/Applications/Calculator/Calculator.cpp @@ -6,13 +6,13 @@ */ #include "Calculator.h" -#include "KeypadValue.h" #include <AK/Assertions.h> #include <AK/Math.h> +#include <LibCrypto/BigFraction/BigFraction.h> -KeypadValue Calculator::begin_operation(Operation operation, KeypadValue argument) +Crypto::BigFraction Calculator::begin_operation(Operation operation, Crypto::BigFraction argument) { - KeypadValue res = 0; + Crypto::BigFraction res {}; switch (operation) { case Operation::None: @@ -27,7 +27,7 @@ KeypadValue Calculator::begin_operation(Operation operation, KeypadValue argumen return argument; case Operation::Sqrt: - if (argument < 0) { + if (argument < Crypto::BigFraction {}) { m_has_error = true; return argument; } @@ -35,7 +35,7 @@ KeypadValue Calculator::begin_operation(Operation operation, KeypadValue argumen clear_operation(); break; case Operation::Inverse: - if (argument == 0) { + if (argument == Crypto::BigFraction {}) { m_has_error = true; return argument; } @@ -43,14 +43,14 @@ KeypadValue Calculator::begin_operation(Operation operation, KeypadValue argumen clear_operation(); break; case Operation::Percent: - res = argument * KeypadValue { 1, 2 }; // also known as `KeypadValue{0.01}` + res = argument * Crypto::BigFraction { 1, 100 }; break; case Operation::ToggleSign: res = -argument; break; case Operation::MemClear: - m_mem = 0; + m_mem.set_to_0(); res = argument; break; case Operation::MemRecall: @@ -66,15 +66,12 @@ KeypadValue Calculator::begin_operation(Operation operation, KeypadValue argumen break; } - if (should_be_rounded(res)) - round(res); - return res; } -KeypadValue Calculator::finish_operation(KeypadValue argument) +Crypto::BigFraction Calculator::finish_operation(Crypto::BigFraction argument) { - KeypadValue res = 0; + Crypto::BigFraction res {}; switch (m_operation_in_progress) { case Operation::None: @@ -90,7 +87,7 @@ KeypadValue Calculator::finish_operation(KeypadValue argument) res = m_saved_argument * argument; break; case Operation::Divide: - if (argument == 0) { + if (argument == Crypto::BigFraction {}) { m_has_error = true; return argument; } @@ -108,9 +105,6 @@ KeypadValue Calculator::finish_operation(KeypadValue argument) VERIFY_NOT_REACHED(); } - if (should_be_rounded(res)) - round(res); - clear_operation(); return res; } @@ -118,32 +112,6 @@ KeypadValue Calculator::finish_operation(KeypadValue argument) void Calculator::clear_operation() { m_operation_in_progress = Operation::None; - m_saved_argument = 0; + m_saved_argument.set_to_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 bbfcac5546..5d995bf7e1 100644 --- a/Userland/Applications/Calculator/Calculator.h +++ b/Userland/Applications/Calculator/Calculator.h @@ -7,7 +7,7 @@ #pragma once -#include "KeypadValue.h" +#include <LibCrypto/BigFraction/BigFraction.h> // This type implements the regular calculator // behavior, such as performing arithmetic @@ -39,8 +39,8 @@ public: MemAdd }; - KeypadValue begin_operation(Operation, KeypadValue); - KeypadValue finish_operation(KeypadValue); + Crypto::BigFraction begin_operation(Operation, Crypto::BigFraction); + Crypto::BigFraction finish_operation(Crypto::BigFraction); bool has_error() const { return m_has_error; } @@ -48,24 +48,8 @@ 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 }; + Crypto::BigFraction m_saved_argument {}; + Crypto::BigFraction m_mem {}; bool m_has_error { false }; }; diff --git a/Userland/Applications/Calculator/CalculatorWidget.cpp b/Userland/Applications/Calculator/CalculatorWidget.cpp index 887fd8fcb0..cfe111fe38 100644 --- a/Userland/Applications/Calculator/CalculatorWidget.cpp +++ b/Userland/Applications/Calculator/CalculatorWidget.cpp @@ -8,9 +8,9 @@ */ #include "CalculatorWidget.h" -#include "KeypadValue.h" #include <Applications/Calculator/CalculatorGML.h> #include <LibCore/Event.h> +#include <LibCrypto/BigFraction/BigFraction.h> #include <LibGUI/Button.h> #include <LibGUI/Label.h> #include <LibGUI/TextBox.h> @@ -50,14 +50,14 @@ CalculatorWidget::CalculatorWidget() m_clear_button = *find_descendant_of_type_named<GUI::Button>("clear_button"); m_clear_button->on_click = [this](auto) { - m_keypad.set_value(0.0); + m_keypad.set_to_0(); m_calculator.clear_operation(); update_display(); }; m_clear_error_button = *find_descendant_of_type_named<GUI::Button>("clear_error_button"); m_clear_error_button->on_click = [this](auto) { - m_keypad.set_value(0.0); + m_keypad.set_to_0(); update_display(); }; @@ -99,18 +99,18 @@ CalculatorWidget::CalculatorWidget() m_equals_button = *find_descendant_of_type_named<GUI::Button>("equal_button"); m_equals_button->on_click = [this](auto) { - KeypadValue argument = m_keypad.value(); - KeypadValue res = m_calculator.finish_operation(argument); - m_keypad.set_value(res); + Crypto::BigFraction argument = m_keypad.value(); + Crypto::BigFraction res = m_calculator.finish_operation(move(argument)); + m_keypad.set_value(move(res)); update_display(); }; } void CalculatorWidget::perform_operation(Calculator::Operation operation) { - KeypadValue argument = m_keypad.value(); - KeypadValue res = m_calculator.begin_operation(operation, argument); - m_keypad.set_value(res); + Crypto::BigFraction argument = m_keypad.value(); + Crypto::BigFraction res = m_calculator.begin_operation(operation, move(argument)); + m_keypad.set_value(move(res)); update_display(); } @@ -134,9 +134,9 @@ String CalculatorWidget::get_entry() return m_entry->text(); } -void CalculatorWidget::set_entry(KeypadValue value) +void CalculatorWidget::set_entry(Crypto::BigFraction value) { - m_keypad.set_value(value); + m_keypad.set_value(move(value)); update_display(); } @@ -167,7 +167,7 @@ void CalculatorWidget::keydown_event(GUI::KeyEvent& event) m_keypad.type_decimal_point(); mimic_pressed_button(m_decimal_point_button); } else if (event.key() == KeyCode::Key_Escape || event.key() == KeyCode::Key_Delete) { - m_keypad.set_value(0.0); + m_keypad.set_to_0(); m_calculator.clear_operation(); mimic_pressed_button(m_clear_button); } else if (event.key() == KeyCode::Key_Backspace) { diff --git a/Userland/Applications/Calculator/CalculatorWidget.h b/Userland/Applications/Calculator/CalculatorWidget.h index 282be01193..07845cd3df 100644 --- a/Userland/Applications/Calculator/CalculatorWidget.h +++ b/Userland/Applications/Calculator/CalculatorWidget.h @@ -10,8 +10,8 @@ #include "Calculator.h" #include "Keypad.h" -#include "KeypadValue.h" #include <AK/Vector.h> +#include <LibCrypto/BigFraction/BigFraction.h> #include <LibGUI/Widget.h> class CalculatorWidget final : public GUI::Widget { @@ -19,7 +19,7 @@ class CalculatorWidget final : public GUI::Widget { public: virtual ~CalculatorWidget() override = default; String get_entry(); - void set_entry(KeypadValue); + void set_entry(Crypto::BigFraction); private: CalculatorWidget(); diff --git a/Userland/Applications/Calculator/Keypad.cpp b/Userland/Applications/Calculator/Keypad.cpp index b600237929..b94fd79e2d 100644 --- a/Userland/Applications/Calculator/Keypad.cpp +++ b/Userland/Applications/Calculator/Keypad.cpp @@ -7,38 +7,31 @@ */ #include "Keypad.h" -#include "KeypadValue.h" -#include <AK/IntegralMath.h> #include <AK/StringBuilder.h> +#include <LibCrypto/BigFraction/BigFraction.h> +#include <LibCrypto/BigInt/UnsignedBigInteger.h> +#include <LibCrypto/NumberTheory/ModularFunctions.h> void Keypad::type_digit(int digit) { - u64 previous_value = 0; switch (m_state) { case State::External: m_state = State::TypingInteger; - m_negative = false; m_int_value = digit; - m_frac_value = 0; - m_frac_length = 0; + m_frac_value.set_to_0(); + m_frac_length.set_to_0(); break; case State::TypingInteger: - VERIFY(m_frac_value.value() == 0); + VERIFY(m_frac_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; + m_int_value.set_to(m_int_value.multiplied_by(10)); + m_int_value.set_to(m_int_value.plus(digit)); break; case State::TypingDecimal: - previous_value = m_frac_value.value(); - m_frac_value *= 10; - m_frac_value += digit; - if (m_frac_value.has_overflow()) - m_frac_value = previous_value; - else - m_frac_length++; + m_frac_value.set_to(m_frac_value.multiplied_by(10)); + m_frac_value.set_to(m_frac_value.plus(digit)); + + m_frac_length.set_to(m_frac_length.plus(1)); break; } } @@ -47,14 +40,13 @@ void Keypad::type_decimal_point() { switch (m_state) { case State::External: - m_negative = false; - m_int_value = 0; - m_frac_value = 0; - m_frac_length = 0; + m_int_value.set_to_0(); + m_frac_value.set_to_0(); + m_frac_length.set_to_0(); m_state = State::TypingDecimal; break; case State::TypingInteger: - VERIFY(m_frac_value.value() == 0); + VERIFY(m_frac_value == 0); VERIFY(m_frac_length == 0); m_state = State::TypingDecimal; break; @@ -68,70 +60,78 @@ void Keypad::type_backspace() { switch (m_state) { case State::External: - m_negative = false; - m_int_value = 0; - m_frac_value = 0; - m_frac_length = 0; + m_int_value.set_to_0(); + m_frac_value.set_to_0(); + m_frac_length.set_to_0(); break; case State::TypingDecimal: if (m_frac_length > 0) { - m_frac_value /= 10; - m_frac_length--; + m_frac_value.set_to(m_frac_value.divided_by(10).quotient); + m_frac_length.set_to(m_frac_length.minus(1)); break; } - VERIFY(m_frac_value.value() == 0); + VERIFY(m_frac_value == 0); m_state = State::TypingInteger; [[fallthrough]]; case State::TypingInteger: - VERIFY(m_frac_value.value() == 0); + VERIFY(m_frac_value == 0); VERIFY(m_frac_length == 0); - m_int_value /= 10; - if (m_int_value.value() == 0) - m_negative = false; + m_int_value.set_to(m_int_value.divided_by(10).quotient); break; } } -KeypadValue Keypad::value() const +Crypto::BigFraction Keypad::value() const { - 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; + if (m_state != State::External) { + Crypto::SignedBigInteger sum { m_int_value.multiplied_by(Crypto::NumberTheory::Power("10"_bigint, m_frac_length)).plus(m_frac_value) }; + Crypto::BigFraction res { move(sum), Crypto::NumberTheory::Power("10"_bigint, m_frac_length) }; + + m_internal_value = move(res); + } + + return m_internal_value; } -void Keypad::set_value(KeypadValue value) +void Keypad::set_value(Crypto::BigFraction value) { m_state = State::External; - if (value.m_value < 0) { - m_negative = true; - value = -value; - } else - m_negative = false; + m_internal_value = move(value); +} + +void Keypad::set_to_0() +{ + m_int_value.set_to_0(); + m_frac_value.set_to_0(); + m_frac_length.set_to_0(); - m_int_value = value.m_value / AK::pow<u64>(10, value.m_decimal_places); - m_frac_value = value.m_value % AK::pow<u64>(10, value.m_decimal_places); - m_frac_length = value.m_decimal_places; + m_internal_value.set_to_0(); + + m_state = State::External; } String Keypad::to_string() const { + // TODO: Implement custom rounding length in the calculator. + constexpr auto maximum_precision = 6; + if (m_state == State::External) + return m_internal_value.to_string(maximum_precision); + StringBuilder builder; - if (m_negative) - builder.append('-'); - 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) - builder.append('.'); + String const integer_value = m_int_value.to_base(10); + String const frac_value = m_frac_value.to_base(10); + unsigned const number_pre_zeros = m_frac_length.to_u64() - (frac_value.length() - 1) - (frac_value == "0" ? 0 : 1); + + builder.append(integer_value); - 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("{:0{}}"sv, m_frac_value.value(), m_frac_length); + // NOTE: We test for the state so the decimal point appears on screen as soon as you type it. + if (m_state == State::TypingDecimal) { + builder.append('.'); + builder.append_repeated('0', number_pre_zeros); + if (frac_value != "0") + builder.append(frac_value); } return builder.to_string(); diff --git a/Userland/Applications/Calculator/Keypad.h b/Userland/Applications/Calculator/Keypad.h index f542aa38bf..0410e31b51 100644 --- a/Userland/Applications/Calculator/Keypad.h +++ b/Userland/Applications/Calculator/Keypad.h @@ -7,8 +7,9 @@ #pragma once -#include "KeypadValue.h" #include <AK/String.h> +#include <LibCrypto/BigFraction/BigFraction.h> +#include <LibCrypto/BigInt/UnsignedBigInteger.h> // This type implements number typing and // displaying mechanics. It does not perform @@ -24,23 +25,27 @@ public: void type_decimal_point(); void type_backspace(); - KeypadValue value() const; - void set_value(KeypadValue); + Crypto::BigFraction value() const; + void set_value(Crypto::BigFraction); + void set_to_0(); String to_string() const; private: // Internal representation of the current decimal value. - bool m_negative { false }; - Checked<u64> m_int_value { 0 }; - Checked<u64> m_frac_value { 0 }; - u8 m_frac_length { 0 }; + // Those variables are only used when the user is entering a value. + // Otherwise, the BigFraction m_internal_value is used. + Crypto::UnsignedBigInteger m_int_value { 0 }; + Crypto::UnsignedBigInteger m_frac_value { 0 }; + Crypto::UnsignedBigInteger m_frac_length { 0 }; // E.g. for -35.004200, // m_negative = true // m_int_value = 35 // m_frac_value = 4200 // m_frac_length = 6 + mutable Crypto::BigFraction m_internal_value {}; + enum class State { External, TypingInteger, diff --git a/Userland/Applications/Calculator/KeypadValue.cpp b/Userland/Applications/Calculator/KeypadValue.cpp deleted file mode 100644 index 144e447ff6..0000000000 --- a/Userland/Applications/Calculator/KeypadValue.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#include "KeypadValue.h" -#include <AK/IntegralMath.h> -#include <AK/Math.h> -#include <AK/String.h> - -KeypadValue::KeypadValue(i64 value, u8 decimal_places) - : m_value(value) - , m_decimal_places(value == 0 ? 0 : decimal_places) -{ -} - -KeypadValue::KeypadValue(i64 value) - : m_value(value) -{ -} - -KeypadValue::KeypadValue(StringView sv) -{ - String str = sv.to_string(); // TODO: Once we have a StringView equivalent for this C API, we won't need to create a copy for this anymore. - size_t first_index = 0; - char* dot_ptr; - i64 int_part = strtoll(&str[first_index], &dot_ptr, 10); - size_t dot_index = dot_ptr - str.characters(); - if ((dot_index < str.length()) && (str[dot_index] == '.')) { - size_t second_index = dot_index + 1; - char* end_ptr; - i64 frac_part = strtoll(&str[second_index], &end_ptr, 10); - size_t end_index = end_ptr - str.characters(); - u8 frac_length = end_index - second_index; - *this = KeypadValue { int_part } + KeypadValue { frac_part, frac_length }; - } else { - *this = KeypadValue { int_part }; - } -}; - -KeypadValue KeypadValue::operator+(KeypadValue const& rhs) -{ - return operator_helper<KeypadValue>(*this, rhs, [](KeypadValue const&, KeypadValue const& more_decimal_places, i64 less_decimal_places_equalized, i64 more_decimal_places_equalized, bool) -> KeypadValue { - return { - more_decimal_places_equalized + less_decimal_places_equalized, - more_decimal_places.m_decimal_places - }; - }); -} - -KeypadValue KeypadValue::operator-(KeypadValue const& rhs) -{ - return *this + (-rhs); -} - -KeypadValue KeypadValue::operator*(KeypadValue const& rhs) -{ - return operator_helper<KeypadValue>(*this, rhs, [](KeypadValue const& less_decimal_places, KeypadValue const& more_decimal_places, i64, i64, bool) -> KeypadValue { - return { - less_decimal_places.m_value * more_decimal_places.m_value, - (u8)(less_decimal_places.m_decimal_places + more_decimal_places.m_decimal_places) - }; - }); -} - -KeypadValue KeypadValue::operator-(void) const -{ - return { -m_value, m_decimal_places }; -} - -KeypadValue KeypadValue::sqrt(void) const -{ - return KeypadValue { AK::sqrt((double)(*this)) }; -} - -KeypadValue KeypadValue::invert(void) const -{ - return KeypadValue { 1.0 / (double)(*this) }; -} - -KeypadValue KeypadValue::operator/(KeypadValue const& rhs) -{ - return KeypadValue { (double)(*this) / (double)rhs }; -} - -bool KeypadValue::operator<(KeypadValue const& rhs) -{ - return operator_helper<bool>(*this, rhs, [](KeypadValue const&, KeypadValue const&, i64 less_decimal_places_equalized, i64 more_decimal_places_equalized, bool lhs_is_less) { - if (lhs_is_less) - return (less_decimal_places_equalized < more_decimal_places_equalized); - else - return (more_decimal_places_equalized < less_decimal_places_equalized); - }); -} - -bool KeypadValue::operator==(KeypadValue const& rhs) -{ - return operator_helper<bool>(*this, rhs, [](KeypadValue const&, KeypadValue const&, i64 less_decimal_places_equalized, i64 more_decimal_places_equalized, bool) { - return less_decimal_places_equalized == more_decimal_places_equalized; - }); -} - -// This is a helper function for the operators. A lot of them need to do very similar calculations, so this function -// does the calculations for them and calls them on the result. In case they don't need the result of a particular -// calculation, they simply ignore that argument. -// The arguments to this function are the operands on the left- and right-hand sides and the callback to call on the -// values computed by this function. -// The first two KeypadValues it passes to the callback are the two original operands, but sorted by the amount of -// decimal places. -// The next two i64s it passes to the callback are these sorted KeypadValues, but normalized, which means that if -// you have for example 12.1 (represented as {121, 1}) and 54.23 (represented as {5423, 2}), you will get 1210 and -// 5423, so that you can compare these two i64s directly in order to compare the original KeypadValues. -// Unfortunately, not all operators are symmetric, so the last boolean tells the callback whether the left-hand side -// was the KeypadValue with less decimal places (true), or the one with more decimal places (false). -template<typename T, typename F> -ALWAYS_INLINE T KeypadValue::operator_helper(KeypadValue const& lhs, KeypadValue const& rhs, F callback) -{ - KeypadValue const& less_decimal_places = (lhs.m_decimal_places < rhs.m_decimal_places) ? lhs : rhs; - KeypadValue const& more_decimal_places = (lhs.m_decimal_places < rhs.m_decimal_places) ? rhs : lhs; - - i64 more_decimal_places_equalized = more_decimal_places.m_value; - i64 less_decimal_places_equalized = AK::pow<i64>(10, more_decimal_places.m_decimal_places - less_decimal_places.m_decimal_places) * less_decimal_places.m_value; - - bool lhs_is_less = (lhs.m_decimal_places < rhs.m_decimal_places); - - return callback(less_decimal_places, more_decimal_places, - less_decimal_places_equalized, more_decimal_places_equalized, - lhs_is_less); -} - -KeypadValue::KeypadValue(double d) -{ - bool negative = false; - if (d < 0) { - negative = true; - d = -d; - } - i8 current_pow = 0; - while (AK::pow(10.0, (double)current_pow) <= d) - current_pow += 1; - current_pow -= 1; - double epsilon = 1e-6; - while (d >= epsilon || current_pow >= 0) { - m_value *= 10; - i8 digit = (u64)(d * AK::pow(0.1, (double)current_pow)) % 10; - m_value += digit; - d -= digit * AK::pow(10.0, (double)current_pow); - if (current_pow < 0) - m_decimal_places += 1; - current_pow -= 1; - if (m_decimal_places > 6) - break; - } - m_value = negative ? (-m_value) : m_value; -} - -KeypadValue::operator double() const -{ - double res = (double)m_value / AK::pow(10.0, (double)m_decimal_places); - return res; -} diff --git a/Userland/Applications/Calculator/KeypadValue.h b/Userland/Applications/Calculator/KeypadValue.h deleted file mode 100644 index 3d63e0f27f..0000000000 --- a/Userland/Applications/Calculator/KeypadValue.h +++ /dev/null @@ -1,48 +0,0 @@ -/* - * Copyright (c) 2021, the SerenityOS developers. - * - * SPDX-License-Identifier: BSD-2-Clause - */ - -#pragma once - -#include <AK/Math.h> -#include <AK/String.h> -#include <AK/Types.h> - -class KeypadValue { - friend class Keypad; - friend class Calculator; - -public: - KeypadValue(i64, u8); - KeypadValue(i64); - - explicit KeypadValue(StringView); - - KeypadValue operator+(KeypadValue const&); - KeypadValue operator-(KeypadValue const&); - KeypadValue operator*(KeypadValue const&); - KeypadValue operator-(void) const; - bool operator<(KeypadValue const&); - bool operator==(KeypadValue const&); - - KeypadValue sqrt() const; - KeypadValue invert() const; - KeypadValue operator/(KeypadValue const&); - -private: - explicit KeypadValue(double); - explicit operator double() const; - - template<typename T, typename F> - T operator_helper(KeypadValue const& lhs, KeypadValue const& rhs, F callback); - - // This class represents a pair of a value together with the amount of decimal places that value is offset by. - // For example, if we were to represent the value -123.55 in this format, m_value would be -12355 and - // m_decimal_places would be 2, because when you shift -12355 2 digits to the right, you get -123.55. - // This way, most operations don't have to be performed on doubles, but can be performed without loss of - // precision on this class. - i64 m_value { 0 }; - u8 m_decimal_places { 0 }; -}; diff --git a/Userland/Applications/Calculator/main.cpp b/Userland/Applications/Calculator/main.cpp index 91035a66f5..0e6bf8c2f8 100644 --- a/Userland/Applications/Calculator/main.cpp +++ b/Userland/Applications/Calculator/main.cpp @@ -6,6 +6,7 @@ #include "CalculatorWidget.h" #include <LibCore/System.h> +#include <LibCrypto/NumberTheory/ModularFunctions.h> #include <LibGUI/Action.h> #include <LibGUI/Application.h> #include <LibGUI/Clipboard.h> @@ -51,20 +52,22 @@ ErrorOr<int> serenity_main(Main::Arguments arguments) auto clipboard = GUI::Clipboard::the().fetch_data_and_type(); if (clipboard.mime_type == "text/plain") { if (!clipboard.data.is_empty()) { - widget->set_entry(KeypadValue(StringView(clipboard.data))); + widget->set_entry(Crypto::BigFraction(StringView(clipboard.data))); } } })); auto& constants_menu = window->add_menu("&Constants"); + auto const power = Crypto::NumberTheory::Power("10"_bigint, "10"_bigint); + constants_menu.add_action(GUI::Action::create("&Pi", TRY(Gfx::Bitmap::try_load_from_file("/res/icons/calculator/pi.png"sv)), [&](auto&) { - widget->set_entry(KeypadValue { 31415926535, 10 }); + widget->set_entry(Crypto::BigFraction { Crypto::SignedBigInteger(31415926535), power }); })); constants_menu.add_action(GUI::Action::create("&Euler's Number", TRY(Gfx::Bitmap::try_load_from_file("/res/icons/calculator/eulers_number.png"sv)), [&](auto&) { - widget->set_entry(KeypadValue { 27182818284, 10 }); + widget->set_entry(Crypto::BigFraction { Crypto::SignedBigInteger(27182818284), power }); })); constants_menu.add_action(GUI::Action::create("&Phi", TRY(Gfx::Bitmap::try_load_from_file("/res/icons/calculator/phi.png"sv)), [&](auto&) { - widget->set_entry(KeypadValue { 16180339887, 10 }); + widget->set_entry(Crypto::BigFraction { Crypto::SignedBigInteger(16180339887), power }); })); auto& help_menu = window->add_menu("&Help"); |