あるとしましょう。0.33
出力する必要があります1/3
。
がある場合は0.4
、を出力する必要があります2/5
。
アイデアは、データをよりよく理解する方法として、ユーザーが「 yからxの部分」を理解できるようにすることです。
パーセンテージが良い代用であることは知っていますが、これを行う簡単な方法があるかどうか疑問に思いました。
あるとしましょう。0.33
出力する必要があります1/3
。
がある場合は0.4
、を出力する必要があります2/5
。
アイデアは、データをよりよく理解する方法として、ユーザーが「 yからxの部分」を理解できるようにすることです。
パーセンテージが良い代用であることは知っていますが、これを行う簡単な方法があるかどうか疑問に思いました。
David Eppstein による、与えられた実数のC コードに対する有理近似の発見が、まさにあなたが求めているものであることがわかりました。連分数の理論に基づいており、非常に高速でかなりコンパクトです。
特定の分子と分母の制限用にカスタマイズされたバージョンを使用しました。
/*
** find rational approximation to given real number
** David Eppstein / UC Irvine / 8 Aug 1993
**
** With corrections from Arno Formella, May 2008
**
** usage: a.out r d
** r is real number to approx
** d is the maximum denominator allowed
**
** based on the theory of continued fractions
** if x = a1 + 1/(a2 + 1/(a3 + 1/(a4 + ...)))
** then best approximation is found by truncating this series
** (with some adjustments in the last term).
**
** Note the fraction can be recovered as the first column of the matrix
** ( a1 1 ) ( a2 1 ) ( a3 1 ) ...
** ( 1 0 ) ( 1 0 ) ( 1 0 )
** Instead of keeping the sequence of continued fraction terms,
** we just keep the last partial product of these matrices.
*/
#include <stdio.h>
main(ac, av)
int ac;
char ** av;
{
double atof();
int atoi();
void exit();
long m[2][2];
double x, startx;
long maxden;
long ai;
/* read command line arguments */
if (ac != 3) {
fprintf(stderr, "usage: %s r d\n",av[0]); // AF: argument missing
exit(1);
}
startx = x = atof(av[1]);
maxden = atoi(av[2]);
/* initialize matrix */
m[0][0] = m[1][1] = 1;
m[0][1] = m[1][0] = 0;
/* loop finding terms until denom gets too big */
while (m[1][0] * ( ai = (long)x ) + m[1][1] <= maxden) {
long t;
t = m[0][0] * ai + m[0][1];
m[0][1] = m[0][0];
m[0][0] = t;
t = m[1][0] * ai + m[1][1];
m[1][1] = m[1][0];
m[1][0] = t;
if(x==(double)ai) break; // AF: division by zero
x = 1/(x - (double) ai);
if(x>(double)0x7FFFFFFF) break; // AF: representation failure
}
/* now remaining x is between 0 and 1/ai */
/* approx as either 0 or 1/m where m is max that will fit in maxden */
/* first try zero */
printf("%ld/%ld, error = %e\n", m[0][0], m[1][0],
startx - ((double) m[0][0] / (double) m[1][0]));
/* now try other possibility */
ai = (maxden - m[1][1]) / m[1][0];
m[0][0] = m[0][0] * ai + m[0][1];
m[1][0] = m[1][0] * ai + m[1][1];
printf("%ld/%ld, error = %e\n", m[0][0], m[1][0],
startx - ((double) m[0][0] / (double) m[1][0]));
}
Python 2.6 からfractions
モジュールがあります。
(ドキュメントから引用。)
>>> from fractions import Fraction
>>> Fraction('3.1415926535897932').limit_denominator(1000)
Fraction(355, 113)
>>> from math import pi, cos
>>> Fraction.from_float(cos(pi/3))
Fraction(4503599627370497, 9007199254740992)
>>> Fraction.from_float(cos(pi/3)).limit_denominator()
Fraction(1, 2)
出力が人間の読者に結果の順序の印象を与えることである場合、「113/211」のようなものを返すことは意味がないため、出力は1桁の数字の使用に制限する必要があります(おそらく1/ 10 および 9/10)。もしそうなら、27の異なる分数しかないことがわかります。
出力を生成するための基礎となる計算は決して変更されないため、関数が最大で log(27) ~= 4 3/4 の比較を実行するように、単純に二分探索ツリーをハードコーディングすることで解決できます。テスト済みの C バージョンのコードを次に示します。
char *userTextForDouble(double d, char *rval)
{
if (d == 0.0)
return "0";
// TODO: negative numbers:if (d < 0.0)...
if (d >= 1.0)
sprintf(rval, "%.0f ", floor(d));
d = d-floor(d); // now only the fractional part is left
if (d == 0.0)
return rval;
if( d < 0.47 )
{
if( d < 0.25 )
{
if( d < 0.16 )
{
if( d < 0.12 ) // Note: fixed from .13
{
if( d < 0.11 )
strcat(rval, "1/10"); // .1
else
strcat(rval, "1/9"); // .1111....
}
else // d >= .12
{
if( d < 0.14 )
strcat(rval, "1/8"); // .125
else
strcat(rval, "1/7"); // .1428...
}
}
else // d >= .16
{
if( d < 0.19 )
{
strcat(rval, "1/6"); // .1666...
}
else // d > .19
{
if( d < 0.22 )
strcat(rval, "1/5"); // .2
else
strcat(rval, "2/9"); // .2222...
}
}
}
else // d >= .25
{
if( d < 0.37 ) // Note: fixed from .38
{
if( d < 0.28 ) // Note: fixed from .29
{
strcat(rval, "1/4"); // .25
}
else // d >=.28
{
if( d < 0.31 )
strcat(rval, "2/7"); // .2857...
else
strcat(rval, "1/3"); // .3333...
}
}
else // d >= .37
{
if( d < 0.42 ) // Note: fixed from .43
{
if( d < 0.40 )
strcat(rval, "3/8"); // .375
else
strcat(rval, "2/5"); // .4
}
else // d >= .42
{
if( d < 0.44 )
strcat(rval, "3/7"); // .4285...
else
strcat(rval, "4/9"); // .4444...
}
}
}
}
else
{
if( d < 0.71 )
{
if( d < 0.60 )
{
if( d < 0.55 ) // Note: fixed from .56
{
strcat(rval, "1/2"); // .5
}
else // d >= .55
{
if( d < 0.57 )
strcat(rval, "5/9"); // .5555...
else
strcat(rval, "4/7"); // .5714
}
}
else // d >= .6
{
if( d < 0.62 ) // Note: Fixed from .63
{
strcat(rval, "3/5"); // .6
}
else // d >= .62
{
if( d < 0.66 )
strcat(rval, "5/8"); // .625
else
strcat(rval, "2/3"); // .6666...
}
}
}
else
{
if( d < 0.80 )
{
if( d < 0.74 )
{
strcat(rval, "5/7"); // .7142...
}
else // d >= .74
{
if(d < 0.77 ) // Note: fixed from .78
strcat(rval, "3/4"); // .75
else
strcat(rval, "7/9"); // .7777...
}
}
else // d >= .8
{
if( d < 0.85 ) // Note: fixed from .86
{
if( d < 0.83 )
strcat(rval, "4/5"); // .8
else
strcat(rval, "5/6"); // .8333...
}
else // d >= .85
{
if( d < 0.87 ) // Note: fixed from .88
{
strcat(rval, "6/7"); // .8571
}
else // d >= .87
{
if( d < 0.88 ) // Note: fixed from .89
{
strcat(rval, "7/8"); // .875
}
else // d >= .88
{
if( d < 0.90 )
strcat(rval, "8/9"); // .8888...
else
strcat(rval, "9/10"); // .9
}
}
}
}
}
}
return rval;
}
これは、小数を分数に変換する背後にある数学を説明するリンクです。
http://www.webmath.com/dec2fract.html
そして、これは実際に VB を使用してそれを行う方法のサンプル関数です ( www.freevbcode.com/ShowCode.asp?ID=582から):
Public Function Dec2Frac(ByVal f As Double) As String
Dim df As Double
Dim lUpperPart As Long
Dim lLowerPart As Long
lUpperPart = 1
lLowerPart = 1
df = lUpperPart / lLowerPart
While (df <> f)
If (df < f) Then
lUpperPart = lUpperPart + 1
Else
lLowerPart = lLowerPart + 1
lUpperPart = f * lLowerPart
End If
df = lUpperPart / lLowerPart
Wend
Dec2Frac = CStr(lUpperPart) & "/" & CStr(lLowerPart)
End Function
(Google 検索から: 小数を分数に変換、小数を分数コードに変換)
What Every Computer Scientist Should Know about Floating Point Arithmeticを読みたいと思うかもしれません。
大きな数を掛けて、精度を指定する必要があります。
3.141592 * 1000000 = 3141592
次に、分数を作成できます。
3 + (141592 / 1000000)
GCDを介して削減します...
3 + (17699 / 125000)
しかし、意図した分数を取り出す方法はありません。代わりに、コード全体で常に分数を使用することをお勧めします。オーバーフローを回避できる場合は、分数を減らすことを忘れないでください。
devinmooreによって提案されたVBコードのPerlおよびJavascriptバージョンは次のとおりです。
Perl:
sub dec2frac {
my $d = shift;
my $df = 1;
my $top = 1;
my $bot = 1;
while ($df != $d) {
if ($df < $d) {
$top += 1;
}
else {
$bot += 1;
$top = int($d * $bot);
}
$df = $top / $bot;
}
return "$top/$bot";
}
そして、ほぼ同じjavascript:
function dec2frac(d) {
var df = 1;
var top = 1;
var bot = 1;
while (df != d) {
if (df < d) {
top += 1;
}
else {
bot += 1;
top = parseInt(d * bot);
}
df = top / bot;
}
return top + '/' + bot;
}
//Put in your test number here:
var floatNumber = 2.56;
alert(floatNumber + " = " + dec2frac(floatNumber));
AC# の実装
/// <summary>
/// Represents a rational number
/// </summary>
public struct Fraction
{
public int Numerator;
public int Denominator;
/// <summary>
/// Constructor
/// </summary>
public Fraction(int numerator, int denominator)
{
this.Numerator = numerator;
this.Denominator = denominator;
}
/// <summary>
/// Approximates a fraction from the provided double
/// </summary>
public static Fraction Parse(double d)
{
return ApproximateFraction(d);
}
/// <summary>
/// Returns this fraction expressed as a double, rounded to the specified number of decimal places.
/// Returns double.NaN if denominator is zero
/// </summary>
public double ToDouble(int decimalPlaces)
{
if (this.Denominator == 0)
return double.NaN;
return System.Math.Round(
Numerator / (double)Denominator,
decimalPlaces
);
}
/// <summary>
/// Approximates the provided value to a fraction.
/// http://stackoverflow.com/questions/95727/how-to-convert-floats-to-human-readable-fractions
/// </summary>
private static Fraction ApproximateFraction(double value)
{
const double EPSILON = .000001d;
int n = 1; // numerator
int d = 1; // denominator
double fraction = n / d;
while (System.Math.Abs(fraction - value) > EPSILON)
{
if (fraction < value)
{
n++;
}
else
{
d++;
n = (int)System.Math.Round(value * d);
}
fraction = n / (double)d;
}
return new Fraction(n, d);
}
}
スターンブロコットツリーは、単純な分母を使用して実数を分数で近似するかなり自然な方法を導きます。
問題の一部は、非常に多くの分数が実際には分数として簡単に解釈されないことです。たとえば、0.33 は 1/3 ではなく、33/100 です。しかし、小学校での訓練を覚えていれば、10 進数を分数に変換するプロセスがありますが、ほとんどの場合、10 進数は 0.33 ではなく 0.329999999999998 などで保存されるため、希望どおりの結果が得られる可能性は低いです。
これを気にしないでください。ただし、必要な場合は、次のことを行うことができます。
小数部分を削除するまで、元の値に 10 を掛けます。その数を保持し、除数として使用します。次に、共通の分母を探して一連の単純化を行います。
したがって、0.4 は 4/10 になります。次に、低い値 (おそらく素数) から始まる公約数を探します。2 から始めて、除算の下限が除算自体と同じかどうかをチェックすることで、2 が分子と分母の両方を均等に除算するかどうかを確認します。
floor(5/2) = 2
5/2 = 2.5
したがって、5 は 2 を均等に分割しません。次に、次の数字、たとえば 3 をチェックします。これを、小さい方の数字の平方根以上になるまで行います。
あなたがそれをした後、あなたは必要です
これは「アルゴリズム」ではなく、単なる Python ソリューションです: http://docs.python.org/library/fractions.html
>>> from fractions import Fraction
>>> Fraction('3.1415926535897932').limit_denominator(1000)
Fraction(355, 113)
「0.33があるとしましょう。「1/3」を出力する必要があります。」
「ソリューション」にはどの程度の精度が期待できますか?0.33は1/3に等しくありません。「良い」(読みやすい)答えをどのように認識しますか?
いずれにせよ、可能なアルゴリズムは次のとおりです。
Yが10未満であるX/Yの形式で最も近い分数を見つけることが予想される場合は、Yごとに9つの可能なYをすべてループして、Xを計算し、最も正確なものを選択できます。
次の手順を使用して、任意のプログラミング言語でこれを行うことができます。
例:0.2 = 0.2 x 10 ^ 1/10 ^ 1 = 2/10 = 1/5
だから、それは「5分の1」と読むことができます
R の組み込みソリューション:
library(MASS)
fractions(0.666666666)
## [1] 2/3
これは連分数法を使用し、精度を調整するためのオプションcycles
と引数があります。max.denominator
受け入れても構わないと思っているエラーのレベルを把握する必要があります。すべての小数が単純な小数になるわけではありません。私はおそらく60のような簡単に割り切れる数を選び、その値に最も近い60分の1を計算してから、分数を単純化します。
Ruby にはすでに組み込みのソリューションがあります。
0.33.rationalize.to_s # => "33/100"
0.4.rationalize.to_s # => "2/5"
Rails では、ActiveRecord の数値属性も変換できます。
product.size = 0.33
product.size.to_r.to_s # => "33/100"
1 つの解決策は、最初からすべての数値を有理数として格納することです。有理数演算用のライブラリがあります (例: GMP )。OO 言語を使用している場合、有理数クラス ライブラリを使用して数値クラスを置き換えることができる場合があります。
とりわけ、金融プログラムは、このようなソリューションを使用して、正確な計算を行い、プレーンフロートを使用すると失われる可能性のある精度を維持できるようにします。
もちろん、かなり遅くなるので、実用的ではないかもしれません。実行する必要がある計算の量と、精度がどれほど重要かによって異なります。
a = rational(1);
b = rational(3);
c = a / b;
print (c.asFraction) ---> "1/3"
print (c.asFloat) ----> "0.333333"
これを行う最善の方法は、最初に float 値を ascii 表現に変換することだと思います。C++ では を使用できostringstream
、C では を使用できますsprintf
。C++ では次のようになります。
ostringstream oss;
float num;
cin >> num;
oss << num;
string numStr = oss.str();
int i = numStr.length(), pow_ten = 0;
while (i > 0) {
if (numStr[i] == '.')
break;
pow_ten++;
i--;
}
for (int j = 1; j < pow_ten; j++) {
num *= 10.0;
}
cout << static_cast<int>(num) << "/" << pow(10, pow_ten - 1) << endl;
ストレート C でも同様のアプローチを採用できます。
その後、分数が最低条件であることを確認する必要があります。このアルゴリズムは正確な答えを返します。つまり、0.33 は「1/3」ではなく「33/100」を出力します。ただし、0.4 は「4/10」となり、これを最小の項に減らすと「2/5」になります。これは、EppStein のソリューションほど強力ではないかもしれませんが、より簡単だと思います。
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.
他のテストとコードは、github の私の MathFunctions ライブラリで見つけることができます。
上記のコードを完成させ、as3に変換しました
public static function toFrac(f:Number) : String
{
if (f>1)
{
var parte1:int;
var parte2:Number;
var resultado:String;
var loc:int = String(f).indexOf(".");
parte2 = Number(String(f).slice(loc, String(f).length));
parte1 = int(String(f).slice(0,loc));
resultado = toFrac(parte2);
parte1 *= int(resultado.slice(resultado.indexOf("/") + 1, resultado.length)) + int(resultado.slice(0, resultado.indexOf("/")));
resultado = String(parte1) + resultado.slice(resultado.indexOf("/"), resultado.length)
return resultado;
}
if( f < 0.47 )
if( f < 0.25 )
if( f < 0.16 )
if( f < 0.13 )
if( f < 0.11 )
return "1/10";
else
return "1/9";
else
if( f < 0.14 )
return "1/8";
else
return "1/7";
else
if( f < 0.19 )
return "1/6";
else
if( f < 0.22 )
return "1/5";
else
return "2/9";
else
if( f < 0.38 )
if( f < 0.29 )
return "1/4";
else
if( f < 0.31 )
return "2/7";
else
return "1/3";
else
if( f < 0.43 )
if( f < 0.40 )
return "3/8";
else
return "2/5";
else
if( f < 0.44 )
return "3/7";
else
return "4/9";
else
if( f < 0.71 )
if( f < 0.60 )
if( f < 0.56 )
return "1/2";
else
if( f < 0.57 )
return "5/9";
else
return "4/7";
else
if( f < 0.63 )
return "3/5";
else
if( f < 0.66 )
return "5/8";
else
return "2/3";
else
if( f < 0.80 )
if( f < 0.74 )
return "5/7";
else
if(f < 0.78 )
return "3/4";
else
return "7/9";
else
if( f < 0.86 )
if( f < 0.83 )
return "4/5";
else
return "5/6";
else
if( f < 0.88 )
return "6/7";
else
if( f < 0.89 )
return "7/8";
else
if( f < 0.90 )
return "8/9";
else
return "9/10";
}
これを困難にする 2 つの基本的な問題があります。
1) 浮動小数点は正確な表現ではありません。つまり、"z" の値になる "x/y" の分数がある場合、分数アルゴリズムは "x/y" 以外の結果を返す可能性があります。
2) 無理数は有理数よりはるかに多く無限に存在します。有理数とは、分数で表すことができる数です。できないものは不合理です。
ただし、安価な方法では、浮動小数点の精度は限られているため、いつでも何らかの形で表すことができます。(おもう...)
これは、ブルート フォース アプローチを使用する JavaScript での迅速かつ汚い実装です。まったく最適化されていません。事前に定義された分数の範囲内で機能します: http://jsfiddle.net/PdL23/1/
/* This should convert any decimals to a simplified fraction within the range specified by the two for loops. Haven't done any thorough testing, but it seems to work fine.
I have set the bounds for numerator and denominator to 20, 20... but you can increase this if you want in the two for loops.
Disclaimer: Its not at all optimized. (Feel free to create an improved version.)
*/
decimalToSimplifiedFraction = function(n) {
for(num = 1; num < 20; num++) { // "num" is the potential numerator
for(den = 1; den < 20; den++) { // "den" is the potential denominator
var multiplyByInverse = (n * den ) / num;
var roundingError = Math.round(multiplyByInverse) - multiplyByInverse;
// Checking if we have found the inverse of the number,
if((Math.round(multiplyByInverse) == 1) && (Math.abs(roundingError) < 0.01)) {
return num + "/" + den;
}
}
}
};
//Put in your test number here.
var floatNumber = 2.56;
alert(floatNumber + " = " + decimalToSimplifiedFraction(floatNumber));
これは、JPS で使用されているアプローチに触発されています。
これはルビーの実装ですhttp://github.com/valodzka/frac
Math.frac(0.2, 100) # => (1/5)
Math.frac(0.33, 10) # => (1/3)
Math.frac(0.33, 100) # => (33/100)
多くの人が述べているように、浮動小数点を分数に変換することは実際にはできません (.25 のように非常に正確でない限り)。もちろん、分数の大きな配列に対してある種のルックアップを作成し、ある種のファジー ロジックを使用して探している結果を生成することもできます。繰り返しますが、これは正確ではなく、分母をどれだけ大きくしたいかの下限を定義する必要があります。
.32 < x < .34 = 1/3 またはそのようなもの。