delphi 从小数中获取分数

bvjveswy  于 2022-11-04  发布在  其他
关注(0)|答案(4)|浏览(204)

我正在开发一个解方程组的程序。当它给我结果时,它就像:“x1= 1,36842”,我想得到“1,36842”的分数,所以我写了这段代码。

procedure TForm1.Button1Click(Sender: TObject);
var numero,s:string;
    a,intpart,fracpart,frazfatta:double;
    y,i,mcd,x,nume,denomin,R:integer;
begin
 a:=StrToFloat(Edit1.Text);  //get the value of a
 IntPart := Trunc(a);        // here I get the numerator and the denominator
 FracPart := a-Trunc(a);
 Edit2.Text:=FloatToStr(FracPart);
 numero:='1';
 for i:= 1 to (length(Edit2.Text)-2) do
 begin
  numero:=numero+'0';
 end;                       //in this loop it creates a string that has many 0 as the length of the denominator
 Edit3.text:=FloatToStr(IntPart);
 y:=StrToInt(numero);
 x:=StrToInt(Edit3.Text);
 while y <> 0 do
 begin
  R:= x mod y;
  x:=y;
  y:=R;
 end;
 mcd:=x;              //at the end of this loop I have the greatest common divisor
 nume:= StrToInt(Edit3.Text) div mcd;
 denomin:= StrToInt(numero) div mcd;
 Memo1.Lines.Add('fraction: '+IntToStr(nume)+'/'+IntToStr(denomin));
end;

它不能正确工作,因为它给我的分数是错误的。有人能帮助我吗?

thtygnil

thtygnil1#

您的程式码无法运作,因为您使用的是二进制浮点。而且二进制浮点型别无法表示您尝试表示的十进制数。可表示的二进制浮点数的格式为 s2e,其中 s 是有效数,e 是指数。因此,例如,您无法将0.1表示为二进制浮点值。
最明显的解决方案是使用整数运算来执行计算。根本不要调用StrToFloat。不要接触浮点运算。自己解析输入字符串。定位小数点。使用后面的位数来计算小数位数。去掉任何前导或尾随的零。然后使用整数运算来执行其余操作。
例如,假设输入为'2.79'。通过处理文本将其转换为分子和分母变量

Numerator := 279;
Denominator := 100;

显然,您必须编写字符串解析例程,而不是使用整数字面值,但这就是例程。
最后,通过求出这两个整数的gcd来完成问题。
底线是,要表示和操作十进制数据,你需要一个十进制算法。而这不包括二进制浮点。

wa7juj8i

wa7juj8i2#

我建议先定义一个函数GreaterCommonDivisor函数(wiki参考)
这将是Java/C类代码,因为我不熟悉 Delphi

float x = inputnum // where inputnum is a float
// eg. x = 123.56

然后,乘以

int n = 1;
while(decimalpart != 0){// or cast int and check if equal-> (int)x == x
    x = x * 10;
    decimalpart = x % 1;
    // or a function getting the decimal part if the cast does work
    n *= 10;
}

// running eg. x = 123.56 now x = 12356
//             n = 100

那么此时应该有(float)x/n == inputnum
这意味着你有一个分数,在这一点上可能不会被简化。

int gcd = GreaterCommonDivisor(x, n);
 // GreaterCommonDivisor(12356, 100) returns 4
 // therefore for correct implementation gcd = 4
 x /= gcd; // 12356 / 4 = 3089
 n /= gcd; // 100 / 4 = 25

这应该是快速和简单的实施,但:

主要缺陷:

  • 浮点数必须终止。例如,预期值0.3333333333333333333不会舍入为1/3
  • Float * n〈= max_int_value,否则会出现溢出,有一些解决方法,但可能有另一种解决方案更适合这些较大的数字
acruukt9

acruukt93#

Continued fractions可以用来找到真实的的有理近似值。下面是JavaScript中的一个实现,我相信移植到 Delphi 是很简单的:

function float2rat(x) {
    var tolerance = 1.0E-6;
    var h1=1; var h2=0;
    var k1=0; var k2=1;
    var b = x;
    do {
        var a = Math.floor(b);
        var aux = h1; h1 = a*h1+h2; h2 = aux;
        aux = k1; k1 = a*k1+k2; k2 = aux;
        b = 1/(b-a);
    } while (Math.abs(x-h1/k1) > x*tolerance);

    return h1+"/"+k1;
}

例如,1.36842会转换为26/19。
你可以找到一个实时演示和更多关于这个算法on my blog的信息。

6yoyoihd

6yoyoihd4#

@Joni我试了1/2,结果是“被零除”的错误;我更正循环,添加:

if b - a = 0 then BREAK;

要避免

b:= 1 / (b - a);

相关问题