LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
numeric_evaluation_power.cpp
浏览该文件的文档.
2#include <cmath>
3#include <algorithm>
4#include <limits>
5
6namespace LMCAS::detail {
7namespace {
8
11 bool odd;
12};
13
15 const auto* number = dynamic_cast<const NumberNode*>(power.exponent().get());
16 const auto* integer = number ? std::get_if<BigInt>(&number->value()) : nullptr;
17 if (integer) {
18 return {true, integer->is_odd()};
19 }
20 const auto* rational = number ? std::get_if<Rational>(&number->value()) : nullptr;
21 if (rational) {
22 const bool integral = rational->is_integer();
23 return {integral, integral && rational->get_numerator().is_odd()};
24 }
25 const bool integral = std::trunc(value) == value;
26 return {integral, integral && std::fmod(value, 2.0) != 0.0};
27}
28
29}
30
32 auto step = context_.consume_steps(1, "evaluate_numeric");
33 if (!step) return Result<ApproxReal>::failure(step.error());
34 if (node.is_zero()) return numeric_approximation(0.0);
35 BigInt numerator;
36 BigInt denominator(1);
37 if (const auto* integer = std::get_if<BigInt>(&node.value())) {
38 numerator = *integer;
39 } else {
40 const auto& rational = std::get<Rational>(node.value());
41 numerator = rational.get_numerator();
42 denominator = rational.get_denominator();
43 }
44 if (numerator.is_negative()) {
46 "real square root requires a non-negative argument");
47 }
48 const auto num_bits = numerator.bit_length();
49 const auto den_bits = denominator.bit_length();
50 auto bits = context_.require_integer_bits(std::max(num_bits, den_bits), "evaluate_numeric");
51 if (!bits) return Result<ApproxReal>::failure(bits.error());
52 const bool reciprocal_scale = num_bits < den_bits;
53 const auto difference = reciprocal_scale ? den_bits - num_bits : num_bits - den_bits;
54 constexpr int max_scale = 2 * std::numeric_limits<double>::max_exponent;
55 constexpr int min_scale = 2 * (std::numeric_limits<double>::digits -
56 std::numeric_limits<double>::min_exponent + 1);
57 if (difference > static_cast<std::size_t>(reciprocal_scale ? min_scale : max_scale)) {
58 return numeric_failure(CasErrc::NumericFailure, "numeric square root is outside the floating-point range");
59 }
60 const auto shift = difference - difference % 2;
61 if (reciprocal_scale) numerator <<= shift;
62 else denominator <<= shift;
63 const int scale = static_cast<int>(shift / 2) * (reciprocal_scale ? -1 : 1);
64 const double value = std::ldexp(std::sqrt(Rational(numerator, denominator).to_double()), scale);
65 if (value == 0.0) {
66 return numeric_failure(CasErrc::NumericFailure, "numeric square root underflow");
67 }
69}
70
72 const auto* base_number = dynamic_cast<const NumberNode*>(node.base().get());
73 const auto* exponent_number = dynamic_cast<const NumberNode*>(node.exponent().get());
74 if (base_number && exponent_number) {
75 const auto* exponent = std::get_if<Rational>(&exponent_number->value());
76 if (exponent && *exponent == Rational(1, 2)) {
77 auto step = context_.consume_steps(1, "evaluate_numeric");
78 if (!step) return Result<ApproxReal>::failure(step.error());
79 return square_root(*base_number);
80 }
81 if (exponent_number->is_negative_one()) {
82 auto step = context_.consume_steps(2, "evaluate_numeric");
83 if (!step) return Result<ApproxReal>::failure(step.error());
84 if (base_number->is_zero()) {
85 return numeric_failure(CasErrc::DomainError, "zero cannot be raised to a non-positive power");
86 }
87 try {
88 if (const auto* integer = std::get_if<BigInt>(&base_number->value())) {
89 return numeric_approximation(Rational(BigInt(1), *integer).to_double());
90 }
91 const auto& rational = std::get<Rational>(base_number->value());
93 Rational(rational.get_denominator(), rational.get_numerator()).to_double());
94 } catch (const std::range_error& error) {
95 return numeric_failure(CasErrc::NumericFailure, error.what());
96 } catch (const std::overflow_error& error) {
97 return numeric_failure(CasErrc::NumericFailure, error.what());
98 } catch (const std::underflow_error& error) {
99 return numeric_failure(CasErrc::NumericFailure, error.what());
100 }
101 }
102 }
103 auto base = evaluate(node.base());
104 if (!base) {
105 return base;
106 }
107 auto exponent = evaluate(node.exponent());
108 if (!exponent) {
109 return exponent;
110 }
111 const double base_value = base.value().value;
112 const double exponent_value = exponent.value().value;
113 if (base_value == 0.0 && exponent_value <= 0.0) {
114 return numeric_failure(CasErrc::DomainError, "zero cannot be raised to a non-positive power");
115 }
116 if (std::signbit(base_value) && std::isfinite(exponent_value)) {
117 const auto parity = exponent_parity(node, exponent_value);
118 if (base_value < 0.0 && std::isfinite(base_value) && !parity.integral) {
120 "a negative real base requires an integer exponent");
121 }
122 if (parity.integral) {
123 const double magnitude = std::pow(std::abs(base_value), exponent_value);
124 if (magnitude == 0.0 && base_value != 0.0) {
125 return numeric_failure(CasErrc::NumericFailure, "numeric power underflow");
126 }
127 return numeric_approximation(parity.odd ? -magnitude : magnitude);
128 }
129 }
130 const double value = std::pow(base_value, exponent_value);
131 if (value == 0.0 && base_value != 0.0) {
132 return numeric_failure(CasErrc::NumericFailure, "numeric power underflow");
133 }
135}
136
137}
Result< void > consume_steps(std::size_t amount=1, const std::string &operation={})
Result< void > require_integer_bits(std::size_t amount, const std::string &operation={}) const
幂运算节点,表示 base^exponent。
const std::shared_ptr< const SymbolicNode > & exponent() const noexcept
static Result failure(CasErrc code, std::string message, std::string operation={})
Result< ApproxReal > square_root(const NumberNode &node)
Result< ApproxReal > evaluate(const std::shared_ptr< const SymbolicNode > &node)
Result< ApproxReal > power(const PowerNode &node)
ExponentParity exponent_parity(const PowerNode &power, double value)
多元 Hensel 提升(单变量步)
const SymbolicNodePtr & node(const ::LMCAS::SymbolicExpr &expression) noexcept
Result< ApproxReal > numeric_failure(CasErrc code, std::string message)
Result< ApproxReal > numeric_approximation(double value)
ExprResult integer(Integer)=delete
const ExprPtr & exponent
const std::string const ExprPtr & value
const ExprPtr & denominator
ExprResult rational(const Rational &value)