void foo(double input)
{
double integral = std::floor(input);
double frac = input - integral;
const long precision = 1000000000; // This is the accuracy.
long gcd_ = gcd(round(frac * precision), precision);
long denominator = precision / gcd_;
long numerator = round(frac * precision) / gcd_;
std::cout << integral << " + ";
std::cout << numerator << " / " << denominator << std::endl;
}
long gcd(long a, long b)
{
if (a == 0)
return b;
else if (b == 0)
return a;
if (a < b)
return gcd(a, b % a);
else
return gcd(b, a % b);
}
对于变量float x = 0.333;,最佳有理近似不一定是333 / 1000,因为存储的值不是 * 精确 * 0.333而是0.333000004291534423828125,这是因为浮点的内部表示的精度有限。
一旦赋值,浮点值就不会有内存知道它来自何处,也不会有内存知道源代码是否将它定义为float x = 0.333;与float x = 0.333000004;,因为这两个值都有the same内部表示。分离数字的字符串表示的(相关但不同)问题(例如,用户输入的值)转换为整数和小数部分的问题无法通过先转换为浮点,然后对转换后的值运行浮点计算来解决。
#include <iostream>
using namespace std;
// converts the string half of the inputed decimal number into numerical values void converting
(string decimalNumber, float&numerator, float& denominator )
{ float number; string valueAfterPoint =decimalNumber.substr(decimalNumber.find("." ((decimalNumber.length() -1) )); // store the value after the decimal into a valueAfterPoint
int length = valueAfterPoint.length(); //stores the length of the value after the decimal point into length
numerator = atof(valueAfterPoint.c_str()); // converts the string type decimal number into a float value and stores it into the numerator
// loop increases the decimal value of the numerator by multiples of ten as long as the length is above zero of the decimal
for (; length > 0; length--)
numerator *= 10;
do
denominator *=10;
while (denominator < numerator);
// simplifies the the converted values of the numerator and denominator into simpler values for an easier to read output
void simplifying (float& numerator, float& denominator) { int maximumNumber = 9; //Numbers in the tenths place can only range from zero to nine so the maximum number for a position in a position for the decimal number will be nine
bool isDivisble; // is used as a checker to verify whether the value of the numerator has the found the dividing number that will a value of zero
// Will check to see if the numerator divided denominator is will equal to zero
if(int(numerator) % int(denominator) == 0) {
numerator /= denominator;
denominator = 1;
return; }
//check to see if the maximum number is greater than the denominator to simplify to lowest form while (maximumNumber < denominator) { maximumNumber *=10; }
// the maximum number loops from nine to zero. This conditions stops if the function isDivisible is true
for(; maximumNumber > 0;maximumNumber --){
isDivisble = ((int(numerator) % maximumNumber == 0) && int(denominator)% maximumNumber == 0);
if(isDivisble)
{
numerator /= maximumNumber; // when is divisible true numerator be devided by the max number value for example 25/5 = numerator = 5
denominator /= maximumNumber; //// when is divisible true denominator be devided by themax number value for example 100/5 = denominator = 20
}
// stop value if numerator and denominator is lower than 17 than it is at the lowest value
int stop = numerator + denominator;
if (stop < 17)
{
return;
} } }
#include <concepts>
/**
* \brief Multiply two numbers together checking for overflow.
* \tparam T The unsigned integral type to check for multiplicative overflow.
* \param a The multiplier.
* \param b The multicland.
* \return The result and a value indicating whether the multiplication
* overflowed.
*/
template<std::unsigned_integral T>
auto mul_overflow(T a, T b) -> std::tuple<T, bool>
{
size_t constexpr shift{ std::numeric_limits<T>::digits / 2 };
T constexpr mask{ (T{ 1 } << shift) - T{ 1 } };
T const a_high = a >> shift;
T const a_low = a & mask;
T const b_high = b >> shift;
T const b_low = b & mask;
T const low_low{ a_low * b_low };
if (!(a_high || b_high))
{
return { low_low, false };
}
bool overflowed = a_high && b_high;
T const low_high{ a_low * b_high };
T const high_low{ a_high * b_low };
T const ret{ low_low + ((low_high + high_low) << shift) };
return
{
ret,
overflowed
|| ret < low_low
|| (low_high >> shift) != 0
|| (high_low >> shift) != 0
};
}
/**
* \brief Converts a floating point value to a numerator and
* denominator pair.
*
* If the floating point value is larger than the maximum that the Tout
* type can hold, the results are silly.
*
* \tparam Tout The integral output type.
* \tparam Tin The floating point input type.
* \param f The value to convert to a numerator and denominator.
* \return The numerator and denominator.
*/
template <std::integral Tout, std::floating_point Tin>
auto to_fraction(Tin f) -> std::tuple<Tout, Tout>
{
const Tin multiplier
{
std::pow(std::numeric_limits<Tin>::radix,
std::numeric_limits<Tin>::digits)
};
uint64_t denominator{ static_cast<uint64_t>(multiplier) };
int power;
Tout num_fix{ 1 };
if constexpr (std::is_signed_v<Tout>)
{
num_fix = f < static_cast<Tin>(0) ? -1 : 1;
f = std::abs(f);
}
uint64_t numerator
{
static_cast<uint64_t>(std::frexp(f, &power) * multiplier)
};
uint64_t const factor
{
static_cast<uint64_t>(std::pow(
std::numeric_limits<Tin>::radix, std::abs(power)))
};
if (power > 0)
{
while(true)
{
auto const [res, overflow]{ mul_overflow(numerator, factor) };
if (!overflow)
{
numerator = res;
break;
}
numerator >>= 1;
denominator >>= 1;
}
}
else
{
while (true)
{
auto const [res, overflow]{ mul_overflow(denominator, factor) };
if (!overflow)
{
denominator = res;
break;
}
numerator >>= 1;
denominator >>= 1;
}
}
// get the results into the requested sized integrals.
while ((numerator > std::numeric_limits<Tout>::max()
|| denominator > std::numeric_limits<Tout>::max())
&& denominator > 1)
{
numerator >>= 1;
denominator >>= 1;
}
return
{
num_fix * static_cast<Tout>(numerator),
static_cast<Tout>(denominator)
};
}
您可以这样称呼它:
auto [n, d] { to_fraction<int8_t>(-124.777f) };
你得到n=-124,d=1;
auto [n, d] { to_fraction<uint64_t>(.33333333333333) };
#include<iostream>
#include<cmath>
#include<algorithm>
#include<functional>
#include<iomanip>
#include<string>
#include<vector>
#include <exception>
#include <sstream>
// note using std = c++11
// header section
#ifndef rational_H
#define rational_H
struct invalid : std::exception {
const char* what() const noexcept { return "not a number\n"; }};
struct Fraction {
public:
long long value{0};
long long numerator{0};
long long denominator{0};
}; Fraction F;
class fraction : public Fraction{
public:
fraction() {}
void ctf(double &);
void get_fraction(std::string& w, std::string& d, long double& n) {
F.value = (long long )n;
set_whole_part(w);
set_fraction_part(d);
make_fraction();
}
long long set_whole_part(std::string& w) {
return whole = std::stoll(w);
}
long long set_fraction_part(std::string& d) {
return decimal = std::stoll(d);
}
void make_fraction();
bool cmpf(long long&, long long&, const long double& epsilon);
int Euclids_method(long long&, long long&);
long long get_f_part() { return decimal; };
void convert(std::vector<long long>&);
bool is_negative{ false };
friend std::ostream& operator<<(std::ostream& os, fraction& ff);
struct get_sub_length;
private:
long long whole{ 0 };
long long decimal{ 0 };
};
#endif // rational_H
// definitions/source
struct get_sub_length {
size_t sub_len{};
size_t set_decimal_length(size_t& n) {
sub_len = n;
return sub_len;
}
size_t get_decimal_length() { return sub_len; }
}; get_sub_length slen;
struct coefficient {
std::vector<long long>coef;
}; coefficient C;
//compare's the value returned by convert with the original
// decimal value entered.
//if its within the tolarence of the epsilon consider it the best
//approximation you can get.
//feel free to experiment with the epsilon.
//for better results.
bool fraction::cmpf(long long& n1, long long& d1, const long double& epsilon = 0.0000005)
{
long double ex = pow(10, slen.get_decimal_length());
long long d = get_f_part(); // the original fractional part to use for comparison.
long double a = (long double)d / ex;
long double b = ((long double)d1 / (long double)n1);
if ((fabs(a - b) <= epsilon)) { return true; }
return false;
}
//Euclids algorithm returns the cofficients of a continued fraction through recursive division,
//for example: 0.375 -> 1/(375/1000) (note: for the fractional portion only).
// 1000/375 -> Remainder of 2.6666.... and 1000 -(2*375)=250,using only the integer value
// 375/250 -> Remainder of 1.5 and 375-(1*250)=125,
// 250/125 -> Remainder of 2.0 and 250-(2*125)=2
//the coefficients of the continued fraction are the integer values 2,1,2
// These are generally written [0;2,1,2] or [0;2,1,1,1] were 0 is the whole number value.
int fraction::Euclids_method(long long& n_dec, long long& exp)
{
long long quotient = 0;
if ((exp >= 1) && (n_dec != 0)) {
quotient = exp / n_dec;
C.coef.push_back(quotient);
long long divisor = n_dec;
long long dividend = exp - (quotient * n_dec);
Euclids_method(dividend, divisor); // recursive division
}
return 0;
}
// Convert is adding the elements stored in coef as a simple continued fraction
// which should result in a good approximation of the original decimal number.
void fraction::convert(std::vector<long long>& coef)
{
std::vector<long long>::iterator pos;
pos = C.coef.begin(), C.coef.end();
long long n1 = 0;
long long n2 = 1;
long long d1 = 1;
long long d2 = 0;
for_each(C.coef.begin(), C.coef.end(), [&](size_t pos) {
if (cmpf(n1, d1) == false) {
F.numerator = (n1 * pos) + n2;
n2 = n1;
n1 = F.numerator;
F.denominator = (d1 * pos) + d2;
d2 = d1;
d1 = F.denominator;
}
});
//flip the fraction back over to format the correct output.
F.numerator = d1;
F.denominator = n1;
}
// creates a fraction from the decimal component
// insures its in its abs form to ease calculations.
void fraction::make_fraction() {
size_t count = slen.get_decimal_length();
long long n_dec = decimal;
long long exp = (long long)pow(10, count);
Euclids_method(n_dec, exp);
convert(C.coef);
}
std::string get_w(const std::string& s)
{
std::string st = "0";
std::string::size_type pos;
pos = s.find(".");
if (pos - 1 == std::string::npos) {
st = "0";
return st;
}
else { st = s.substr(0, pos);
return st;
}
if (!(s.find("."))){
st = "0";
return st;
}
return st;
}
std::string get_d(const std::string& s)
{
std::string st = "0";
std::string::size_type pos;
pos = s.find(".");
if (pos == std::string::npos) {
st = "0";
return st;
}
std::string sub = s.substr(pos + 1);
st = sub;
size_t sub_len = sub.length();
slen.set_decimal_length(sub_len);
return st;
}
void fraction::ctf(double& nn)
{
//using stringstream for conversion to string
std::istringstream is;
is >> nn;
std::ostringstream os;
os << std::fixed << std::setprecision(14) << nn;
std::string s = os.str();
is_negative = false; //reset for loops
C.coef.erase(C.coef.begin(), C.coef.end()); //reset for loops
long double n = 0.0;
int m = 0;
//The whole number part will be seperated from the decimal part leaving a pure fraction.
//In such cases using Euclids agorithm would take the reciprocal 1/(n/exp) or exp/n.
//for pure continued fractions the cf must start with 0 + 1/(n+1/(n+...etc
//So the vector is initilized with zero as its first element.
C.coef.push_back(m);
std::cout << '\n';
if (s == "q") { // for loop structures
exit(0);
}
if (s.front() == '-') { // flag negative values.
is_negative = true; // represent nagative in output
s.erase(remove(s.begin(), s.end(), '-'), s.end()); // using abs
}
// w, d, seperate the string components
std::string w = get_w(s);
std::string d = get_d(s);
try
{
if (!(n = std::stold(s))) {throw invalid(); } // string_to_double()
get_fraction(w, d, n);
}
catch (std::exception& e) {
std::cout << e.what();
std::cout <<'\n'<< std::endl;
}
}
// The ostream formats and displays the various outputs
std::ostream& operator<<(std::ostream& os, fraction& f)
{
std::cout << '\n';
if (f.is_negative == true) {
os << "The coefficients are [" << '-' << f.whole << ";";
for (size_t i = 1; i < C.coef.size(); ++i) {
os << C.coef[i] << ',';
}
std::cout << "]" << '\n';
os << "The cf is: " << '-' << f.whole;
for (size_t i = 1; i < C.coef.size(); ++i) {
os << "+1/(" << C.coef[i];
}
for (size_t i = 1; i < C.coef.size(); ++i) {
os << ')';
}
std::cout << '\n';
if (F.value >= 1 && F.numerator == 0 && F.denominator == 1) {
F.numerator = abs(f.whole);
os << '-' << F.numerator << '/' << F.denominator << '\n';
return os;
}
else if (F.value == 0 && F.numerator == 0 && F.denominator == 1) {
os << F.numerator << '/' << F.denominator << '\n';
return os;
}
else if (F.value == 0 && F.numerator != 0 && F.denominator != 0) {
os << '-' << abs(F.numerator) << '/' << abs(F.denominator) << '\n';
return os;
}
else if (F.numerator == 0 && F.denominator == 0) {
os << '-' << f.whole << '\n';
return os;
}
else
os << '-' << (abs(f.whole) * abs(F.denominator) + abs(F.numerator)) << '/' << abs(F.denominator) << '\n';
}
if (f.is_negative == false) {
os << "The coefficients are [" << f.whole << ";";
for (size_t i = 1; i < C.coef.size(); ++i) {
os << C.coef[i] << ',';
}
std::cout << "]" << '\n';
os << "The cf is: " << f.whole;
for (size_t i = 1; i < C.coef.size(); ++i) {
os << "+1/(" << C.coef[i];
}
for (size_t i = 1; i < C.coef.size(); ++i) {
os << ')';
}
std::cout << '\n';
if (F.value >= 1 && F.numerator == 0 && F.denominator == 1) {
F.numerator = abs(f.whole);
os << F.numerator << '/' << F.denominator << '\n';
return os;
}
else if (F.value == 0 && F.numerator != 0 && F.denominator != 0) {
os << abs(F.numerator) << '/' << abs(F.denominator) << '\n';
return os;
}
else if (F.numerator == 0 && F.denominator == 0) {
os << f.whole << '\n';
return os;
}
else
os << (abs(f.whole) * abs(F.denominator) + abs(F.numerator)) << '/' << abs(F.denominator) << '\n';
os << f.whole << ' ' << F.numerator << '/' << F.denominator << '\n';
}
return os;
}
int main()
{
fraction f;
double s = 0;
std::cout << "Enter a number to convert to a fraction\n";
std::cout << "Enter a \"q\" to quit\n";
// uncomment for a loop
while (std::cin >> s) {
f.ctf(s);
std::cout << f << std::endl;
}
// comment out these lines if you want the loop
//std::cin >> s;
//f.ctf(s);
//std::cout << f << std::endl;
}
6条答案
按热度按时间pxiryf3j1#
首先得到分数部分,然后得到gcd。使用欧几里得算法http://en.wikipedia.org/wiki/Euclidean_algorithm
niknxzdl2#
有时你会想近似小数,而不需要等价。例如pi=3.14159近似为22/7或355/113。我们可以使用cycle参数来得到:
u7up0aaq3#
(* 注解太长 *。)
我认为,正确的解释是可能的,但太容易说错问题或误解答案。
这里提出的问题是找到给定浮点值的有理近似值。
这当然是可能的,因为C++中使用的浮点格式只能存储有理数值,最常见的形式是符号/尾数/指数。(为了使数字更简单),
0.333
是stored as1499698695241728 * 2^(-52)
。这等价于分数1499698695241728 / 2^52
,其convergents提供了越来越精确的近似,一直到原始值:一个月三个月一个月、一个月四个月一个月、一个月五个月一个月、一个月六个月。这里有两点需要注意。
float x = 0.333;
,最佳有理近似不一定是333 / 1000
,因为存储的值不是 * 精确 *0.333
而是0.333000004291534423828125
,这是因为浮点的内部表示的精度有限。float x = 0.333;
与float x = 0.333000004;
,因为这两个值都有the same内部表示。分离数字的字符串表示的(相关但不同)问题(例如,用户输入的值)转换为整数和小数部分的问题无法通过先转换为浮点,然后对转换后的值运行浮点计算来解决。[ * 编辑 * ] 以下是
0.333f
示例的分步详细信息。1.将
float
转换为精确分数的代码。1.这给出了
float val = 0.333f;
的有理表示为5586813/16777216
。1.剩下要做的是确定精确分数的收敛性,这只能通过整数计算来完成。end result是(由WA提供):
7y4bm7vi4#
我想出了一个算法来解决这个问题,但我认为它太长了,可以用更少的代码行来完成。很抱歉,缩进很差,很难在溢出时对齐所有内容。
u4vypkhs5#
我完全同意dxiv的solution,但我需要一个更通用的函数(我为了好玩而加入了带符号的东西,因为我的用例只包含正值):
您可以这样称呼它:
你得到
n=-124
,d=1
;给出
n=6004799503160601
和d=18014398509481984
pgvzfuti6#