From 219a80de8f183478f52248099b76dafd3315c19b Mon Sep 17 00:00:00 2001 From: wvpm <24685035+wvpm@users.noreply.github.com> Date: Mon, 11 May 2026 17:05:54 +0200 Subject: [PATCH] Minimise fixed point header --- src/openvic-simulation/core/Math.hpp | 40 +- src/openvic-simulation/core/MathSqrt.hpp | 31 ++ .../core/template/Concepts.hpp | 3 - .../country/CountryInstance.cpp | 35 +- .../economy/production/ArtisanalProducer.cpp | 19 +- .../production/ResourceGatheringOperation.cpp | 35 +- .../economy/trading/GoodMarket.cpp | 9 +- src/openvic-simulation/map/MapDefinition.cpp | 12 +- .../map/ProvinceDefinition.cpp | 6 +- .../pathfinding/PathingBase.hpp | 9 +- src/openvic-simulation/population/Pop.cpp | 9 +- .../types/fixed_point/FixedPoint.cpp | 300 +++++++++++- .../types/fixed_point/FixedPoint.hpp | 442 ++---------------- .../fixed_point/FixedPointStackString.hpp | 13 + .../types/fixed_point/Fraction.hpp | 5 +- .../types/fixed_point/Math.hpp | 105 +++++ tests/src/types/FixedPoint.cpp | 70 +-- tests/src/types/Vector2.cpp | 7 +- tests/src/types/Vector3.cpp | 7 +- tests/src/types/Vector4.cpp | 7 +- 20 files changed, 633 insertions(+), 531 deletions(-) create mode 100644 src/openvic-simulation/core/MathSqrt.hpp create mode 100644 src/openvic-simulation/types/fixed_point/FixedPointStackString.hpp create mode 100644 src/openvic-simulation/types/fixed_point/Math.hpp diff --git a/src/openvic-simulation/core/Math.hpp b/src/openvic-simulation/core/Math.hpp index 8050714c0..a6a39e01f 100644 --- a/src/openvic-simulation/core/Math.hpp +++ b/src/openvic-simulation/core/Math.hpp @@ -6,14 +6,15 @@ #include #include "openvic-simulation/core/Typedefs.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" namespace OpenVic { template - [[nodiscard]] OV_SPEED_INLINE constexpr T abs(T num); + [[nodiscard]] OV_SPEED_INLINE constexpr T abs(const T num); template requires std::integral || std::floating_point - [[nodiscard]] OV_SPEED_INLINE constexpr T abs(T num) { + [[nodiscard]] OV_SPEED_INLINE constexpr T abs(const T num) { if (std::is_constant_evaluated()) { return num < 0 ? -num : num; } else { @@ -21,9 +22,14 @@ namespace OpenVic { } } + template<> + [[nodiscard]] OV_SPEED_INLINE constexpr fixed_point_t abs(const fixed_point_t num) { + return num.abs(); + } + template - requires (!(std::integral || std::floating_point)) - [[nodiscard]] OV_SPEED_INLINE constexpr T abs(T num); + requires (!(std::integral || std::floating_point || std::is_same_v)) + [[nodiscard]] OV_SPEED_INLINE constexpr T abs(T const& num); template OV_SPEED_INLINE constexpr int64_t round_to_int64(T num) { @@ -58,32 +64,6 @@ namespace OpenVic { return ret; } - OV_SPEED_INLINE constexpr uint64_t sqrt(uint64_t n) { - uint64_t x = n; - uint64_t c = 0; - uint64_t d = 1ull << 62; - - while (d > n) { - d >>= 2; - } - - for (; d != 0; d >>= 2) { - if (x >= c + d) { - x -= c + d; - c = (c >> 1) + d; - } else { - c >>= 1; - } - } - - // round up - if (x > 0) { - c += 1; - } - - return c; - } - OV_SPEED_INLINE constexpr bool is_power_of_two(uint64_t n) { return n > 0 && (n & (n - 1)) == 0; } diff --git a/src/openvic-simulation/core/MathSqrt.hpp b/src/openvic-simulation/core/MathSqrt.hpp new file mode 100644 index 000000000..4c365bf75 --- /dev/null +++ b/src/openvic-simulation/core/MathSqrt.hpp @@ -0,0 +1,31 @@ +#pragma once + +#include + +namespace OpenVic { + inline constexpr uint64_t sqrt(uint64_t n) { + uint64_t x = n; + uint64_t c = 0; + uint64_t d = 1ull << 62; + + while (d > n) { + d >>= 2; + } + + for (; d != 0; d >>= 2) { + if (x >= c + d) { + x -= c + d; + c = (c >> 1) + d; + } else { + c >>= 1; + } + } + + // round up + if (x > 0) { + c += 1; + } + + return c; + } +} \ No newline at end of file diff --git a/src/openvic-simulation/core/template/Concepts.hpp b/src/openvic-simulation/core/template/Concepts.hpp index dd4fba1e7..d21d92c12 100644 --- a/src/openvic-simulation/core/template/Concepts.hpp +++ b/src/openvic-simulation/core/template/Concepts.hpp @@ -144,9 +144,6 @@ namespace OpenVic { { typename Case::equal {}(identifier, identifier) } -> std::same_as; }; - template - concept integral_max_size_4 = std::integral && sizeof(T) <= 4; - template concept unary_negatable = requires(T const& a) { { -a } -> std::same_as; diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp index 341247069..818610c0f 100644 --- a/src/openvic-simulation/country/CountryInstance.cpp +++ b/src/openvic-simulation/country/CountryInstance.cpp @@ -45,6 +45,7 @@ #include "openvic-simulation/types/ClampedValue.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/fixed_point/Math.hpp" #include "openvic-simulation/types/IndexedFlatMap.hpp" #include "openvic-simulation/population/PopSize.hpp" #include "openvic-simulation/population/PopSum.hpp" @@ -1331,7 +1332,7 @@ void CountryInstance::_update_production() { for (auto const& [country, money_invested] : foreign_investments) { if (country.get().exists()) { - const fixed_point_t investment_industrial_power = fixed_point_t::mul_div( + const fixed_point_t investment_industrial_power = fp::mul_div( money_invested, country_defines.get_country_investment_industrial_score_factor(), 100 @@ -1425,13 +1426,13 @@ void CountryInstance::_update_budget() { administrative_efficiency_from_administrators.set(fixed_point_t::_1); administrator_percentage.set(fixed_point_t::_0); } else { - administrator_percentage.set(fixed_point_t::from_fraction(administrators, total_non_colonial_population)); + administrator_percentage.set(fp::from_fraction(administrators, total_non_colonial_population)); - const pop_sum_t desired_administrators = fixed_point_t::multiply_truncate( + const pop_sum_t desired_administrators = fp::multiply_truncate( total_non_colonial_population, desired_administrator_percentage.get_untracked() ); - const pop_sum_t effective_administrators = fixed_point_t::multiply_truncate( + const pop_sum_t effective_administrators = fp::multiply_truncate( administrators, fixed_point_t::_1 + get_modifier_effect_value(*modifier_effect_cache.get_administrative_efficiency()) ); @@ -1439,7 +1440,7 @@ void CountryInstance::_update_budget() { desired_administrators == 0 ? fixed_point_t::_1 : std::min( - fixed_point_t::from_fraction(effective_administrators, desired_administrators) + fp::from_fraction(effective_administrators, desired_administrators) * (fixed_point_t::_1 + get_modifier_effect_value(*modifier_effect_cache.get_administrative_efficiency_modifier())), fixed_point_t::_1 ); @@ -1564,14 +1565,14 @@ void CountryInstance::_update_population() { for (auto const& [pop_type, pop_size] : get_population_by_type()) { if (pop_type.research_leadership_optimum > 0 && pop_size > 0) { - const pop_sum_t optimum_size = fixed_point_t::multiply_truncate( + const pop_sum_t optimum_size = fp::multiply_truncate( get_total_population(), pop_type.research_leadership_optimum ); const fixed_point_t factor = optimum_size == 0 ? fixed_point_t::_1 : std::min( - fixed_point_t::from_fraction( + fp::from_fraction( pop_size, optimum_size ), fixed_point_t::_1 @@ -1638,7 +1639,7 @@ void CountryInstance::_update_military() { } military_power_from_land.set( - supply_consumption * fixed_point_t::mul_div( + supply_consumption * fp::mul_div( sum_of_regiment_type_stats, fixed_point_t::parse_raw(regular_army_size), fixed_point_t::parse_raw(7 * (1 + unit_type_manager.get_regiment_type_count())) @@ -1682,7 +1683,7 @@ void CountryInstance::_update_military() { // TODO - use country_defines.get_min_mobilize_limit(); (wiki: "lowest maximum of brigades you can mobilize. (by default 3)") mobilisation_max_regiment_count = regiment_count - + fixed_point_t::multiply_truncate(regiment_count, mobilisation_impact); + + fp::multiply_truncate(regiment_count, mobilisation_impact); mobilisation_potential_regiment_count = 0; // TODO - calculate max regiments from poor citizens if (mobilisation_potential_regiment_count > mobilisation_max_regiment_count) { @@ -1709,7 +1710,7 @@ void CountryInstance::_update_military() { naval_unit_start_experience += get_modifier_effect_value(*modifier_effect_cache.get_naval_unit_start_experience()); recruit_time = fixed_point_t::_1 + get_modifier_effect_value(*modifier_effect_cache.get_unit_recruitment_time()); - combat_width = fixed_point_t::multiply_truncate( + combat_width = fp::multiply_truncate( military_defines.get_base_combat_width(), get_modifier_effect_value(*modifier_effect_cache.get_combat_width_additive()) ); @@ -2213,7 +2214,7 @@ void CountryInstance::manage_national_stockpile( const fixed_point_t weight = weights[good_index]; const fixed_point_t max_costs = max_costs_per_good[good_index]; - fixed_point_t cash_available_for_good = fixed_point_t::mul_div( + fixed_point_t cash_available_for_good = fp::mul_div( cash_left_to_spend_draft, weight, weights_sum @@ -2470,7 +2471,7 @@ void CountryInstance::request_salaries_and_welfare_and_import_subsidies(Pop& pop SharedPopTypeValues const& pop_type_values = shared_country_values.get_shared_pop_type_values(pop_type); if (actual_administration_budget > 0) { - const fixed_point_t administration_salary = fixed_point_t::mul_div( + const fixed_point_t administration_salary = fp::mul_div( pop_size * administration_salary_base_by_pop_type.at(pop_type).get_untracked(), actual_administration_budget, projected_administration_spending_unscaled_by_slider.get_untracked() @@ -2482,7 +2483,7 @@ void CountryInstance::request_salaries_and_welfare_and_import_subsidies(Pop& pop } if (actual_education_budget > 0) { - const fixed_point_t education_salary = fixed_point_t::mul_div( + const fixed_point_t education_salary = fp::mul_div( pop_size * education_salary_base_by_pop_type.at(pop_type).get_untracked(), actual_education_budget, projected_education_spending_unscaled_by_slider.get_untracked() @@ -2494,7 +2495,7 @@ void CountryInstance::request_salaries_and_welfare_and_import_subsidies(Pop& pop } if (actual_military_budget > 0) { - const fixed_point_t military_salary = fixed_point_t::mul_div( + const fixed_point_t military_salary = fp::mul_div( pop_size * military_salary_base_by_pop_type.at(pop_type).get_untracked(), actual_military_budget, projected_military_spending_unscaled_by_slider.get_untracked() @@ -2506,7 +2507,7 @@ void CountryInstance::request_salaries_and_welfare_and_import_subsidies(Pop& pop } if (actual_social_budget > 0) { - const fixed_point_t pension_income = fixed_point_t::mul_div( + const fixed_point_t pension_income = fp::mul_div( pop_size * calculate_pensions_base(pop_type), actual_social_budget, projected_social_spending_unscaled_by_slider.get_untracked() @@ -2516,7 +2517,7 @@ void CountryInstance::request_salaries_and_welfare_and_import_subsidies(Pop& pop actual_pensions_spending += pension_income; } - const fixed_point_t unemployment_subsidies = fixed_point_t::mul_div( + const fixed_point_t unemployment_subsidies = fp::mul_div( pop.get_unemployed() * calculate_unemployment_subsidies_base(pop_type), actual_social_budget, projected_social_spending_unscaled_by_slider.get_untracked() @@ -2528,7 +2529,7 @@ void CountryInstance::request_salaries_and_welfare_and_import_subsidies(Pop& pop } if (actual_import_subsidies_budget > 0) { - const fixed_point_t import_subsidies = fixed_point_t::mul_div( + const fixed_point_t import_subsidies = fp::mul_div( effective_tariff_rate.get_untracked() // < 0 * pop.get_yesterdays_import_value().get_copy_of_value(), actual_import_subsidies_budget, // < 0 diff --git a/src/openvic-simulation/economy/production/ArtisanalProducer.cpp b/src/openvic-simulation/economy/production/ArtisanalProducer.cpp index fef8535e2..18c4db0b8 100644 --- a/src/openvic-simulation/economy/production/ArtisanalProducer.cpp +++ b/src/openvic-simulation/economy/production/ArtisanalProducer.cpp @@ -21,6 +21,7 @@ #include "openvic-simulation/population/Pop.hpp" #include "openvic-simulation/population/PopValuesFromProvince.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/fixed_point/Math.hpp" using namespace OpenVic; @@ -231,7 +232,7 @@ void ArtisanalProducer::artisan_tick_handler::allocate_money_for_inputs( } const ptrdiff_t i = it - input_goods.begin(); - const fixed_point_t optimal_quantity = fixed_point_t::mul_div( + const fixed_point_t optimal_quantity = fp::mul_div( demand_per_input[i], max_possible_satisfaction_numerator, max_possible_satisfaction_denominator @@ -259,7 +260,7 @@ void ArtisanalProducer::artisan_tick_handler::allocate_money_for_inputs( const fixed_point_t max_quantity_to_buy = good_demand - stockpiled_quantity; if (max_quantity_to_buy > 0) { - const fixed_point_t optimal_quantity = fixed_point_t::mul_div( + const fixed_point_t optimal_quantity = fp::mul_div( good_demand, max_possible_satisfaction_numerator, max_possible_satisfaction_denominator @@ -490,11 +491,15 @@ fixed_point_t ArtisanalProducer::calculate_production_type_score( static_assert(0 <= k); static_assert(k <= 1); - return ( - k * fixed_point_t::mul_div(costs, costs, revenue) - -(1+k)*costs - + revenue - ).mul_div(Pop::size_denominator, workforce); //factor out pop size without making values too small + return fp::mul_div( + ( + k * fp::mul_div(costs, costs, revenue) + -(1+k)*costs + + revenue + ), + Pop::size_denominator, + workforce + ); //factor out pop size without making values too small } ProductionType const* ArtisanalProducer::pick_production_type( diff --git a/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp b/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp index 2ac1e6dd3..6cf48cfe1 100644 --- a/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp +++ b/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp @@ -10,10 +10,11 @@ #include "openvic-simulation/map/State.hpp" #include "openvic-simulation/modifier/ModifierEffectCache.hpp" #include "openvic-simulation/population/Pop.hpp" -#include "openvic-simulation/population/PopType.hpp" -#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/population/PopSize.hpp" #include "openvic-simulation/population/PopSum.hpp" +#include "openvic-simulation/population/PopType.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/fixed_point/Math.hpp" #include "openvic-simulation/types/TypedIndices.hpp" #include "openvic-simulation/utility/Logger.hpp" #include "openvic-simulation/utility/Containers.hpp" @@ -80,7 +81,7 @@ void ResourceGatheringOperation::initialise_rgo_size_multiplier() { } else { size_multiplier = ( ( - fixed_point_t::from_fraction( + fp::from_fraction( total_worker_count_in_province, base_workforce_size ) / size_modifier @@ -198,7 +199,7 @@ void ResourceGatheringOperation::hire() { proportion_to_hire = 1; } else { //hire all pops proportionally - proportion_to_hire = fixed_point_t::from_fraction(max_employee_count_cache, available_worker_count); + proportion_to_hire = fp::from_fraction(max_employee_count_cache, available_worker_count); } std::span jobs = production_type.get_jobs(); @@ -256,13 +257,15 @@ fixed_point_t ResourceGatheringOperation::produce() { if (total_owner_count_in_state_cache > 0) { switch (owner_job.effect_type) { case Job::effect_t::OUTPUT: - output_multiplier += owner_job.effect_multiplier.mul_div( + output_multiplier += fp::mul_div( + owner_job.effect_multiplier, total_owner_count_in_state_cache, state_population ); break; case Job::effect_t::THROUGHPUT: - throughput_multiplier += owner_job.effect_multiplier.mul_div( + throughput_multiplier += fp::mul_div( + owner_job.effect_multiplier, total_owner_count_in_state_cache, state_population ); @@ -323,9 +326,9 @@ fixed_point_t ResourceGatheringOperation::produce() { const fixed_point_t effect_multiplier = job.effect_multiplier; const fixed_point_t amount = job.amount; const fixed_point_t effect = effect_multiplier != fixed_point_t::_1 - && fixed_point_t::from_fraction(employees_of_type, max_employee_count_cache) > amount + && fp::from_fraction(employees_of_type, max_employee_count_cache) > amount ? effect_multiplier * amount //special Vic2 logic - : effect_multiplier.mul_div(employees_of_type, max_employee_count_cache); + : fp::mul_div(effect_multiplier, employees_of_type, max_employee_count_cache); switch (job.effect_type) { case Job::effect_t::OUTPUT: @@ -377,7 +380,7 @@ void ResourceGatheringOperation::pay_employees(memory::vector& re if (revenue <= total_minimum_wage) { for (Employee& employee : employees) { const fixed_point_t income_for_this_pop = std::max( - fixed_point_t::mul_div( + fp::mul_div( revenue, employee.get_minimum_wage_cached(), total_minimum_wage @@ -396,13 +399,17 @@ void ResourceGatheringOperation::pay_employees(memory::vector& re fixed_point_t::_1 - total_minimum_wage / revenue_left ); const fixed_point_t owner_share = std::min( - fixed_point_t::from_fraction(2 * total_owner_count_in_state_cache, total_worker_count_in_province_cache), + fp::from_fraction(2 * total_owner_count_in_state_cache, total_worker_count_in_province_cache), upper_limit ); for (Pop& owner_pop : *owner_pops_cache_nullable) { const fixed_point_t income_for_this_pop = std::max( - revenue_left * owner_share.mul_div(owner_pop.get_size(), total_owner_count_in_state_cache), + revenue_left * fp::mul_div( + owner_share, + owner_pop.get_size(), + total_owner_count_in_state_cache + ), fixed_point_t::epsilon //revenue > 0 is already checked, so rounding up ); owner_pop.add_rgo_owner_income(income_for_this_pop); @@ -435,7 +442,11 @@ void ResourceGatheringOperation::pay_employees(memory::vector& re const pop_size_t employee_size = employee.get_size(); const fixed_point_t income_for_this_pop = std::max( - revenue_left.mul_div(employee_size, count_workers_to_be_paid), + fp::mul_div( + revenue_left, + employee_size, + count_workers_to_be_paid + ), fixed_point_t::epsilon //revenue > 0 is already checked, so rounding up ); diff --git a/src/openvic-simulation/economy/trading/GoodMarket.cpp b/src/openvic-simulation/economy/trading/GoodMarket.cpp index 8a735c169..7b721307c 100644 --- a/src/openvic-simulation/economy/trading/GoodMarket.cpp +++ b/src/openvic-simulation/economy/trading/GoodMarket.cpp @@ -6,6 +6,7 @@ #include "openvic-simulation/economy/trading/MarketSellOrder.hpp" #include "openvic-simulation/misc/GameRulesManager.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/fixed_point/Math.hpp" #include "openvic-simulation/utility/Containers.hpp" using namespace OpenVic; @@ -200,7 +201,7 @@ void GoodMarket::execute_orders( actual_bought_per_country[country_index_optional.value()] -= distributed_supply; } - distributed_supply = fixed_point_t::mul_div( + distributed_supply = fp::mul_div( remaining_supply, purchasing_power_per_order[i], purchasing_power_sum @@ -351,7 +352,7 @@ void GoodMarket::execute_orders( const fixed_point_t total_domestic_supply = supply_per_country[country_index]; quantity_sold_domestically = total_bought_domestically >= total_domestic_supply ? quantity_offered - : fixed_point_t::mul_div( + : fp::mul_div( quantity_offered, total_bought_domestically, total_domestic_supply //> 0 as we're selling @@ -359,7 +360,7 @@ void GoodMarket::execute_orders( quantity_offered_as_export = quantity_offered - quantity_sold_domestically; } - const fixed_point_t fair_share_of_exports = fixed_point_t::mul_div( + const fixed_point_t fair_share_of_exports = fp::mul_div( quantity_offered_as_export, total_quantity_traded_as_export, total_quantity_offered_as_export @@ -438,7 +439,7 @@ void GoodMarket::execute_buy_orders( //no imports money_spent_on_imports = 0; } else { - const fixed_point_t money_spent_domestically = fixed_point_t::mul_div( + const fixed_point_t money_spent_domestically = fp::mul_div( money_spent_total, supply_in_my_country, actual_bought_in_my_country diff --git a/src/openvic-simulation/map/MapDefinition.cpp b/src/openvic-simulation/map/MapDefinition.cpp index a50ae778b..fb940c52b 100644 --- a/src/openvic-simulation/map/MapDefinition.cpp +++ b/src/openvic-simulation/map/MapDefinition.cpp @@ -17,18 +17,20 @@ #include +#include "openvic-simulation/core/FormatValidate.hpp" #include "openvic-simulation/core/io/BMP.hpp" +#include "openvic-simulation/core/string/CharConv.hpp" +#include "openvic-simulation/core/Typedefs.hpp" #include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/map/ProvinceDefinition.hpp" #include "openvic-simulation/modifier/ModifierManager.hpp" #include "openvic-simulation/types/Colour.hpp" +#include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/fixed_point/Math.hpp" #include "openvic-simulation/types/OrderedContainersMath.hpp" #include "openvic-simulation/types/TypedSpan.hpp" #include "openvic-simulation/types/Vector.hpp" -#include "openvic-simulation/core/FormatValidate.hpp" #include "openvic-simulation/utility/Logger.hpp" -#include "openvic-simulation/core/string/CharConv.hpp" -#include "openvic-simulation/core/Typedefs.hpp" #include "openvic-simulation/utility/Containers.hpp" using namespace OpenVic; @@ -111,7 +113,9 @@ ProvinceDefinition::distance_t MapDefinition::calculate_distance_between( ) ); - return fvec2_t { min_x, to_pos.y - from_pos.y }.length_squared().sqrt(); + return fp::sqrt( + fvec2_t { min_x, to_pos.y - from_pos.y }.length_squared() + ); } using adjacency_t = ProvinceDefinition::adjacency_t; diff --git a/src/openvic-simulation/map/ProvinceDefinition.cpp b/src/openvic-simulation/map/ProvinceDefinition.cpp index 78f9b2751..d63cf1840 100644 --- a/src/openvic-simulation/map/ProvinceDefinition.cpp +++ b/src/openvic-simulation/map/ProvinceDefinition.cpp @@ -5,6 +5,7 @@ #include "openvic-simulation/dataloader/NodeTools.hpp" #include "openvic-simulation/economy/BuildingType.hpp" #include "openvic-simulation/map/MapDefinition.hpp" +#include "openvic-simulation/types/fixed_point/Math.hpp" #include "openvic-simulation/utility/Containers.hpp" using namespace OpenVic; @@ -64,7 +65,10 @@ bool ProvinceDefinition::load_positions( const fixed_point_t rotation = get_building_rotation(building_type_manager.get_port_building_type()); /* At 0 rotation the port faces west, as rotation increases the port rotates anti-clockwise. */ - const fvec2_t port_dir { -rotation.cos(), rotation.sin() }; + const fvec2_t port_dir { + fp::cos(-rotation), + fp::sin(rotation) + }; const ivec2_t port_facing_position = static_cast(*port_position + port_dir / 4); ProvinceDefinition const* province_ptr = map_definition.get_province_definition_at(port_facing_position); diff --git a/src/openvic-simulation/pathfinding/PathingBase.hpp b/src/openvic-simulation/pathfinding/PathingBase.hpp index 3ab4f0bf4..1b5bf129f 100644 --- a/src/openvic-simulation/pathfinding/PathingBase.hpp +++ b/src/openvic-simulation/pathfinding/PathingBase.hpp @@ -8,6 +8,7 @@ #include "openvic-simulation/pathfinding/PointMap.hpp" #include "openvic-simulation/types/Signal.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/fixed_point/Math.hpp" #include "openvic-simulation/utility/Containers.hpp" #include @@ -64,11 +65,15 @@ namespace OpenVic { } virtual fixed_point_t _estimate_cost(search_const_iterator from_it, search_const_iterator end_it) { - return fixed_point_t { from_it.value().point->position.distance_squared(end_it.value().point->position) }.sqrt(); + return fp::sqrt( + fixed_point_t { from_it.value().point->position.distance_squared(end_it.value().point->position) } + ); } virtual fixed_point_t _compute_cost(search_const_iterator from_it, search_const_iterator end_it) { - return fixed_point_t { from_it.value().point->position.distance_squared(end_it.value().point->position) }.sqrt(); + return fp::sqrt( + fixed_point_t { from_it.value().point->position.distance_squared(end_it.value().point->position) } + ); } virtual bool _solve( // diff --git a/src/openvic-simulation/population/Pop.cpp b/src/openvic-simulation/population/Pop.cpp index a180c5d47..32c317917 100644 --- a/src/openvic-simulation/population/Pop.cpp +++ b/src/openvic-simulation/population/Pop.cpp @@ -37,6 +37,7 @@ #include "openvic-simulation/population/Religion.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/fixed_point/FixedPointMap.hpp" +#include "openvic-simulation/types/fixed_point/Math.hpp" #include "openvic-simulation/types/IndexedFlatMap.hpp" #include "openvic-simulation/types/OrderedContainers.hpp" #include "openvic-simulation/types/TypedIndices.hpp" @@ -77,7 +78,7 @@ fixed_point_t Pop::get_unemployment_fraction() const { if (!get_type().can_be_unemployed) { return 0; } - return fixed_point_t::from_fraction(get_unemployed(), size); + return fp::from_fraction(get_unemployed(), size); } void Pop::setup_pop_test_values(IssueManager const& issue_manager) { @@ -422,7 +423,7 @@ void Pop::allocate_for_needs( } fixed_point_t weight = market_instance.get_good_instance(good_definition).get_price_inverse(); - fixed_point_t cash_available_for_good = fixed_point_t::mul_div( + fixed_point_t cash_available_for_good = fp::mul_div( cash_left_to_spend_draft, weight, weights_sum @@ -676,7 +677,7 @@ void Pop::after_buy(void* actor, BuyResult const& buy_result) { if (quantity_added_to_stockpile > 0) { quantity_left_to_consume -= quantity_added_to_stockpile; - const fixed_point_t expense = fixed_point_t::mul_div( + const fixed_point_t expense = fp::mul_div( money_spent, quantity_added_to_stockpile, quantity_bought @@ -705,7 +706,7 @@ void Pop::after_buy(void* actor, BuyResult const& buy_result) { if (get_country_to_report_economy_nullable != nullptr) { \ get_country_to_report_economy_nullable->report_pop_need_consumption(pop.type, good_definition, consumed_quantity); \ } \ - const fixed_point_t expense = fixed_point_t::mul_div( \ + const fixed_point_t expense = fp::mul_div( \ money_spent, \ consumed_quantity, \ quantity_bought \ diff --git a/src/openvic-simulation/types/fixed_point/FixedPoint.cpp b/src/openvic-simulation/types/fixed_point/FixedPoint.cpp index 80cf69b85..1ca61c324 100644 --- a/src/openvic-simulation/types/fixed_point/FixedPoint.cpp +++ b/src/openvic-simulation/types/fixed_point/FixedPoint.cpp @@ -1,11 +1,38 @@ #include "FixedPoint.hpp" +#include "FixedPointStackString.hpp" + +#include +#include #include #include +#include "openvic-simulation/core/string/CharConv.hpp" #include "openvic-simulation/core/Typedefs.hpp" +#include "openvic-simulation/utility/Logger.hpp" + +/* Base e exponential lookup table */ +#include "openvic-simulation/types/fixed_point/FixedPointLUT_2_16_EXP_e.hpp" + +/* Base 2001 exponential lookup table */ +#include "openvic-simulation/types/fixed_point/FixedPointLUT_2_16_EXP_2001.hpp" using namespace OpenVic; +static_assert(_detail::LUT::_2_16_EXP_e_DIVISOR == 1 << fixed_point_t::PRECISION); +static_assert(_detail::LUT::_2_16_EXP_2001_DIVISOR == 1 << fixed_point_t::PRECISION); + +OV_SPEED_INLINE void fixed_point_t::warn_if_truncated() const { + if (OV_unlikely( + value != 0 + && abs() < fixed_point_t::_1 + )) { + spdlog::warn_s( + "0 < abs(Fixed point) < 1, truncation will result in zero, this may be a bug. raw_value: {} as float: {}", + get_raw_value(), + static_cast(*this) + ); + } +} fmt::format_context::iterator fmt::formatter::format(fixed_point_t fp, format_context& ctx) const { format_specs specs { _specs }; @@ -14,7 +41,7 @@ fmt::format_context::iterator fmt::formatter::format(fixed_point_ detail::handle_dynamic_spec(_specs.dynamic_width(), specs.width, _specs.width_ref, ctx); } - fixed_point_t::stack_string result = fp.to_array(specs.precision); + fp_stack_string result = fp.to_array(specs.precision); if (OV_unlikely(result.empty())) { return ctx.out(); } @@ -52,3 +79,274 @@ fmt::format_context::iterator fmt::formatter::format(fixed_point_ view = { buffer.data(), buffer.size() }; return detail::write(out, view, specs, ctx.locale()); } + +std::to_chars_result fixed_point_t::to_chars(char* first, char* last, size_t decimal_places) const { + if (first == nullptr || first >= last) { + return { last, std::errc::value_too_large }; + } + + if (is_negative()) { + *first = '-'; + ++first; + if (last - first <= 1) { + return { last, std::errc::value_too_large }; + } + } + + std::to_chars_result result {}; + if (decimal_places == static_cast(-1)) { + result = OpenVic::to_chars(first, last, abs().unsafe_truncate()); + if (OV_unlikely(result.ec != std::errc {})) { + return result; + } + if (OV_unlikely(last - result.ptr <= 1)) { + return result; + } + + fixed_point_t frac = abs().get_frac(); + if (frac != _0) { + *result.ptr = '.'; + ++result.ptr; + do { + if (OV_unlikely(last - result.ptr <= 1)) { + return { last, std::errc::value_too_large }; + } + frac *= 10; + *result.ptr = static_cast('0' + frac.unsafe_truncate()); + ++result.ptr; + frac = frac.get_frac(); + } while (frac != _0); + } + return result; + } + + // Add the specified number of decimal places, potentially 0 (so no decimal point) + fixed_point_t err = _0_50; + for (size_t i = decimal_places; i > 0; --i) { + err /= 10; + } + fixed_point_t val = this->abs() + err; + + + result = OpenVic::to_chars(first, last, val.unsafe_truncate()); + if (OV_unlikely(result.ec != std::errc {})) { + return result; + } + + if (decimal_places > 0) { + if (OV_unlikely(last - result.ptr <= 1)) { + return result; + } + val = val.get_frac(); + *result.ptr = '.'; + result.ptr++; + do { + if (OV_unlikely(last - result.ptr <= 1)) { + return result; + } + val *= 10; + *result.ptr = static_cast('0' + val.unsafe_truncate()); + ++result.ptr; + val = val.get_frac(); + } while (--decimal_places > 0); + } + return result; +} + +fp_stack_string fixed_point_t::to_array(size_t decimal_places) const { + fp_stack_string str {}; + std::to_chars_result result = to_chars(str._array.data(), str._array.data() + str._array.size(), decimal_places); + str._string_size = result.ptr - str.data(); + return str; +} + +namespace OpenVic { + std::ostream& operator<<(std::ostream& stream, fixed_point_t const& obj) { + fp_stack_string result = obj.to_array(); + if (OV_unlikely(result.empty())) { + return stream; + } + + return stream << static_cast(result); + } +} + +std::from_chars_result fixed_point_t::from_chars(char const* begin, char const* end) { + if (begin == nullptr || begin >= end) { + return { begin, std::errc::invalid_argument }; + } + + if (char const& c = *(end - 1); c == 'f' || c == 'F') { + --end; + if (begin == end) { + return { begin, std::errc::invalid_argument }; + } + } + + char const* dot_pointer = begin; + while (*dot_pointer != '.' && ++dot_pointer != end) {} + // "." + if (dot_pointer == begin && dot_pointer + 1 == end) { + return { begin, std::errc::invalid_argument }; + } + + fixed_point_t result = 0; + std::from_chars_result from_chars = {}; + if (dot_pointer != begin) { + // Non-empty integer part, may be negative + from_chars = from_chars_integer(begin, dot_pointer, result); + } + + if (from_chars.ec != std::errc{}) { + return from_chars; + } + + if (dot_pointer + 1 < end) { + // Non-empty fractional part, cannot be negative + fixed_point_t adder; + from_chars = from_chars_fraction(dot_pointer + 1, end, adder); + result += result.is_negative() || (*begin == '-' && result == _0) ? -adder : adder; + } + + if (from_chars.ec != std::errc{}) { + return { begin, from_chars.ec }; + } + + value = result.value; + return from_chars; +} + +std::from_chars_result fixed_point_t::from_chars_with_plus(char const* begin, char const* end) { + if (begin && *begin == '+') { + begin++; + if (begin < end && *begin == '-') { + return std::from_chars_result { begin, std::errc::invalid_argument }; + } + } + + return from_chars(begin, end); +} + +std::from_chars_result fixed_point_t::from_chars_integer(char const* str, char const* const end, fixed_point_t& value) { + int64_t parsed_value = 0; + std::from_chars_result result = string_to_int64(str, end, parsed_value); + if (result.ec == std::errc{}) { + if (parsed_value > std::numeric_limits::max()) { + result.ec = std::errc::value_too_large; + } else { + value = fixed_point_t(static_cast(parsed_value)); + } + } + return result; +} + +std::from_chars_result fixed_point_t::from_chars_fraction(char const* begin, char const* end, fixed_point_t& value) { + if (begin && *begin == '-') { + return { begin, std::errc::invalid_argument }; + } + + end = end - begin > PRECISION ? begin + PRECISION : end; + uint64_t parsed_value; + std::from_chars_result result = string_to_uint64(begin, end, parsed_value); + if (result.ec != std::errc{}) { + return result; + } + + for (ptrdiff_t remaining_shift = PRECISION - (end - begin); remaining_shift > 0; remaining_shift--) { + parsed_value *= 10; + } + uint64_t decimal = pow(static_cast(10), PRECISION); + int64_t ret = 0; + for (int i = PRECISION - 1; i >= 0; --i) { + decimal >>= 1; + if (parsed_value >= decimal) { + parsed_value -= decimal; + ret |= 1 << i; + } + } + value = parse_raw(ret); + return result; +} + +fixed_point_t fixed_point_t::parse(char const* str, char const* end, bool* successful) { + fixed_point_t value = 0; + std::from_chars_result result = value.from_chars_with_plus(str, end); + if (successful) { + *successful = result.ec == std::errc {}; + } + return value; +} + +fixed_point_t fixed_point_t::parse(char const* str, size_t length, bool* successful) { + return parse(str, str + length, successful); +} + +fixed_point_t fixed_point_t::parse(std::string_view str, bool* successful) { + return parse(str.data(), str.length(), successful); +} + +fixed_point_t fixed_point_t::parse_unsafe(char const* value) { + char* endpointer; + double double_value = std::strtod(value, &endpointer); + + if (*endpointer != '\0') { + spdlog::error_s("Unsafe fixed point parse failed to parse the end of a string: \"{}\"", endpointer); + } + + value_type integer_value = static_cast(double_value * ONE + 0.5 * (double_value < 0 ? -1 : 1)); + + return parse_raw(integer_value); +} + +template +requires (sizeof(T) >= 4) +static fixed_point_t parse_capped_generic(const T value) { + fixed_point_t result; + if (value > std::numeric_limits::max()) { + if (value >= fixed_point_t::max.truncate()) { + spdlog::error_s("parse_capped value exceeded int32 max. Falling back to fixed_point_t::max"); + result = fixed_point_t::max; + } else { + spdlog::warn_s("parse_capped value exceeded int32 max. It still fits but exceeds fixed_point_t::usable_max"); + result = fixed_point_t::parse_raw(value << fixed_point_t::PRECISION); + } + } else { + result = fixed_point_t(static_cast(value)); + } + + return result; +} + +fixed_point_t fixed_point_t::parse_capped(const int64_t value) { return parse_capped_generic(value); } +fixed_point_t fixed_point_t::parse_capped(const uint64_t value) { return parse_capped_generic(value); } + +template EXP_LUT> +OV_SPEED_INLINE static constexpr fixed_point_t _exp_internal(fixed_point_t const& x) { + const bool negative = x.is_negative(); + fixed_point_t::value_type bits = negative ? -x.get_raw_value() : x.get_raw_value(); + fixed_point_t result = fixed_point_t::_1; + + for (size_t index = 0; bits != 0 && index < EXP_LUT.size(); ++index, bits >>= 1) { + if (bits & 1LL) { + result *= fixed_point_t::parse_raw(EXP_LUT[index]); + } + } + + if (bits != 0) { + spdlog::error_s("Fixed point exponential overflow!"); + } + + if (negative) { + return fixed_point_t::_1 / result; + } else { + return result; + } +} + +fixed_point_t fixed_point_t::exp(fixed_point_t const& x) { + return _exp_internal<_detail::LUT::_2_16_EXP_e.size(), _detail::LUT::_2_16_EXP_e>(x); +} + +fixed_point_t fixed_point_t::exp_2001(fixed_point_t const& x) { + return _exp_internal<_detail::LUT::_2_16_EXP_2001.size(), _detail::LUT::_2_16_EXP_2001>(x); +} \ No newline at end of file diff --git a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp index 764c7c4fb..30f6ec28e 100644 --- a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp +++ b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp @@ -5,37 +5,20 @@ #include #include #include -#include #include -#include -#include -#include #include #include -#include - -#include "openvic-simulation/types/StackString.hpp" -#include "openvic-simulation/utility/Getters.hpp" -#include "openvic-simulation/utility/Logger.hpp" -#include "openvic-simulation/core/string/CharConv.hpp" -#include "openvic-simulation/utility/Containers.hpp" #include "openvic-simulation/core/Typedefs.hpp" -#include "openvic-simulation/core/Math.hpp" -#include "openvic-simulation/core/template/Concepts.hpp" - -/* Sin lookup table */ -#include "openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp" - -/* Base e exponential lookup table */ -#include "openvic-simulation/types/fixed_point/FixedPointLUT_2_16_EXP_e.hpp" - -/* Base 2001 exponential lookup table */ -#include "openvic-simulation/types/fixed_point/FixedPointLUT_2_16_EXP_2001.hpp" namespace OpenVic { + struct fp_stack_string; + enum class midpoint_rounding { AWAY_ZERO, TO_ZERO }; + + template + concept integral_max_size_4 = std::integral && sizeof(T) <= 4; struct fixed_point_t { /* PROPERTY generated getter functions will return fixed points by value, rather than const reference. */ @@ -59,12 +42,12 @@ namespace OpenVic { * overflowing. For larger values this will not work and the result will be missing its most significant bits. */ private: - value_type PROPERTY_RW_CUSTOM_NAME(value, get_raw_value, set_raw_value); - - static_assert(_detail::LUT::SIN_PRECISION == PRECISION); - static_assert(_detail::LUT::_2_16_EXP_e_DIVISOR == 1 << PRECISION); - static_assert(_detail::LUT::_2_16_EXP_2001_DIVISOR == 1 << PRECISION); - + value_type value; + public: + [[nodiscard]] constexpr value_type const& get_raw_value() const { + return value; + } + private: // Doesn't account for sign, so -n.abc -> 1 - 0.abc OV_ALWAYS_INLINE constexpr fixed_point_t get_frac() const { return parse_raw(value & FRAC_MASK); @@ -147,29 +130,6 @@ namespace OpenVic { return value <=> static_cast(rhs) << PRECISION; } - OV_SPEED_INLINE constexpr fixed_point_t sin() const { - using namespace _detail::LUT; - - value_type num = (*this % pi2 * one_div_pi2).get_raw_value(); - - const bool negative = num < 0; - if (negative) { - num = -num; - } - - const value_type index = num >> SIN_SHIFT; - const value_type a = SIN[index]; - const value_type b = SIN[index + 1]; - - const value_type fraction = (num - (index << SIN_SHIFT)) << SIN_COUNT_LOG2; - const value_type result = a + (((b - a) * fraction) >> SIN_PRECISION); - return !negative ? parse_raw(result) : parse_raw(-result); - } - - OV_SPEED_INLINE constexpr fixed_point_t cos() const { - return (*this + pi_half).sin(); - } - OV_SPEED_INLINE constexpr bool is_negative() const { return value < 0; } @@ -178,12 +138,6 @@ namespace OpenVic { return !is_negative() ? parse_raw(value) : parse_raw(-value); } - OV_SPEED_INLINE constexpr fixed_point_t sqrt() const { - return !is_negative() - ? parse_raw(OpenVic::sqrt(static_cast(value) << PRECISION)) - : _0; - } - OV_SPEED_INLINE constexpr bool is_integer() const { return get_frac() == 0; } @@ -196,19 +150,15 @@ namespace OpenVic { return value >> PRECISION; } + OV_SPEED_INLINE void warn_if_truncated() const; + template OV_SPEED_INLINE explicit constexpr operator T() const { -#ifdef DEV_ENABLED + #ifdef DEV_ENABLED if (!std::is_constant_evaluated()) { - if (OV_unlikely(value != 0 && abs() < fixed_point_t::_1)) { - spdlog::warn_s( - "0 < abs(Fixed point) < 1, truncation will result in zero, this may be a bug. raw_value: {} as float: {}", - get_raw_value(), - static_cast(*this) - ); - } + warn_if_truncated(); } -#endif + #endif return unsafe_truncate(); } @@ -220,21 +170,6 @@ namespace OpenVic { OV_SPEED_INLINE constexpr T truncate() const { return static_cast(*this); } - - template - OV_SPEED_INLINE static constexpr StrongType multiply_truncate(StrongType const& integer, fixed_point_t const& fp) { - return StrongType(multiply_truncate( - type_safe::get(integer), - fp - )); - } - - template - OV_SPEED_INLINE static constexpr T multiply_truncate(T const& integer, fixed_point_t const& fp) { - return static_cast( - (static_cast(integer) * fp.get_raw_value()) >> PRECISION - ); - } template OV_SPEED_INLINE explicit constexpr operator T() const { @@ -323,221 +258,40 @@ namespace OpenVic { return static_cast(round_to_int64((value / static_cast(ONE)) * T { 100000 })) / T { 100000 }; } - OV_SPEED_INLINE constexpr std::to_chars_result to_chars(char* first, char* last, size_t decimal_places = -1) const { - if (first == nullptr || first >= last) { - return { last, std::errc::value_too_large }; - } - - if (is_negative()) { - *first = '-'; - ++first; - if (last - first <= 1) { - return { last, std::errc::value_too_large }; - } - } - - std::to_chars_result result {}; - if (decimal_places == static_cast(-1)) { - result = OpenVic::to_chars(first, last, abs().unsafe_truncate()); - if (OV_unlikely(result.ec != std::errc {})) { - return result; - } - if (OV_unlikely(last - result.ptr <= 1)) { - return result; - } - - fixed_point_t frac = abs().get_frac(); - if (frac != _0) { - *result.ptr = '.'; - ++result.ptr; - do { - if (OV_unlikely(last - result.ptr <= 1)) { - return { last, std::errc::value_too_large }; - } - frac *= 10; - *result.ptr = static_cast('0' + frac.unsafe_truncate()); - ++result.ptr; - frac = frac.get_frac(); - } while (frac != _0); - } - return result; - } - - // Add the specified number of decimal places, potentially 0 (so no decimal point) - fixed_point_t err = _0_50; - for (size_t i = decimal_places; i > 0; --i) { - err /= 10; - } - fixed_point_t val = this->abs() + err; - - - result = OpenVic::to_chars(first, last, val.unsafe_truncate()); - if (OV_unlikely(result.ec != std::errc {})) { - return result; - } - - if (decimal_places > 0) { - if (OV_unlikely(last - result.ptr <= 1)) { - return result; - } - val = val.get_frac(); - *result.ptr = '.'; - result.ptr++; - do { - if (OV_unlikely(last - result.ptr <= 1)) { - return result; - } - val *= 10; - *result.ptr = static_cast('0' + val.unsafe_truncate()); - ++result.ptr; - val = val.get_frac(); - } while (--decimal_places > 0); - } - return result; - } - - struct stack_string; - OV_SPEED_INLINE constexpr stack_string to_array(size_t decimal_places = -1) const; - - struct stack_string final : StackString<25> { - protected: - using StackString::StackString; - friend OV_SPEED_INLINE constexpr stack_string fixed_point_t::to_array(size_t decimal_places) const; - }; - - OV_SPEED_INLINE memory::string to_string(size_t decimal_places = -1) const { - stack_string result = to_array(decimal_places); - if (OV_unlikely(result.empty())) { - return {}; - } + std::to_chars_result to_chars(char* first, char* last, size_t decimal_places = -1) const; - return result; - } + fp_stack_string to_array(size_t decimal_places = -1) const; // Deterministic OV_ALWAYS_INLINE static constexpr fixed_point_t parse_raw(value_type value) { return fixed_point_t { raw_value, value }; } + OV_SPEED_INLINE static constexpr fixed_point_t parse_capped(const int32_t value) { return fixed_point_t(value); } + template - static constexpr fixed_point_t parse_capped(const T value) { - fixed_point_t result; - if (value > std::numeric_limits::max()) { - if (value >= fixed_point_t::max.truncate()) { - if (std::is_constant_evaluated()) { - assert(value >= fixed_point_t::max.truncate()); - } else { - spdlog::error_s("parse_capped value exceeded int32 max. Falling back to fixed_point_t::max"); - } - result = fixed_point_t::max; - } else { - if (!std::is_constant_evaluated()) { - spdlog::warn_s("parse_capped value exceeded int32 max. It still fits but exceeds fixed_point_t::usable_max"); - } - result = fixed_point_t::parse_raw(value << PRECISION); - } - } else { - result = fixed_point_t(static_cast(value)); - } + requires (sizeof(T) < 4) + OV_SPEED_INLINE static constexpr fixed_point_t parse_capped(const T value) { return fixed_point_t(static_cast(value)); } - return result; - } + static fixed_point_t parse_capped(const int64_t value); + static fixed_point_t parse_capped(const uint64_t value); - template - OV_SPEED_INLINE static constexpr fixed_point_t from_fraction(StrongType const& numerator, StrongType const& denominator) { - return from_fraction(type_safe::get(numerator), type_safe::get(denominator)); - } - - template - OV_SPEED_INLINE static constexpr fixed_point_t from_fraction(const TNumerator numerator, const TDenominator denominator) { - return parse_raw((static_cast(numerator) << PRECISION) / static_cast(denominator)); - } - - static constexpr fixed_point_t from_fraction(const int64_t numerator, const int64_t denominator) { - assert(numerator < 140737488355328LL); //2^47 - assert(numerator >= -140737488355328LL); //- 2^47 - return parse_raw((numerator << PRECISION) / denominator); - } - - static constexpr fixed_point_t from_fraction(const uint64_t numerator, const uint64_t denominator) { - assert(numerator < 281474976710656ULL); //2^48 - return parse_raw((numerator << PRECISION) / denominator); + template + requires (sizeof(T) >= 4) + static fixed_point_t parse_capped(const T value) { + return std::is_signed_v + ? parse_capped(static_cast(value)) + : parse_capped(static_cast(value)); } // Deterministic - OV_SPEED_INLINE constexpr std::from_chars_result from_chars(char const* begin, char const* end) { - if (begin == nullptr || begin >= end) { - return { begin, std::errc::invalid_argument }; - } - - if (char const& c = *(end - 1); c == 'f' || c == 'F') { - --end; - if (begin == end) { - return { begin, std::errc::invalid_argument }; - } - } - - char const* dot_pointer = begin; - while (*dot_pointer != '.' && ++dot_pointer != end) {} - // "." - if (dot_pointer == begin && dot_pointer + 1 == end) { - return { begin, std::errc::invalid_argument }; - } - - fixed_point_t result = 0; - std::from_chars_result from_chars = {}; - if (dot_pointer != begin) { - // Non-empty integer part, may be negative - from_chars = from_chars_integer(begin, dot_pointer, result); - } - - if (from_chars.ec != std::errc{}) { - return from_chars; - } - - if (dot_pointer + 1 < end) { - // Non-empty fractional part, cannot be negative - fixed_point_t adder; - from_chars = from_chars_fraction(dot_pointer + 1, end, adder); - result += result.is_negative() || (*begin == '-' && result == _0) ? -adder : adder; - } - - if (from_chars.ec != std::errc{}) { - return { begin, from_chars.ec }; - } - - value = result.value; - return from_chars; - } - - OV_SPEED_INLINE constexpr std::from_chars_result from_chars_with_plus(char const* begin, char const* end) { - if (begin && *begin == '+') { - begin++; - if (begin < end && *begin == '-') { - return std::from_chars_result { begin, std::errc::invalid_argument }; - } - } - - return from_chars(begin, end); - } + std::from_chars_result from_chars(char const* begin, char const* end); + std::from_chars_result from_chars_with_plus(char const* begin, char const* end); // Deterministic - OV_SPEED_INLINE static constexpr fixed_point_t parse(char const* str, char const* end, bool* successful = nullptr) { - fixed_point_t value = 0; - std::from_chars_result result = value.from_chars_with_plus(str, end); - if (successful) { - *successful = result.ec == std::errc {}; - } - return value; - } - - OV_SPEED_INLINE static constexpr fixed_point_t parse(char const* str, size_t length, bool* successful = nullptr) { - return parse(str, str + length, successful); - } - - OV_SPEED_INLINE static constexpr fixed_point_t parse(std::string_view str, bool* successful = nullptr) { - return parse(str.data(), str.length(), successful); - } + static fixed_point_t parse(char const* str, char const* end, bool* successful = nullptr); + static fixed_point_t parse(char const* str, size_t length, bool* successful = nullptr); + static fixed_point_t parse(std::string_view str, bool* successful = nullptr); // Not Deterministic OV_SPEED_INLINE static constexpr fixed_point_t parse_unsafe(float value) { @@ -545,31 +299,9 @@ namespace OpenVic { } // Not Deterministic - OV_SPEED_INLINE static fixed_point_t parse_unsafe(char const* value) { - char* endpointer; - double double_value = std::strtod(value, &endpointer); - - if (*endpointer != '\0') { - spdlog::error_s("Unsafe fixed point parse failed to parse the end of a string: \"{}\"", endpointer); - } - - value_type integer_value = static_cast(double_value * ONE + 0.5 * (double_value < 0 ? -1 : 1)); + static fixed_point_t parse_unsafe(char const* value); - return parse_raw(integer_value); - } - - OV_SPEED_INLINE explicit operator memory::string() const { - return to_string(); - } - - friend std::ostream& operator<<(std::ostream& stream, fixed_point_t const& obj) { - stack_string result = obj.to_array(); - if (OV_unlikely(result.empty())) { - return stream; - } - - return stream << static_cast(result); - } + friend std::ostream& operator<<(std::ostream& stream, fixed_point_t const& obj); OV_SPEED_INLINE constexpr friend fixed_point_t operator-(fixed_point_t const& obj) { return parse_raw(-obj.value); @@ -680,21 +412,6 @@ namespace OpenVic { return *this; } - template - OV_SPEED_INLINE constexpr fixed_point_t mul_div(StrongType const& numerator, StrongType const& denominator) const { - return mul_div(type_safe::get(numerator), type_safe::get(denominator)); - } - - template - OV_SPEED_INLINE constexpr fixed_point_t mul_div(T const& numerator, T const& denominator) const { - return parse_raw(value * numerator / denominator); - } - - //Preserves accuracy. Performing a normal multiplication of small values results in 0. - OV_SPEED_INLINE constexpr static fixed_point_t mul_div(fixed_point_t const& a, fixed_point_t const& b, fixed_point_t const& denominator) { - return a.mul_div(b.value, denominator.value); - } - OV_SPEED_INLINE constexpr friend fixed_point_t operator%(fixed_point_t const& lhs, fixed_point_t const& rhs) { return parse_raw(lhs.value % rhs.value); } @@ -746,91 +463,19 @@ namespace OpenVic { private: // Deterministic // Can produce negative values - OV_SPEED_INLINE static constexpr std::from_chars_result from_chars_integer(char const* str, char const* const end, fixed_point_t& value) { - int64_t parsed_value = 0; - std::from_chars_result result = string_to_int64(str, end, parsed_value); - if (result.ec == std::errc{}) { - if (parsed_value > std::numeric_limits::max()) { - result.ec = std::errc::value_too_large; - } else { - value = fixed_point_t(static_cast(parsed_value)); - } - } - return result; - } + static std::from_chars_result from_chars_integer(char const* str, char const* const end, fixed_point_t& value); // Deterministic // Cannot produce negative values - OV_SPEED_INLINE static constexpr std::from_chars_result from_chars_fraction(char const* begin, char const* end, fixed_point_t& value) { - if (begin && *begin == '-') { - return { begin, std::errc::invalid_argument }; - } - - end = end - begin > PRECISION ? begin + PRECISION : end; - uint64_t parsed_value; - std::from_chars_result result = string_to_uint64(begin, end, parsed_value); - if (result.ec != std::errc{}) { - return result; - } - - for (ptrdiff_t remaining_shift = PRECISION - (end - begin); remaining_shift > 0; remaining_shift--) { - parsed_value *= 10; - } - uint64_t decimal = pow(static_cast(10), PRECISION); - int64_t ret = 0; - for (int i = PRECISION - 1; i >= 0; --i) { - decimal >>= 1; - if (parsed_value >= decimal) { - parsed_value -= decimal; - ret |= 1 << i; - } - } - value = parse_raw(ret); - return result; - } - - template EXP_LUT> - OV_SPEED_INLINE static constexpr fixed_point_t _exp_internal(fixed_point_t const& x) { - const bool negative = x.is_negative(); - value_type bits = negative ? -x.value : x.value; - fixed_point_t result = _1; - - for (size_t index = 0; bits != 0 && index < EXP_LUT.size(); ++index, bits >>= 1) { - if (bits & 1LL) { - result *= parse_raw(EXP_LUT[index]); - } - } - - if (bits != 0) { - spdlog::error_s("Fixed point exponential overflow!"); - } - - if (negative) { - return _1 / result; - } else { - return result; - } - } + static std::from_chars_result from_chars_fraction(char const* begin, char const* end, fixed_point_t& value); public: - OV_SPEED_INLINE static constexpr fixed_point_t exp(fixed_point_t const& x) { - return _exp_internal<_detail::LUT::_2_16_EXP_e.size(), _detail::LUT::_2_16_EXP_e>(x); - } - - OV_SPEED_INLINE static constexpr fixed_point_t exp_2001(fixed_point_t const& x) { - return _exp_internal<_detail::LUT::_2_16_EXP_2001.size(), _detail::LUT::_2_16_EXP_2001>(x); - } + static fixed_point_t exp(fixed_point_t const& x); + static fixed_point_t exp_2001(fixed_point_t const& x); }; static_assert(sizeof(fixed_point_t) == fixed_point_t::SIZE, "fixed_point_t is not 8 bytes"); - OV_SPEED_INLINE constexpr fixed_point_t::stack_string fixed_point_t::to_array(size_t decimal_places) const { - stack_string str {}; - std::to_chars_result result = to_chars(str._array.data(), str._array.data() + str._array.size(), decimal_places); - str._string_size = result.ptr - str.data(); - return str; - } - inline constexpr fixed_point_t fixed_point_t::max = parse_raw(std::numeric_limits::max()); inline constexpr fixed_point_t fixed_point_t::min = parse_raw(std::numeric_limits::min()); inline constexpr fixed_point_t fixed_point_t::usable_max = parse_raw(2147483648LL); @@ -857,11 +502,6 @@ namespace OpenVic { inline constexpr fixed_point_t fixed_point_t::deg2rad = parse_raw(1143LL); inline constexpr fixed_point_t fixed_point_t::rad2deg = parse_raw(3754936LL); inline constexpr fixed_point_t fixed_point_t::e = parse_raw(178145LL); - - template<> - [[nodiscard]] OV_SPEED_INLINE constexpr fixed_point_t abs(fixed_point_t num) { - return num.abs(); - } } template<> diff --git a/src/openvic-simulation/types/fixed_point/FixedPointStackString.hpp b/src/openvic-simulation/types/fixed_point/FixedPointStackString.hpp new file mode 100644 index 000000000..692dbaa0d --- /dev/null +++ b/src/openvic-simulation/types/fixed_point/FixedPointStackString.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include "openvic-simulation/types/StackString.hpp" + +namespace OpenVic { + struct fixed_point_t; + + struct fp_stack_string final : StackString<25> { + friend fixed_point_t; + protected: + using StackString::StackString; + }; +} \ No newline at end of file diff --git a/src/openvic-simulation/types/fixed_point/Fraction.hpp b/src/openvic-simulation/types/fixed_point/Fraction.hpp index 205a18b08..613942f53 100644 --- a/src/openvic-simulation/types/fixed_point/Fraction.hpp +++ b/src/openvic-simulation/types/fixed_point/Fraction.hpp @@ -1,6 +1,7 @@ #pragma once #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/fixed_point/Math.hpp" #include "openvic-simulation/core/Typedefs.hpp" namespace OpenVic { @@ -9,11 +10,11 @@ namespace OpenVic { fixed_point_t denominator = fixed_point_t::_1; OV_SPEED_INLINE constexpr friend fixed_point_t operator*(Fraction const& lhs, fixed_point_t const& rhs) { - return fixed_point_t::mul_div(rhs, lhs.numerator, lhs.denominator); + return fp::mul_div(rhs, lhs.numerator, lhs.denominator); } OV_SPEED_INLINE constexpr friend fixed_point_t operator*(fixed_point_t const& lhs, Fraction const& rhs) { - return fixed_point_t::mul_div(lhs, rhs.numerator, rhs.denominator); + return fp::mul_div(lhs, rhs.numerator, rhs.denominator); } }; } \ No newline at end of file diff --git a/src/openvic-simulation/types/fixed_point/Math.hpp b/src/openvic-simulation/types/fixed_point/Math.hpp new file mode 100644 index 000000000..a2f7854b3 --- /dev/null +++ b/src/openvic-simulation/types/fixed_point/Math.hpp @@ -0,0 +1,105 @@ +#pragma once + +#include "FixedPoint.hpp" + +#include + +#include "openvic-simulation/core/MathSqrt.hpp" +#include "openvic-simulation/core/template/Concepts.hpp" +#include "openvic-simulation/core/Typedefs.hpp" + +/* Sin lookup table */ +#include "openvic-simulation/types/fixed_point/FixedPointLUT_sin.hpp" + +namespace OpenVic::fp { + static_assert(_detail::LUT::SIN_PRECISION == fixed_point_t::PRECISION); + + OV_SPEED_INLINE static constexpr fixed_point_t sin(fixed_point_t const& v) { + using namespace _detail::LUT; + + fixed_point_t::value_type num = (v % fixed_point_t::pi2 * fixed_point_t::one_div_pi2).get_raw_value(); + + const bool negative = num < 0; + if (negative) { + num = -num; + } + + const fixed_point_t::value_type index = num >> SIN_SHIFT; + const fixed_point_t::value_type a = SIN[index]; + const fixed_point_t::value_type b = SIN[index + 1]; + + const fixed_point_t::value_type fraction = (num - (index << SIN_SHIFT)) << SIN_COUNT_LOG2; + const fixed_point_t::value_type result = a + (((b - a) * fraction) >> SIN_PRECISION); + return fixed_point_t::parse_raw( + !negative ? result : -result + ); + } + + OV_SPEED_INLINE static constexpr fixed_point_t cos(fixed_point_t const& v) { + return fp::sin(v + fixed_point_t::pi_half); + } + + OV_SPEED_INLINE static constexpr fixed_point_t sqrt(fixed_point_t const& v) { + return !v.is_negative() + ? fixed_point_t::parse_raw( + OpenVic::sqrt( + static_cast(v.get_raw_value()) << fixed_point_t::PRECISION + ) + ) + : fixed_point_t::_0; + } + + //Preserves accuracy. Performing a normal multiplication of small values results in 0. + template + OV_SPEED_INLINE static constexpr fixed_point_t mul_div(fixed_point_t const& multiplier, T const& numerator, T const& denominator) { + return fixed_point_t::parse_raw(multiplier.get_raw_value() * numerator / denominator); + } + + OV_SPEED_INLINE static constexpr fixed_point_t mul_div(fixed_point_t const& multiplier, fixed_point_t const& numerator, fixed_point_t const& denominator) { + return mul_div(multiplier, numerator.get_raw_value(), denominator.get_raw_value()); + } + + template + OV_SPEED_INLINE static constexpr fixed_point_t mul_div(fixed_point_t const& multiplier, StrongType const& numerator, StrongType const& denominator) { + return mul_div(multiplier, type_safe::get(numerator), type_safe::get(denominator)); + } + + template + OV_SPEED_INLINE static constexpr T multiply_truncate(T const& integer, fixed_point_t const& v) { + return static_cast( + (static_cast(integer) * v.get_raw_value()) >> fixed_point_t::PRECISION + ); + } + + template + OV_SPEED_INLINE static constexpr StrongType multiply_truncate(StrongType const& integer, fixed_point_t const& v) { + return StrongType(multiply_truncate( + type_safe::get(integer), + v + )); + } + + template + OV_SPEED_INLINE static constexpr fixed_point_t from_fraction(const TNumerator numerator, const TDenominator denominator) { + return fixed_point_t::parse_raw( + (static_cast(numerator) << fixed_point_t::PRECISION) + / static_cast(denominator) + ); + } + + OV_SPEED_INLINE static constexpr fixed_point_t from_fraction(const int64_t numerator, const int64_t denominator) { + assert(numerator < 140737488355328LL); //2^47 + assert(numerator >= -140737488355328LL); //- 2^47 + return fixed_point_t::parse_raw((numerator << fixed_point_t::PRECISION) / denominator); + } + + OV_SPEED_INLINE static constexpr fixed_point_t from_fraction(const uint64_t numerator, const uint64_t denominator) { + assert(numerator < 281474976710656ULL); //2^48 + return fixed_point_t::parse_raw((numerator << fixed_point_t::PRECISION) / denominator); + } + + template + OV_SPEED_INLINE static constexpr fixed_point_t from_fraction(StrongType const& numerator, StrongType const& denominator) { + return from_fraction(type_safe::get(numerator), type_safe::get(denominator)); + } +} \ No newline at end of file diff --git a/tests/src/types/FixedPoint.cpp b/tests/src/types/FixedPoint.cpp index b0a48c47b..39a95e6ac 100644 --- a/tests/src/types/FixedPoint.cpp +++ b/tests/src/types/FixedPoint.cpp @@ -1,4 +1,6 @@ #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/fixed_point/FixedPointStackString.hpp" +#include "openvic-simulation/types/fixed_point/Math.hpp" #include #include @@ -40,15 +42,15 @@ TEST_CASE("fixed_point_t Linear algebra methods", "[fixed_point_t][fixed_point_t static constexpr fixed_point_t one = 1; static constexpr fixed_point_t two = 2; static constexpr fixed_point_t three = 3; - CONSTEXPR_CHECK(one.sin() == 0.8414306640625_a); - CONSTEXPR_CHECK(two.sin() == 0.9093170166015625_a); - CONSTEXPR_CHECK(three.sin() == 0.1412200927734375_a); - CONSTEXPR_CHECK(one.cos() == 0.5403900146484375_a); - CONSTEXPR_CHECK(two.cos() == -0.4160003662109375_a); - CONSTEXPR_CHECK(three.cos() == -0.989959716796875_a); - - CONSTEXPR_CHECK(fixed_point_t::_0_50.sin() == 0.4793853759765625_a); - CONSTEXPR_CHECK(fixed_point_t::_0_50.cos() == 0.87762451171875_a); + CONSTEXPR_CHECK(fp::sin(one) == 0.8414306640625_a); + CONSTEXPR_CHECK(fp::sin(two) == 0.9093170166015625_a); + CONSTEXPR_CHECK(fp::sin(three) == 0.1412200927734375_a); + CONSTEXPR_CHECK(fp::cos(one) == 0.5403900146484375_a); + CONSTEXPR_CHECK(fp::cos(two) == -0.4160003662109375_a); + CONSTEXPR_CHECK(fp::cos(three) == -0.989959716796875_a); + + CONSTEXPR_CHECK(fp::sin(fixed_point_t::_0_50) == 0.4793853759765625_a); + CONSTEXPR_CHECK(fp::cos(fixed_point_t::_0_50) == 0.87762451171875_a); } TEST_CASE("fixed_point_t Constant methods", "[fixed_point_t][fixed_point_t-constants]") { @@ -132,33 +134,33 @@ TEST_CASE("fixed_point_t Parse methods", "[fixed_point_t][fixed_point_t-parse]") CONSTEXPR_CHECK(fixed_point_t(10) == 10); CONSTEXPR_CHECK(fixed_point_t::parse_raw(10) == fixed_point_t::epsilon * 10); - CONSTEXPR_CHECK(fixed_point_t::parse_capped(140737488355328LL) == fixed_point_t::max); - CONSTEXPR_CHECK(fixed_point_t::parse_capped(140737488355327LL) == fixed_point_t::max); - CONSTEXPR_CHECK(fixed_point_t::parse_capped(140737488355326LL).truncate() == 140737488355326LL); + CHECK(fixed_point_t::parse_capped(140737488355328LL) == fixed_point_t::max); + CHECK(fixed_point_t::parse_capped(140737488355327LL) == fixed_point_t::max); + CHECK(fixed_point_t::parse_capped(140737488355326LL).truncate() == 140737488355326LL); static constexpr std::string_view fixed_point_str = "4.5432"sv; - CONSTEXPR_CHECK(fixed_point_t::parse(fixed_point_str) == 4.5432_a); + CHECK(fixed_point_t::parse(fixed_point_str) == 4.5432_a); bool fixed_point_str_success; CHECK(fixed_point_t::parse(fixed_point_str, &fixed_point_str_success) == 4.5432_a); CHECK(fixed_point_str_success); static constexpr std::string_view neg_fixed_point_str = "-4.5432"sv; - CONSTEXPR_CHECK(fixed_point_t::parse(neg_fixed_point_str) == -4.5432_a); + CHECK(fixed_point_t::parse(neg_fixed_point_str) == -4.5432_a); CHECK(fixed_point_t::parse(neg_fixed_point_str, &fixed_point_str_success) == -4.5432_a); CHECK(fixed_point_str_success); static constexpr std::string_view plus_fixed_point_str = "+4.5432"sv; - CONSTEXPR_CHECK(fixed_point_t::parse(plus_fixed_point_str) == 4.5432_a); + CHECK(fixed_point_t::parse(plus_fixed_point_str) == 4.5432_a); CHECK(fixed_point_t::parse(plus_fixed_point_str, &fixed_point_str_success) == 4.5432_a); CHECK(fixed_point_str_success); static constexpr std::string_view neg_zero_fixed_point_str = "-0"sv; - CONSTEXPR_CHECK(fixed_point_t::parse(neg_zero_fixed_point_str) == 0); + CHECK(fixed_point_t::parse(neg_zero_fixed_point_str) == 0); CHECK(fixed_point_t::parse(neg_zero_fixed_point_str, &fixed_point_str_success) == 0); CHECK(fixed_point_str_success); static constexpr std::string_view neg_0_25_fixed_point_str = "-0.25"sv; - CONSTEXPR_CHECK(fixed_point_t::parse(neg_0_25_fixed_point_str) == -0.25_a); + CHECK(fixed_point_t::parse(neg_0_25_fixed_point_str) == -0.25_a); CHECK(fixed_point_t::parse(neg_0_25_fixed_point_str, &fixed_point_str_success) == -0.25_a); CHECK(fixed_point_str_success); @@ -187,18 +189,18 @@ TEST_CASE("fixed_point_t string methods", "[fixed_point_t][fixed_point_t-string] static constexpr fixed_point_t _0_55 = fixed_point_t::_0_50 + fixed_point_t::_1 / 20; static constexpr fixed_point_t neg_0_55 = -_0_55; - CONSTEXPR_CHECK(constant_zero.to_array() == "0"sv); - CONSTEXPR_CHECK(one.to_array() == "1"sv); - CONSTEXPR_CHECK(constant_one.to_array() == "1"sv); - CONSTEXPR_CHECK(neg_one.to_array() == "-1"sv); - CONSTEXPR_CHECK(neg_two.to_array() == "-2"sv); - CONSTEXPR_CHECK(neg_three.to_array() == "-3"sv); - CONSTEXPR_CHECK(_2_55.to_array() == "2.54998779296875"sv); - CONSTEXPR_CHECK(neg_2_55.to_array() == "-2.54998779296875"sv); - CONSTEXPR_CHECK(_2_55.to_array(2) == "2.55"sv); - CONSTEXPR_CHECK(neg_2_55.to_array(2) == "-2.55"sv); - CONSTEXPR_CHECK(_0_55.to_array(2) == "0.55"sv); - CONSTEXPR_CHECK(neg_0_55.to_array(2) == "-0.55"sv); + CHECK(constant_zero.to_array() == "0"sv); + CHECK(one.to_array() == "1"sv); + CHECK(constant_one.to_array() == "1"sv); + CHECK(neg_one.to_array() == "-1"sv); + CHECK(neg_two.to_array() == "-2"sv); + CHECK(neg_three.to_array() == "-3"sv); + CHECK(_2_55.to_array() == "2.54998779296875"sv); + CHECK(neg_2_55.to_array() == "-2.54998779296875"sv); + CHECK(_2_55.to_array(2) == "2.55"sv); + CHECK(neg_2_55.to_array(2) == "-2.55"sv); + CHECK(_0_55.to_array(2) == "0.55"sv); + CHECK(neg_0_55.to_array(2) == "-0.55"sv); } TEST_CASE("fixed_point_t Other methods", "[fixed_point_t][fixed_point_t-other]") { @@ -210,8 +212,8 @@ TEST_CASE("fixed_point_t Other methods", "[fixed_point_t][fixed_point_t-other]") CONSTEXPR_CHECK_FALSE(fixed_point_t::_1_50.is_integer()); CONSTEXPR_CHECK_FALSE(fixed_point_t::_0_50.is_integer()); CONSTEXPR_CHECK_FALSE(fixed_point_t::_0_01.is_integer()); - CONSTEXPR_CHECK(fixed_point_t::_0_50.sqrt() == 0.7071075439453125_a); - CONSTEXPR_CHECK((-fixed_point_t::_0_50).sqrt() == 0); + CONSTEXPR_CHECK(fp::sqrt(fixed_point_t::_0_50) == 0.7071075439453125_a); + CONSTEXPR_CHECK(fp::sqrt(-fixed_point_t::_0_50) == 0); } TEST_CASE("fixed_point_t Operators", "[fixed_point_t][fixed_point_t-operators]") { @@ -288,7 +290,7 @@ TEST_CASE("fixed_point_t Operators", "[fixed_point_t][fixed_point_t-operators]") CONSTEXPR_CHECK(((int32_t)decimal4) == 3); CONSTEXPR_CHECK( - fixed_point_t::mul_div( + fp::mul_div( fixed_point_t::parse_raw(2), fixed_point_t::parse_raw(3), fixed_point_t::parse_raw(6) @@ -296,13 +298,13 @@ TEST_CASE("fixed_point_t Operators", "[fixed_point_t][fixed_point_t-operators]") ); CONSTEXPR_CHECK( - fixed_point_t::multiply_truncate( + fp::multiply_truncate( 4294967295LL, //2^32 - 1 fixed_point_t::usable_max //2^31 / 2^16 ) == 140737488322560LL //2^47 - 2^15 ); CONSTEXPR_CHECK( - fixed_point_t::multiply_truncate( + fp::multiply_truncate( 281474976710655LL, //2^48 - 1 fixed_point_t::_0_50 ) == 140737488355327LL //2^47 - 1 diff --git a/tests/src/types/Vector2.cpp b/tests/src/types/Vector2.cpp index 154605cc2..727f51844 100644 --- a/tests/src/types/Vector2.cpp +++ b/tests/src/types/Vector2.cpp @@ -3,6 +3,7 @@ #include "openvic-simulation/types/Vector.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/fixed_point/Math.hpp" #include "Approx.hpp" #include "Helper.hpp" // IWYU pragma: keep @@ -110,9 +111,9 @@ TEMPLATE_LIST_TEST_CASE("vec2_t Iterator", "[vec2_t][vec2_t-iterator]", VectorVa TEST_CASE("fvec2_t Length methods", "[vec2_t][fvec2_t][fvec2_t-length]") { static constexpr fvec2_t vector1 = fvec2_t(10, 10); static constexpr fvec2_t vector2 = fvec2_t(20, 30); - CONSTEXPR_CHECK(vector1.length_squared().sqrt() == testing::approx(testing::SQRT2 * 10)); - CONSTEXPR_CHECK(vector2.length_squared().sqrt() == testing::approx(36.05551275463989293119)); - CONSTEXPR_CHECK(vector1.distance_squared(vector2).sqrt() == testing::approx(22.36067977499789696409)); + CONSTEXPR_CHECK(fp::sqrt(vector1.length_squared()) == testing::approx(testing::SQRT2 * 10)); + CONSTEXPR_CHECK(fp::sqrt(vector2.length_squared()) == testing::approx(36.05551275463989293119)); + CONSTEXPR_CHECK(fp::sqrt(vector1.distance_squared(vector2)) == testing::approx(22.36067977499789696409)); } TEST_CASE("dvec2_t Length methods", "[vec2_t][dvec2_t][dvec2_t-length]") { diff --git a/tests/src/types/Vector3.cpp b/tests/src/types/Vector3.cpp index daecfa4ea..b6c4c1d9f 100644 --- a/tests/src/types/Vector3.cpp +++ b/tests/src/types/Vector3.cpp @@ -3,6 +3,7 @@ #include "openvic-simulation/types/Vector.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/fixed_point/Math.hpp" #include "Approx.hpp" #include "Helper.hpp" // IWYU pragma: keep @@ -100,9 +101,9 @@ TEMPLATE_LIST_TEST_CASE("vec3_t Iterator", "[vec3_t][vec3_t-iterator]", VectorVa TEST_CASE("fvec3_t Length methods", "[vec3_t][fvec3_t][fvec3_t-length]") { static constexpr fvec3_t vector1 = fvec3_t(10, 10, 10); static constexpr fvec3_t vector2 = fvec3_t(20, 30, 40); - CONSTEXPR_CHECK(vector1.length_squared().sqrt() == testing::approx(testing::SQRT3 * 10)); - CONSTEXPR_CHECK(vector2.length_squared().sqrt() == testing::approx(53.8516480713450403125)); - CONSTEXPR_CHECK(vector1.distance_squared(vector2).sqrt() == testing::approx(37.41657386773941385584)); + CONSTEXPR_CHECK(fp::sqrt(vector1.length_squared()) == testing::approx(testing::SQRT3 * 10)); + CONSTEXPR_CHECK(fp::sqrt(vector2.length_squared()) == testing::approx(53.8516480713450403125)); + CONSTEXPR_CHECK(fp::sqrt(vector1.distance_squared(vector2)) == testing::approx(37.41657386773941385584)); } TEST_CASE("dvec3_t Length methods", "[vec3_t][dvec3_t][dvec3_t-length]") { diff --git a/tests/src/types/Vector4.cpp b/tests/src/types/Vector4.cpp index 8f7ce9bb8..e5715d87f 100644 --- a/tests/src/types/Vector4.cpp +++ b/tests/src/types/Vector4.cpp @@ -3,6 +3,7 @@ #include "openvic-simulation/types/Vector.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" +#include "openvic-simulation/types/fixed_point/Math.hpp" #include "Approx.hpp" #include "Helper.hpp" // IWYU pragma: keep @@ -99,9 +100,9 @@ TEMPLATE_LIST_TEST_CASE("vec4_t Iterator", "[vec4_t][vec4_t-iterator]", VectorVa TEST_CASE("fvec4_t Length methods", "[vec4_t][fvec4_t][fvec4_t-length]") { static constexpr fvec4_t vector1 = fvec4_t(10, 10, 10, 10); static constexpr fvec4_t vector2 = fvec4_t(20, 30, 40, 50); - CONSTEXPR_CHECK(vector1.length_squared().sqrt() == 20); - CONSTEXPR_CHECK(vector2.length_squared().sqrt() == testing::approx(73.484692283495)); - CONSTEXPR_CHECK(vector1.distance_squared(vector2).sqrt() == testing::approx(54.772255750517)); + CONSTEXPR_CHECK(fp::sqrt(vector1.length_squared()) == 20); + CONSTEXPR_CHECK(fp::sqrt(vector2.length_squared()) == testing::approx(73.484692283495)); + CONSTEXPR_CHECK(fp::sqrt(vector1.distance_squared(vector2)) == testing::approx(54.772255750517)); } TEST_CASE("dvec4_t Length methods", "[vec4_t][dvec4_t][dvec4_t-length]") {