Delphi 中的浮点数小数点近似
Floating point number decimal point approximation in Delphi
在我的 Delphi XE2 项目中,我使用一些真实变量来计算一些凭证相关数据。我写了以下代码:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Math;
type
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
Edit6: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
ServiceTax, RetailPrice, ProcessingFee, VoucherValue, AccountBalance, Airtimepercentage : real;
begin
RetailPrice := StrToFloatDef(Edit1.text, 0);
ServiceTax := StrToFloatDef(Edit2.text, 0);
if (RetailPrice*(10/100) <= 5) then ProcessingFee := RetailPrice*(10/100) else ProcessingFee := 5;
VoucherValue := (RetailPrice/(1+(ServiceTax/100)) - ProcessingFee);
AccountBalance := StrToFloatDef(Edit5.text, 0);
AirTimePercentage := (AccountBalance/VoucherValue)*100;
Edit3.Text := FloatToStrF(ProcessingFee, ffFixed, 16, 6);
Edit4.Text := FloatToStrF(VoucherValue, ffFixed, 16, 6);
Edit6.Text := FloatToStrF(AirTimePercentage, ffFixed, 16, 6);
end;
end.
但问题是VoucherValue
是一个浮点数。它包含一个很长的小数点,但我的要求是最多只有两个小数点,或者可能是一个长小数点,但在两个小数点之后(例如 12.19),所有数字都将为零(例如 12.190000)。所以我尝试了 FormatFloat
如下:
VoucherValue := StrToFloatDef(FormatFloat('0.##', FloatToStrF((RetailPrice/(1+(ServiceTax/100)) - ProcessingFee), ffFixed, 16, 6)), 0);
但是我无法编译并得到如下错误:
[dcc32 Error] Unit1.pas(46): E2250 There is no overloaded version of 'FormatFloat' that can be called with these arguments
FormatFloat
的另一个缺点是它可以截断(即 12.129999 到 12.12)但不能近似(即 12.129999 到 12.13)但我需要近似值。
另一个解决方案是使用另一个字符串变量,但我不喜欢使用。
请推荐我。
我怀疑真正的问题是你的价值无法体现,这个问题已经讨论了很多次了。您的值无法使用二进制浮点数准确表示。
您有两个主要选择:
- 保留类型和值不变,但在输出时将格式保留为两位小数。例如
Format('%.2f', [Value])
或 FormatFloat('0.##', Value)
。与您在问题中陈述的相反,FormatFloat
确实四舍五入到最接近的。
- 使用小数数据类型并准确表示值。
当编译器告诉您没有接受您给它的参数的重载时,您应该做的第一件事是检查可用的重载。然后您会看到 FormatFloat
的所有重载都期望第二个参数的类型为 Extended
。您正在传递 FloatToStrF
的结果,其中 returns 是一个字符串。 (此外,当您调用 FloatToStrF
时,您要求小数点后六位,因此您没有得到四舍五入到两位的值也就不足为奇了。)
在格式化之前不要将您的值转换为字符串;这就是 FormatFloat
已经做的事情。
VoucherValue := StrToFloatDef(FormatFloat('0.##', (RetailPrice/(1+(ServiceTax/100)) - ProcessingFee)), 0);
更好的是,如果字符串不是您真正想要的,则根本不要将您的值转换为字符串。您显然仍然想要一个数值 四舍五入 到一定数量,所以调用 RoundTo
就可以了。对于两位小数,第二个参数应该是-2.
VoucherValue := RoundTo(RetailPrice/(1+(ServiceTax/100)) - ProcessingFee, -2);
在我的 Delphi XE2 项目中,我使用一些真实变量来计算一些凭证相关数据。我写了以下代码:
unit Unit1;
interface
uses
Winapi.Windows, Winapi.Messages, System.SysUtils, System.Variants, System.Classes, Vcl.Graphics,
Vcl.Controls, Vcl.Forms, Vcl.Dialogs, Vcl.StdCtrls, System.Math;
type
TForm1 = class(TForm)
Edit1: TEdit;
Edit2: TEdit;
Edit3: TEdit;
Edit4: TEdit;
Edit5: TEdit;
Edit6: TEdit;
Label1: TLabel;
Label2: TLabel;
Label3: TLabel;
Label4: TLabel;
Label5: TLabel;
Label6: TLabel;
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject);
var
ServiceTax, RetailPrice, ProcessingFee, VoucherValue, AccountBalance, Airtimepercentage : real;
begin
RetailPrice := StrToFloatDef(Edit1.text, 0);
ServiceTax := StrToFloatDef(Edit2.text, 0);
if (RetailPrice*(10/100) <= 5) then ProcessingFee := RetailPrice*(10/100) else ProcessingFee := 5;
VoucherValue := (RetailPrice/(1+(ServiceTax/100)) - ProcessingFee);
AccountBalance := StrToFloatDef(Edit5.text, 0);
AirTimePercentage := (AccountBalance/VoucherValue)*100;
Edit3.Text := FloatToStrF(ProcessingFee, ffFixed, 16, 6);
Edit4.Text := FloatToStrF(VoucherValue, ffFixed, 16, 6);
Edit6.Text := FloatToStrF(AirTimePercentage, ffFixed, 16, 6);
end;
end.
但问题是VoucherValue
是一个浮点数。它包含一个很长的小数点,但我的要求是最多只有两个小数点,或者可能是一个长小数点,但在两个小数点之后(例如 12.19),所有数字都将为零(例如 12.190000)。所以我尝试了 FormatFloat
如下:
VoucherValue := StrToFloatDef(FormatFloat('0.##', FloatToStrF((RetailPrice/(1+(ServiceTax/100)) - ProcessingFee), ffFixed, 16, 6)), 0);
但是我无法编译并得到如下错误:
[dcc32 Error] Unit1.pas(46): E2250 There is no overloaded version of 'FormatFloat' that can be called with these arguments
FormatFloat
的另一个缺点是它可以截断(即 12.129999 到 12.12)但不能近似(即 12.129999 到 12.13)但我需要近似值。
另一个解决方案是使用另一个字符串变量,但我不喜欢使用。
请推荐我。
我怀疑真正的问题是你的价值无法体现,这个问题已经讨论了很多次了。您的值无法使用二进制浮点数准确表示。
您有两个主要选择:
- 保留类型和值不变,但在输出时将格式保留为两位小数。例如
Format('%.2f', [Value])
或FormatFloat('0.##', Value)
。与您在问题中陈述的相反,FormatFloat
确实四舍五入到最接近的。 - 使用小数数据类型并准确表示值。
当编译器告诉您没有接受您给它的参数的重载时,您应该做的第一件事是检查可用的重载。然后您会看到 FormatFloat
的所有重载都期望第二个参数的类型为 Extended
。您正在传递 FloatToStrF
的结果,其中 returns 是一个字符串。 (此外,当您调用 FloatToStrF
时,您要求小数点后六位,因此您没有得到四舍五入到两位的值也就不足为奇了。)
在格式化之前不要将您的值转换为字符串;这就是 FormatFloat
已经做的事情。
VoucherValue := StrToFloatDef(FormatFloat('0.##', (RetailPrice/(1+(ServiceTax/100)) - ProcessingFee)), 0);
更好的是,如果字符串不是您真正想要的,则根本不要将您的值转换为字符串。您显然仍然想要一个数值 四舍五入 到一定数量,所以调用 RoundTo
就可以了。对于两位小数,第二个参数应该是-2.
VoucherValue := RoundTo(RetailPrice/(1+(ServiceTax/100)) - ProcessingFee, -2);