From d789d5f887d1a9e7e56d1e206cd870fa06b9be24 Mon Sep 17 00:00:00 2001 From: wvpm <24685035+wvpm@users.noreply.github.com> Date: Fri, 2 Jan 2026 23:30:31 +0100 Subject: [PATCH] Remove fixed_point_t::parse(int64_t) --- .../country/CountryInstance.cpp | 30 ++++++++---- .../country/CountryInstance.hpp | 5 +- .../defines/MilitaryDefines.cpp | 11 ++++- .../defines/MilitaryDefines.hpp | 3 +- .../economy/production/ArtisanalProducer.cpp | 2 +- .../production/ResourceGatheringOperation.cpp | 2 +- .../military/CombatWidth.hpp | 26 ++++++++++ src/openvic-simulation/population/Pop.cpp | 6 +-- .../population/PopsAggregate.cpp | 2 +- .../scripts/ConditionalWeight.cpp | 2 +- .../types/fixed_point/FixedPoint.hpp | 38 ++++++++++++-- tests/src/types/FixedPoint.cpp | 49 +++++++++++++------ 12 files changed, 136 insertions(+), 40 deletions(-) create mode 100644 src/openvic-simulation/military/CombatWidth.hpp diff --git a/src/openvic-simulation/country/CountryInstance.cpp b/src/openvic-simulation/country/CountryInstance.cpp index a9513a6a6..350073e53 100644 --- a/src/openvic-simulation/country/CountryInstance.cpp +++ b/src/openvic-simulation/country/CountryInstance.cpp @@ -1367,7 +1367,7 @@ 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::parse(administrators) / total_non_colonial_population); + administrator_percentage.set(fixed_point_t(administrators) / total_non_colonial_population); const fixed_point_t desired_administrators = desired_administrator_percentage.get_untracked() * total_non_colonial_population; const fixed_point_t administrative_efficiency_from_administrators_unclamped = std::min( @@ -1563,8 +1563,11 @@ void CountryInstance::_update_military() { } military_power_from_land.set( - supply_consumption * fixed_point_t::parse(regular_army_size) * sum_of_regiment_type_stats - / fixed_point_t::parse(7 * (1 + unit_type_manager.get_regiment_type_count())) + supply_consumption * fixed_point_t::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())) + ) ); if (disarmed) { @@ -1588,7 +1591,12 @@ void CountryInstance::_update_military() { military_power_from_sea.set(military_power_from_sea_running_total / 250); military_power_from_leaders.set( - fixed_point_t::parse(std::min(get_leader_count(), deployed_non_mobilised_regiments)) + fixed_point_t::parse_capped( + std::min( + get_leader_count(), + deployed_non_mobilised_regiments + ) + ) ); // Mobilisation calculations @@ -1598,8 +1606,8 @@ 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 = - ((fixed_point_t::_1 + mobilisation_impact) * fixed_point_t::parse(regiment_count)).floor(); + mobilisation_max_regiment_count = regiment_count + + fixed_point_t::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) { @@ -1626,10 +1634,12 @@ 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::parse(military_defines.get_base_combat_width()) + - get_modifier_effect_value(*modifier_effect_cache.get_combat_width_additive()) - ).floor(); + combat_width = combat_width_t( + ( + type_safe::get(military_defines.get_base_combat_width()) + + get_modifier_effect_value(*modifier_effect_cache.get_combat_width_additive()) + ).floor>() + ); dig_in_cap = get_modifier_effect_value(*modifier_effect_cache.get_dig_in_cap()).floor(); military_tactics = military_defines.get_base_military_tactics() + get_modifier_effect_value(*modifier_effect_cache.get_military_tactics()); diff --git a/src/openvic-simulation/country/CountryInstance.hpp b/src/openvic-simulation/country/CountryInstance.hpp index 76c95d052..65cf159a0 100644 --- a/src/openvic-simulation/country/CountryInstance.hpp +++ b/src/openvic-simulation/country/CountryInstance.hpp @@ -5,6 +5,7 @@ #include #include "openvic-simulation/diplomacy/CountryRelation.hpp" +#include "openvic-simulation/military/CombatWidth.hpp" #include "openvic-simulation/military/UnitBranchedGetterMacro.hpp" #include "openvic-simulation/modifier/ModifierSum.hpp" #include "openvic-simulation/politics/Rule.hpp" @@ -100,7 +101,7 @@ namespace OpenVic { }; // Thresholds for different uncivilised country statuses - static constexpr fixed_point_t PRIMITIVE_CIVILISATION_PROGRESS = fixed_point_t::parse(15) / 100; + static constexpr fixed_point_t PRIMITIVE_CIVILISATION_PROGRESS = fixed_point_t(15) / 100; static constexpr fixed_point_t UNCIVILISED_CIVILISATION_PROGRESS = fixed_point_t::_0_50; private: @@ -382,7 +383,7 @@ namespace OpenVic { fixed_point_t PROPERTY(land_unit_start_experience); fixed_point_t PROPERTY(naval_unit_start_experience); fixed_point_t PROPERTY(recruit_time); - int32_t PROPERTY(combat_width, 1); + combat_width_t PROPERTY(combat_width, combat_width_t(1)); int32_t PROPERTY(dig_in_cap, 0); fixed_point_t PROPERTY(military_tactics); OV_IFLATMAP_PROPERTY(RegimentType, technology_unlock_level_t, regiment_type_unlock_levels); diff --git a/src/openvic-simulation/defines/MilitaryDefines.cpp b/src/openvic-simulation/defines/MilitaryDefines.cpp index 2f2d935a4..8c527f7ad 100644 --- a/src/openvic-simulation/defines/MilitaryDefines.cpp +++ b/src/openvic-simulation/defines/MilitaryDefines.cpp @@ -1,5 +1,11 @@ #include "MilitaryDefines.hpp" +#include + +#include "openvic-simulation/military/CombatWidth.hpp" + +#include + using namespace OpenVic; using namespace OpenVic::NodeTools; @@ -14,7 +20,10 @@ node_callback_t MilitaryDefines::expect_defines() { "DIG_IN_INCREASE_EACH_DAYS", ONE_EXACTLY, expect_days(assign_variable_callback(dig_in_increase_each_days)), "REINFORCE_SPEED", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(reinforce_speed)), "COMBAT_DIFFICULTY_IMPACT", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(combat_difficulty_impact)), - "BASE_COMBAT_WIDTH", ONE_EXACTLY, expect_uint(assign_variable_callback(base_combat_width)), + "BASE_COMBAT_WIDTH", ONE_EXACTLY, expect_int([this](int8_t val)->bool{ + base_combat_width = combat_width_t(static_cast>(val)); + return true; + }), "POP_MIN_SIZE_FOR_REGIMENT", ONE_EXACTLY, expect_uint(assign_variable_callback(min_pop_size_for_regiment)), "POP_SIZE_PER_REGIMENT", ONE_EXACTLY, expect_uint(assign_variable_callback(pop_size_per_regiment)), "SOLDIER_TO_POP_DAMAGE", ONE_EXACTLY, expect_fixed_point(assign_variable_callback(soldier_to_pop_damage)), diff --git a/src/openvic-simulation/defines/MilitaryDefines.hpp b/src/openvic-simulation/defines/MilitaryDefines.hpp index a44ae68c4..3ed13e9b9 100644 --- a/src/openvic-simulation/defines/MilitaryDefines.hpp +++ b/src/openvic-simulation/defines/MilitaryDefines.hpp @@ -1,6 +1,7 @@ #pragma once #include "openvic-simulation/dataloader/NodeTools.hpp" +#include "openvic-simulation/military/CombatWidth.hpp" #include "openvic-simulation/types/Date.hpp" #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" #include "openvic-simulation/types/PopSize.hpp" @@ -16,7 +17,7 @@ namespace OpenVic { Timespan PROPERTY(dig_in_increase_each_days); fixed_point_t PROPERTY(reinforce_speed); fixed_point_t PROPERTY(combat_difficulty_impact); - size_t PROPERTY(base_combat_width, 0); + combat_width_t PROPERTY(base_combat_width, combat_width_t(0)); pop_size_t PROPERTY(min_pop_size_for_regiment, 0); pop_size_t PROPERTY(pop_size_per_regiment, 0); fixed_point_t PROPERTY(soldier_to_pop_damage); diff --git a/src/openvic-simulation/economy/production/ArtisanalProducer.cpp b/src/openvic-simulation/economy/production/ArtisanalProducer.cpp index 8b7aaf6bc..9c54d7dea 100644 --- a/src/openvic-simulation/economy/production/ArtisanalProducer.cpp +++ b/src/openvic-simulation/economy/production/ArtisanalProducer.cpp @@ -541,7 +541,7 @@ ProductionType const* ArtisanalProducer::pick_production_type( } if (current_score == score_estimate) { - relative_score = fixed_point_t::parse(ranked_artisanal_production_types.size() - i) + relative_score = fixed_point_t(static_cast(ranked_artisanal_production_types.size() - i)) / static_cast(1 + ranked_artisanal_production_types.size()); } } diff --git a/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp b/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp index 2a189a9e0..f6bfc8062 100644 --- a/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp +++ b/src/openvic-simulation/economy/production/ResourceGatheringOperation.cpp @@ -306,7 +306,7 @@ fixed_point_t ResourceGatheringOperation::produce() { const fixed_point_t effect_multiplier = job.get_effect_multiplier(); fixed_point_t relative_to_workforce = - fixed_point_t::parse(employees_of_type) / fixed_point_t::parse(max_employee_count_cache); + fixed_point_t(employees_of_type) / fixed_point_t(max_employee_count_cache); const fixed_point_t amount = job.get_amount(); if (effect_multiplier != fixed_point_t::_1 && relative_to_workforce > amount) { relative_to_workforce = amount; diff --git a/src/openvic-simulation/military/CombatWidth.hpp b/src/openvic-simulation/military/CombatWidth.hpp new file mode 100644 index 000000000..c1ed14326 --- /dev/null +++ b/src/openvic-simulation/military/CombatWidth.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +#include +#include + +#include + +namespace OpenVic { + struct combat_width_t : type_safe::strong_typedef, + type_safe::strong_typedef_op::equality_comparison, + type_safe::strong_typedef_op::relational_comparison, + type_safe::strong_typedef_op::integer_arithmetic, + type_safe::strong_typedef_op::mixed_addition, + type_safe::strong_typedef_op::mixed_subtraction { + using strong_typedef::strong_typedef; + }; +} + +template<> +struct fmt::formatter : fmt::formatter { + fmt::format_context::iterator format(OpenVic::combat_width_t const& value, fmt::format_context& ctx) const { + return fmt::formatter::format(type_safe::get(value), ctx); + } +}; \ No newline at end of file diff --git a/src/openvic-simulation/population/Pop.cpp b/src/openvic-simulation/population/Pop.cpp index 3a6a8d59f..bca75d6af 100644 --- a/src/openvic-simulation/population/Pop.cpp +++ b/src/openvic-simulation/population/Pop.cpp @@ -73,7 +73,7 @@ fixed_point_t Pop::get_unemployment_fraction() const { if (!type->can_be_unemployed) { return 0; } - return fixed_point_t::parse(get_unemployed()) / size; + return fixed_point_t(get_unemployed()) / size; } void Pop::setup_pop_test_values(IssueManager const& issue_manager) { @@ -255,8 +255,8 @@ void Pop::update_gamestate( ) { max_supported_regiments = 0; } else { - max_supported_regiments = (fixed_point_t::parse(size) / ( - fixed_point_t::parse(military_defines.get_pop_size_per_regiment()) * pop_size_per_regiment_multiplier + max_supported_regiments = (fixed_point_t(size) / ( + fixed_point_t(military_defines.get_pop_size_per_regiment()) * pop_size_per_regiment_multiplier )).floor() + 1; } } diff --git a/src/openvic-simulation/population/PopsAggregate.cpp b/src/openvic-simulation/population/PopsAggregate.cpp index 646942704..f6ca3e621 100644 --- a/src/openvic-simulation/population/PopsAggregate.cpp +++ b/src/openvic-simulation/population/PopsAggregate.cpp @@ -111,7 +111,7 @@ void PopsAggregate::add_pops_aggregate(PopsAggregate& part) { _yesterdays_import_value_running_total += part.get_yesterdays_import_value_untracked(); // TODO - change casting if pop_size_t changes type - const fixed_point_t part_population = fixed_point_t::parse(part.get_total_population()); + const fixed_point_t part_population = fixed_point_t(part.get_total_population()); average_literacy += part.get_average_literacy() * part_population; average_consciousness += part.get_average_consciousness() * part_population; average_militancy += part.get_average_militancy() * part_population; diff --git a/src/openvic-simulation/scripts/ConditionalWeight.cpp b/src/openvic-simulation/scripts/ConditionalWeight.cpp index 91ca609c9..c5a72da35 100644 --- a/src/openvic-simulation/scripts/ConditionalWeight.cpp +++ b/src/openvic-simulation/scripts/ConditionalWeight.cpp @@ -51,7 +51,7 @@ node_callback_t ConditionalWeight::expect_conditional_weight() { const auto time_callback = [this](std::string_view key, Timespan (*to_timespan)(Timespan::day_t)) -> auto { return [this, key, to_timespan](uint32_t value) -> bool { if (base == 0) { - base = fixed_point_t::parse((*to_timespan)(value).to_int()); + base = fixed_point_t::parse_capped((*to_timespan)(value).to_int()); return true; } else { spdlog::error_s( diff --git a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp index 14c0f0ad0..5aebb2711 100644 --- a/src/openvic-simulation/types/fixed_point/FixedPoint.hpp +++ b/src/openvic-simulation/types/fixed_point/FixedPoint.hpp @@ -219,6 +219,13 @@ namespace OpenVic { return static_cast(*this); } + 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 { return value / static_cast(ONE); @@ -402,9 +409,28 @@ namespace OpenVic { return fixed_point_t { raw_value, value }; } - // Deterministic - OV_SPEED_INLINE static constexpr fixed_point_t parse(int64_t value) { - return parse_raw(value << PRECISION); + 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 << fixed_point_t::PRECISION); + } + } else { + result = fixed_point_t(static_cast(value)); + } + + return result; } // Deterministic @@ -683,7 +709,11 @@ namespace OpenVic { int64_t parsed_value = 0; std::from_chars_result result = string_to_int64(str, end, parsed_value); if (result.ec == std::errc{}) { - value = parse(parsed_value); + 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; } diff --git a/tests/src/types/FixedPoint.cpp b/tests/src/types/FixedPoint.cpp index 56809e310..b0a48c47b 100644 --- a/tests/src/types/FixedPoint.cpp +++ b/tests/src/types/FixedPoint.cpp @@ -1,7 +1,7 @@ #include "openvic-simulation/types/fixed_point/FixedPoint.hpp" -#include -#include +#include +#include #include #include #include @@ -120,18 +120,21 @@ TEST_CASE("fixed_point_t Rounding methods", "[fixed_point_t][fixed_point_t-round } TEST_CASE("fixed_point_t Parse methods", "[fixed_point_t][fixed_point_t-parse]") { - CONSTEXPR_CHECK(fixed_point_t::parse(1) == 1); - CONSTEXPR_CHECK(fixed_point_t::parse(2) == 2); - CONSTEXPR_CHECK(fixed_point_t::parse(3) == 3); - CONSTEXPR_CHECK(fixed_point_t::parse(4) == 4); - CONSTEXPR_CHECK(fixed_point_t::parse(5) == 5); - CONSTEXPR_CHECK(fixed_point_t::parse(6) == 6); - CONSTEXPR_CHECK(fixed_point_t::parse(7) == 7); - CONSTEXPR_CHECK(fixed_point_t::parse(8) == 8); - CONSTEXPR_CHECK(fixed_point_t::parse(9) == 9); - CONSTEXPR_CHECK(fixed_point_t::parse(10) == 10); + CONSTEXPR_CHECK(fixed_point_t(1) == 1); + CONSTEXPR_CHECK(fixed_point_t(2) == 2); + CONSTEXPR_CHECK(fixed_point_t(3) == 3); + CONSTEXPR_CHECK(fixed_point_t(4) == 4); + CONSTEXPR_CHECK(fixed_point_t(5) == 5); + CONSTEXPR_CHECK(fixed_point_t(6) == 6); + CONSTEXPR_CHECK(fixed_point_t(7) == 7); + CONSTEXPR_CHECK(fixed_point_t(8) == 8); + CONSTEXPR_CHECK(fixed_point_t(9) == 9); + 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(10) == 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); static constexpr std::string_view fixed_point_str = "4.5432"sv; CONSTEXPR_CHECK(fixed_point_t::parse(fixed_point_str) == 4.5432_a); @@ -285,8 +288,24 @@ 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(fixed_point_t::parse_raw(2), fixed_point_t::parse_raw(3), fixed_point_t::parse_raw(6)) == - fixed_point_t::parse_raw(1) + fixed_point_t::mul_div( + fixed_point_t::parse_raw(2), + fixed_point_t::parse_raw(3), + fixed_point_t::parse_raw(6) + ) == fixed_point_t::parse_raw(1) + ); + + CONSTEXPR_CHECK( + fixed_point_t::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( + 281474976710655LL, //2^48 - 1 + fixed_point_t::_0_50 + ) == 140737488355327LL //2^47 - 1 ); }