สมมติว่าเรามี 0.33 เราต้องเอาท์พุท "1/3" ถ้าเรามี "0.4" เราต้องส่งออก "2/5"
มันผิดในกรณีทั่วไปเนื่องจาก 1/3 = 0.3333333 = 0 (3) ยิ่งไปกว่านั้นมันเป็นไปไม่ได้ที่จะหาคำตอบจากคำแนะนำข้างต้นคือทศนิยมสามารถแปลงเป็นเศษส่วนด้วยความแม่นยำที่กำหนดได้เนื่องจากผลลัพธ์เป็นเศษส่วนเสมอ
แต่ฉันขอแนะนำฟังก์ชันที่ครอบคลุมของฉันด้วยตัวเลือกมากมายตามแนวคิดของอนุกรมเรขาคณิตไม่มีที่สิ้นสุดโดยเฉพาะในสูตร:
ในตอนแรกฟังก์ชันนี้พยายามหาช่วงเวลาเศษส่วนในการแทนค่าสตริง หลังจากนั้นจะใช้สูตรที่อธิบายไว้ข้างต้น
รหัสตัวเลขที่มีเหตุผลยืมมาจากการใช้ตัวเลขเชิงเหตุผลของStephen M. McKameyใน C # ฉันหวังว่าจะไม่ยากที่จะพอร์ตรหัสของฉันเป็นภาษาอื่น
/// <summary>
/// Convert decimal to fraction
/// </summary>
/// <param name="value">decimal value to convert</param>
/// <param name="result">result fraction if conversation is succsess</param>
/// <param name="decimalPlaces">precision of considereation frac part of value</param>
/// <param name="trimZeroes">trim zeroes on the right part of the value or not</param>
/// <param name="minPeriodRepeat">minimum period repeating</param>
/// <param name="digitsForReal">precision for determination value to real if period has not been founded</param>
/// <returns></returns>
public static bool FromDecimal(decimal value, out Rational<T> result,
int decimalPlaces = 28, bool trimZeroes = false, decimal minPeriodRepeat = 2, int digitsForReal = 9)
{
var valueStr = value.ToString("0.0000000000000000000000000000", CultureInfo.InvariantCulture);
var strs = valueStr.Split('.');
long intPart = long.Parse(strs[0]);
string fracPartTrimEnd = strs[1].TrimEnd(new char[] { '0' });
string fracPart;
if (trimZeroes)
{
fracPart = fracPartTrimEnd;
decimalPlaces = Math.Min(decimalPlaces, fracPart.Length);
}
else
fracPart = strs[1];
result = new Rational<T>();
try
{
string periodPart;
bool periodFound = false;
int i;
for (i = 0; i < fracPart.Length; i++)
{
if (fracPart[i] == '0' && i != 0)
continue;
for (int j = i + 1; j < fracPart.Length; j++)
{
periodPart = fracPart.Substring(i, j - i);
periodFound = true;
decimal periodRepeat = 1;
decimal periodStep = 1.0m / periodPart.Length;
var upperBound = Math.Min(fracPart.Length, decimalPlaces);
int k;
for (k = i + periodPart.Length; k < upperBound; k += 1)
{
if (periodPart[(k - i) % periodPart.Length] != fracPart[k])
{
periodFound = false;
break;
}
periodRepeat += periodStep;
}
if (!periodFound && upperBound - k <= periodPart.Length && periodPart[(upperBound - i) % periodPart.Length] > '5')
{
var ind = (k - i) % periodPart.Length;
var regroupedPeriod = (periodPart.Substring(ind) + periodPart.Remove(ind)).Substring(0, upperBound - k);
ulong periodTailPlusOne = ulong.Parse(regroupedPeriod) + 1;
ulong fracTail = ulong.Parse(fracPart.Substring(k, regroupedPeriod.Length));
if (periodTailPlusOne == fracTail)
periodFound = true;
}
if (periodFound && periodRepeat >= minPeriodRepeat)
{
result = FromDecimal(strs[0], fracPart.Substring(0, i), periodPart);
break;
}
else
periodFound = false;
}
if (periodFound)
break;
}
if (!periodFound)
{
if (fracPartTrimEnd.Length >= digitsForReal)
return false;
else
{
result = new Rational<T>(long.Parse(strs[0]), 1, false);
if (fracPartTrimEnd.Length != 0)
result = new Rational<T>(ulong.Parse(fracPartTrimEnd), TenInPower(fracPartTrimEnd.Length));
return true;
}
}
return true;
}
catch
{
return false;
}
}
public static Rational<T> FromDecimal(string intPart, string fracPart, string periodPart)
{
Rational<T> firstFracPart;
if (fracPart != null && fracPart.Length != 0)
{
ulong denominator = TenInPower(fracPart.Length);
firstFracPart = new Rational<T>(ulong.Parse(fracPart), denominator);
}
else
firstFracPart = new Rational<T>(0, 1, false);
Rational<T> secondFracPart;
if (periodPart != null && periodPart.Length != 0)
secondFracPart =
new Rational<T>(ulong.Parse(periodPart), TenInPower(fracPart.Length)) *
new Rational<T>(1, Nines((ulong)periodPart.Length), false);
else
secondFracPart = new Rational<T>(0, 1, false);
var result = firstFracPart + secondFracPart;
if (intPart != null && intPart.Length != 0)
{
long intPartLong = long.Parse(intPart);
result = new Rational<T>(intPartLong, 1, false) + (intPartLong == 0 ? 1 : Math.Sign(intPartLong)) * result;
}
return result;
}
private static ulong TenInPower(int power)
{
ulong result = 1;
for (int l = 0; l < power; l++)
result *= 10;
return result;
}
private static decimal TenInNegPower(int power)
{
decimal result = 1;
for (int l = 0; l > power; l--)
result /= 10.0m;
return result;
}
private static ulong Nines(ulong power)
{
ulong result = 9;
if (power >= 0)
for (ulong l = 0; l < power - 1; l++)
result = result * 10 + 9;
return result;
}
มีตัวอย่างการใช้งานบางส่วน:
Rational<long>.FromDecimal(0.33333333m, out r, 8, false);
// then r == 1 / 3;
Rational<long>.FromDecimal(0.33333333m, out r, 9, false);
// then r == 33333333 / 100000000;
กรณีของคุณที่มีการตัดส่วนด้านขวาเป็นศูนย์:
Rational<long>.FromDecimal(0.33m, out r, 28, true);
// then r == 1 / 3;
Rational<long>.FromDecimal(0.33m, out r, 28, true);
// then r == 33 / 100;
การกำจัดระยะเวลาขั้นต่ำ:
Rational<long>.FromDecimal(0.123412m, out r, 28, true, 1.5m));
// then r == 1234 / 9999;
Rational<long>.FromDecimal(0.123412m, out r, 28, true, 1.6m));
// then r == 123412 / 1000000; because of minimu repeating of period is 0.1234123 in this case.
การปัดเศษในตอนท้าย:
Rational<long>.FromDecimal(0.8888888888888888888888888889m, out r));
// then r == 8 == 9;
กรณีที่น่าสนใจที่สุด:
Rational<long>.FromDecimal(0.12345678m, out r, 28, true, 2, 9);
// then r == 12345678 / 100000000;
Rational<long>.FromDecimal(0.12345678m, out r, 28, true, 2, 8);
// Conversation failed, because of period has not been founded and there are too many digits in fraction part of input value.
Rational<long>.FromDecimal(0.12121212121212121m, out r, 28, true, 2, 9));
// then r == 4 / 33; Despite of too many digits in input value, period has been founded. Thus it's possible to convert value to fraction.
การทดสอบอื่น ๆ และรหัสทุกคนสามารถหาในMathFunctions ของฉันห้องสมุดบน GitHub
.33
=>"1/3"
เกี่ยวข้องกับฉัน; ผมจะคาดหวัง=>.33
"33/100"
ฉันคิดว่าคุณหมายถึง.33...
แน่นอน แต่มันทำให้เกิดปัญหากับคำถาม - ก่อนที่เราจะสามารถจัดการกับอัลกอริทึมที่เราต้องตัดสินใจเกี่ยวกับพฤติกรรมที่คาดหวัง คำตอบ Python ของ @ Debilski ใช้.limit_denominator()
ค่าเริ่มต้นเป็นตัวหารสูงสุดที่ 10 ^ 7; อาจเป็นค่าเริ่มต้นที่ดีในทางปฏิบัติ แต่สิ่งนี้ยังสามารถทำให้เกิดข้อบกพร่องได้หากคุณไม่ระวังและจะกลับมา"33/100"
ใน.33
กรณีนี้