LMCAS
Lamina Computer Algebra System
载入中...
搜索中...
未找到
rational_representation.cpp
浏览该文件的文档.
1#include "rational.hpp"
2#include <algorithm>
3#include <cmath>
4#include <limits>
5#include <map>
6#include <stdexcept>
7
8namespace LMCAS {
9namespace {
10
11std::string format_scaled_decimal(BigInt scaled, std::size_t digits) {
12 const bool negative = scaled < BigInt(0);
13 if (negative) {
14 scaled = scaled * BigInt(-1);
15 }
16 std::string text = scaled.to_string();
17 if (text.size() <= digits) {
18 text.insert(0, digits - text.size() + 1, '0');
19 }
20 text.insert(text.size() - digits, 1, '.');
21 while (text.back() == '0') {
22 text.pop_back();
23 }
24 if (text.back() == '.') {
25 text.pop_back();
26 }
27 if (negative) {
28 text.insert(text.begin(), '-');
29 }
30 return text;
31}
32
33}
34
35BigInt Rational::pow10(std::size_t exponent) {
36 if (exponent > max_decimal_scale) {
37 throw std::length_error("Rational decimal scale exceeds safety limit");
38 }
39 if (exponent == 0) return BigInt(1);
40 return BigInt(10).power(static_cast<unsigned long>(exponent));
41}
42
43std::size_t Rational::decimal_scale(std::int64_t exponent) {
44 constexpr auto limit = static_cast<std::int64_t>(max_decimal_scale);
46 throw std::length_error("Rational decimal scale exceeds safety limit");
47 }
48 return static_cast<std::size_t>(exponent < 0 ? -exponent : exponent);
49}
50
51Rational Rational::from_double(double value) {
52 if (!std::isfinite(value)) {
53 throw std::invalid_argument("Cannot construct Rational from NaN or infinity");
54 }
55 if (value == 0.0) {
56 return Rational();
57 }
58 int exponent = 0;
59 const double fraction = std::frexp(value, &exponent);
60 const auto mantissa = static_cast<std::int64_t>(std::ldexp(fraction, 53));
61 const int binary_exponent = exponent - 53;
62
63 BigInt num(mantissa);
64 BigInt den(1);
65 if (binary_exponent >= 0) {
66 num <<= static_cast<mp_size_t>(binary_exponent);
67 } else {
68 den <<= static_cast<mp_size_t>(-binary_exponent);
69 }
70 return Rational(num, den);
71}
72
73std::size_t Rational::hash() const {
74 std::size_t seed = numerator.hash();
75 std::size_t d_hash = denominator.hash();
76 seed ^= d_hash + 0x9e3779b9 + (seed << 6) + (seed >> 2);
77 return seed;
78}
79
80std::string Rational::to_string() const {
81 if (is_integer()) {
82 return numerator.to_string();
83 }
84 return numerator.to_string() + "/" + denominator.to_string();
85}
86
87std::string Rational::to_float_string() {
88 if (numerator % denominator == BigInt(0)) return (numerator / denominator).to_string();
89 std::string re;
90 const BigInt ten(10);
91 const BigInt zero(0);
92 if ((numerator < zero) ^ (denominator < zero)) re += '-';
93 BigInt num = numerator.abs(), den = denominator.abs();
94 re += (num / den).to_string();
95 num %= den;
96 re += '.';
97
98 std::map<BigInt, std::size_t> num_index;
99
100 while (num != zero && num_index.find(num) == num_index.end()) {
101 num_index[num] = re.size();
102 re += '0';
103 num *= ten;
104 while (num >= den) {
105 num -= den;
106 re.back()++;
107 }
108 }
109 if (num_index.find(num) != num_index.end()) {
110 re.insert(num_index[num], 1, '(');
111 re += ")...";
112 }
113 return re;
114
115}
116
117std::string Rational::to_float_string(std::int64_t n) const{
118 if (is_zero()) {
119 return "0";
120 }
121 if (n == 0) {
122 return (numerator / denominator).to_string();
123 }
124 const std::size_t digits = decimal_scale(n);
125 const BigInt pow10n = pow10(digits);
126 if (n > 0) {
127 return format_scaled_decimal(numerator * pow10n / denominator, digits);
128 }
129 std::string re = (numerator / (denominator * pow10n)).to_string();
130 if (re.size() == 1 && re[0] == '0') {
131 return re;
132 }
133 re.append(digits, '0');
134 return re;
135}
136
137void Rational::floor(const std::int64_t& n) {
138 floor_without_sim(n);
139 simplify();
140}
141
142void Rational::floor_without_sim(const std::int64_t& n) {
143 if (n == 0) {
144 numerator /= denominator;
145 denominator = BigInt(1);
146 return;
147 }
148 const BigInt pow10n = pow10(decimal_scale(n));
149 if (n > 0) {
150 numerator *= pow10n;
151 numerator /= denominator;
152 denominator = pow10n;
153 } else {
154 denominator *= pow10n;
155 numerator /= denominator;
156 numerator *= pow10n;
157 denominator = BigInt(1);
158 }
159}
160
161BigInt Rational::to_bigint() const {
162 if (!is_integer()) {
163 throw std::runtime_error("Cannot convert non-integer fraction to BigInt");
164 }
165 return numerator;
166}
167
168lmmc_real_t Rational::to_double() const {
169 if (is_zero()) return 0.0;
170 const std::string num_text = numerator.abs().to_string();
171 const std::string den_text = denominator.to_string();
172 const std::size_t num_digits = std::min<std::size_t>(18, num_text.size());
173 const std::size_t den_digits = std::min<std::size_t>(18, den_text.size());
174 const long double num_head = std::stold(num_text.substr(0, num_digits));
175 const long double den_head = std::stold(den_text.substr(0, den_digits));
176 const std::size_t num_tail = num_text.size() - num_digits;
177 const std::size_t den_tail = den_text.size() - den_digits;
178 const bool negative_shift = num_tail < den_tail;
179 const std::size_t shift = negative_shift ? den_tail - num_tail : num_tail - den_tail;
180 if (shift > static_cast<std::uint64_t>(std::numeric_limits<std::int64_t>::max())) {
181 if (negative_shift) {
182 throw std::underflow_error("Rational underflow during double conversion");
183 }
184 throw std::overflow_error("Rational cannot be represented as a finite double");
185 }
186 const std::int64_t decimal_shift = negative_shift ?
187 -static_cast<std::int64_t>(shift) : static_cast<std::int64_t>(shift);
188 long double value = (num_head / den_head) *
189 std::pow(10.0L, static_cast<long double>(decimal_shift));
190 if (numerator.is_negative()) value = -value;
191 const lmmc_real_t converted = static_cast<lmmc_real_t>(value);
192 if (!std::isfinite(converted)) {
193 /**
194 * @brief 用精确整数运算判定 binary64 的有限值舍入边界。
195 * 十进制前缀近似可能越过上界,而精确有理数仍舍入为 DBL_MAX;
196 * 绝对值小于 DBL_MAX + 0.5 ULP 时仍舍入为有限值。
197 */
198 const BigInt max_finite_integer =
199 (BigInt(1) << 1024) - (BigInt(1) << 971);
200 const BigInt finite_rounding_limit =
201 max_finite_integer + (BigInt(1) << 970);
202 if (numerator.abs() <
203 denominator * finite_rounding_limit) {
204 return std::copysign(
205 std::numeric_limits<lmmc_real_t>::max(),
206 numerator.is_negative() ? -1.0 : 1.0);
207 }
208 throw std::overflow_error(
209 "Rational cannot be represented as a finite double");
210 }
211 if (converted == 0.0) {
212 throw std::underflow_error("Rational underflow during double conversion");
213 }
214 return converted;
215}
216
217}
bool is_zero(const SymbolicExpr &expression)
std::string format_scaled_decimal(BigInt scaled, std::size_t digits)
std::shared_ptr< SymbolicExpr > num(int value)
@ Rational
Rational numbers
ExprResult simplify(const ExprPtr &expression, ComputationContext &context)
const ExprPtr & exponent
const std::string const ExprPtr & value
const ExprPtr & denominator
精确有理数 Rational,基于 BigInt 分子/分母表示,自动约分。