高效准确的货币类型到整数的转换

Efficient and accurate conversion of currency type to integer

如果可能,我想避免在类似于以下的代码中将 Currency 转换为 Extended(并且可能会丢失精度):

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;

这部分代码

if rem>=factor div 2 then begin
  inc(Result);
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;