【问题标题】:How can I avoid losing precision with ftFmtBcd?如何避免使用 ftFmtBcd 丢失精度?
【发布时间】:2017-12-12 20:46:09
【问题描述】:

documentation 说:

TFMTBCDField 封装了二进制编码十进制 (BCD) 字段常见的基本行为。 BCD 值比浮点数提供更高的精度和准确度。 BCD 字段通常用于存储和操作货币值。

不幸的是,我发现将这样一个字段与 Extended 值结合使用,我会失去精度(在本例中为两位数):如果我使用

BcdField.AsExtended := Value;

该值实际上被截断为四位数。我该怎么办?

完整示例:

procedure TForm1.Button1Click(Sender: TObject);
var
  LValue: Double;
  LDataset: TClientDataSet;
  LFieldDef: TFieldDef;
begin
  LValue := 1 / 3;
  LDataset := TClientDataSet.Create(self);
  try
    LFieldDef := LDataset.FieldDefs.AddFieldDef;
    LFieldDef.DataType := ftFMTBcd;
    LFieldDef.Size := 6;
    LFieldDef.Precision := 10;
    LFieldDef.Name := 'A';
    LDataset.CreateDataset;
    LDataset.Append;
    LDataset.FieldByName('A').AsExtended := LValue;
    LDataset.Post;
    ShowMessage(FloatToStr(LDataset.FieldByName('A').AsExtended));
    ShowMessage(FloatToStr(LValue));
  finally
    FreeAndNil(LDataset);
  end;
end;

输出(在消息框中):

0,3333
0,333333333333333

【问题讨论】:

  • 嗯,定义为ftExtended?
  • 好的,但大多数时候我们使用MSSQLNUMERIC(..) 字段,这导致delphi 端有ftFmtBcd 字段。我不想更改数据库中的字段...
  • 这似乎是西雅图的问题。刚在东京试过,很惊讶它的工作原理。可能与quality.embarcadero.com/browse/RSP-16200有关?
  • 试试LFieldDef.Size := 15; LFieldDef.Precision := 15;
  • 严格来说,使用返回TBcd 记录的TField.AsBCD 会更准确。但是可能有问题...我没有最新版本来检查源代码,但是当前文档指出:TFMTBCDField.ValueAsBCD 相同,后者又“返回转换后的 AsCurrency 的值到一个 TBcd 值”。这意味着一个错误,即通过Currency,一个专门用于支持比TBCD 更高精度的字段在任何情况下都被限制为Currency 精度。

标签: delphi precision delphi-10-seattle tclientdataset bcd


【解决方案1】:

严格来说,使用返回TBcd 记录的TField.AsBCD 会更准确。 TFmtBcdField 覆盖默认实现并返回准确 TBcd 记录。

TBcd 是一个记录结构,支持简单的算术运算符和IntegerDouble 的隐式转换。所以它应该适合大多数用途。

缺点是:

  • 由于缺少内置机器指令,数学运算可能会稍微慢一些。但对于需要精确表示的情况,这可能是一个合理的权衡。 根据需要进行基准测试和评估。
  • 某些采用DoubleInteger 参数的函数可能需要TBcd 重载实现。

一些相关的考虑:

  • 由于浮点表示的性质,如果您需要精确的精度,则不适合使用Double。请参阅Is floating point math broken? 了解更多信息。
  • Extended 的使用与Double 存在相同的问题,尽管额外的 2 个字节提供了更大的范围和更高的精度 - 它仍然是浮点数据类型。此外,Extended 也有其自身的问题。注意警告here
  • 如果您不需要精确的表示,那么您可以使用BcdToDouble 转换为Double。见BCD Support Routines
  • 在不需要超过 4 个小数但确实需要精确表示的情况下要考虑的另一个选项:使用 Currency 数据类型。它表示为一个 64 位整数,假设除以 10000,这就是它支持 4 个十进制数字的方式。

【讨论】:

    猜你喜欢
    • 2012-09-10
    • 2023-03-24
    • 2011-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-28
    • 1970-01-01
    • 2021-02-11
    相关资源
    最近更新 更多