【问题标题】:Floating point number decimal point approximation in DelphiDelphi中的浮点数小数点逼近
【发布时间】:2015-11-17 18:18:35
【问题描述】:

在我的 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),但我需要近似值。

另一种解决方案是使用另一个字符串变量,但我不喜欢使用。

请给我建议。

【问题讨论】:

标签: delphi delphi-xe2


【解决方案1】:

当编译器告诉您没有接受您给它的参数的重载时,您应该做的第一件事是检查哪些重载可用。然后你会看到FormatFloat 的所有重载都期望第二个参数的类型为Extended。您正在传递FloatToStrF 的结果,它返回一个字符串。 (此外,当您调用FloatToStrF 时,您要求保留六位小数,因此您没有得到四舍五入到两位的值也就不足为奇了。)

在格式化之前不要将你的值转换为字符串; FormatFloat 已经这样做了。

VoucherValue := StrToFloatDef(FormatFloat('0.##', (RetailPrice/(1+(ServiceTax/100)) - ProcessingFee)), 0);

更好的是,如果字符串不是您真正想要的,则根本不要将您的值转换为字符串。您显然仍希望将数值 四舍五入 到一定数量,因此请致电 RoundTo。对于两位小数,第二个参数应该是 -2。

VoucherValue := RoundTo(RetailPrice/(1+(ServiceTax/100)) - ProcessingFee, -2);

【讨论】:

  • 更好的是,写一些更简单的子表达式,而不是看看你能在一行中塞进多少。
  • 我认为值得指出的是 RoundTo 将四舍五入到最接近的可表示值
【解决方案2】:

我怀疑真正的问题是您的价值无法体现,这个问题已经讨论过很多次了。您的值无法使用二进制浮点精确表示。

您有两个主要选择:

  • 保持类型和值不变,但在输出时格式化为两位小数。例如Format('%.2f', [Value])FormatFloat('0.##', Value)。与您在问题中所说的相反,FormatFloat 确实四舍五入到最近。
  • 使用十进制数据类型,因此可以准确地表示值。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多