Delphi 将货币类型高效准确地转换为整数

Delphi 将货币类型高效准确地转换为整数,delphi,precision,type-conversion,Delphi,Precision,Type Conversion,如果可能,我希望避免将货币转换为扩展货币,并避免在类似于以下代码的代码中丢失精度: function CurrencyToNumeric(aCurrency: Currency; aScale: Integer): Int64; const scales: array [-{18}5..-1] of int64 = (100000, 10000, 1000, 100, 10); var aCurrencyAsInt64: Int64 absolute aCurrency; begin

如果可能,我希望避免将货币转换为扩展货币,并避免在类似于以下代码的代码中丢失精度:

function CurrencyToNumeric(aCurrency: Currency; aScale: Integer): Int64;
const 
  scales: array [-{18}5..-1] of int64 = (100000, 10000, 1000, 100, 10); 
var
  aCurrencyAsInt64: Int64 absolute aCurrency;
begin
  if aScale = -4 then
    Result := aCurrencyAsInt64
  else
    Result := Round(aCurrency * scales[aScale]); // currency -> extended -> integer
end;

这可能吗?

我相信您正在寻找这样的函数:

function CurrencyToNumeric(aCurrency: Currency; aScale: Integer): int64;
var
  aCurrencyAsInt64: int64 absolute aCurrency;
  i, factor, rem: Integer;
begin
  if aScale <= -4 then begin
    factor := 1;
    for i := -4 downto aScale+1 do begin
      factor := factor * 10;
    end;
    Result := aCurrencyAsInt64 * factor;
  end else begin
    factor := 1;
    for i := -4 to aScale-1 do begin
      factor := factor * 10;
    end;
    Result := aCurrencyAsInt64 div factor;
    rem := aCurrencyAsInt64 mod factor;
    if rem>=factor div 2 then begin
      inc(Result);
    end;
  end;
end;
执行舍入策略。你很可能希望做出不同的选择。修改这段代码来做到这一点,它应该是显而易见的如何去做


然而,我也不相信问题中的版本是错误的。您是否有任何输入失败的示例?另一方面,避免将定点十进制类型转换为二进制浮点是明智的。现在,如果Embarcadero在不使用浮点运算的情况下实现这种该死的类型就好了

多亏了David的回答,我最终实现了以下实现,它不仅是无浮动的,而且比问题中的函数更快

function CurrencyToNumeric(Value: Currency; Scale: Integer): Int64;
const
  factors: array [-4..-1] of Int64 = (10000, 1000, 100, 10);
var
  factor: Integer;
  ValueAsInt64: Int64 absolute Value;
begin
  if Scale = -4 then
    Result := ValueAsInt64
  else if Scale < -4 then
    Result := ValueAsInt64 * factors[4 + Scale]
  else begin
    factor := factors[-(4 + Scale)];
    Result := ValueAsInt64 div factor;
    if ValueAsInt64 mod factor >= factor div 2 then Inc(Result);
  end;
end;

我看不出这里有什么问题。当你使用舍入时,定义上的精度损失很大,这使乘法中任何可能的舍入误差相形见绌。@Dsm如果乘法只取x.5的错误一侧,那么情况可能不是这样。我不认为这会发生在这里,但我也不认为证明这一点是微不足道的。@DavidHeffernan,是的,我想。当然,10000的除法可能会导致这个问题,而不是乘法,但您的解决方案解决了这个问题。对我来说,这看起来像是过度工程的严重案例,特别是在转换过程中超出货币精度和循环。在我看来,这样的事情是可能的,但我没有这样的例子。完全有可能我在试图消除某些隐式类型转换时做得太过分了。
function CurrencyToNumeric(Value: Currency; Scale: Integer): Int64;
const
  factors: array [-4..-1] of Int64 = (10000, 1000, 100, 10);
var
  factor: Integer;
  ValueAsInt64: Int64 absolute Value;
begin
  if Scale = -4 then
    Result := ValueAsInt64
  else if Scale < -4 then
    Result := ValueAsInt64 * factors[4 + Scale]
  else begin
    factor := factors[-(4 + Scale)];
    Result := ValueAsInt64 div factor;
    if ValueAsInt64 mod factor >= factor div 2 then Inc(Result);
  end;
end;