Delphi 中的四舍五入算法,辗转曲折! (原创)

Delphi在默认情况下,使用的是银行家四舍五入法。所谓银行家舍入法,其实质是一种四舍六入五留双(又称四舍六入五奇偶)法。其规则是:当舍去位的数值小于5时,直接舍去该位;当舍去位的数值大于等于6时,在舍去该位的同时向前位进一;当舍去位的数值等于5时,如果前位数值为奇,则在舍去该位的同时向前位进一,如果前位数值为偶,则直接舍去该位,简单总结就是“奇进偶不进”。
运行下面程序,返回的结果是:

uses
  math;

var
  F1, F2, f3, f4: Extended;
  str: string;
begin
  F1 := 0.115;
  F2 := 0.105;
  f3 := RoundTo(F1, -2);
  f4 := RoundTo(F2, -2);
  str := Format('RoundTo(%.3f, -2)= %.3f '#13#10'RoundTo(%.3f, -2)= %.3f', [F1, F3, F2, F4]);
  ShowMessage(str);
  // 运行结果
  // RoundTo(0.115, -2)= 0.120 
  // RoundTo(0.105, -2)= 0.100
end;

要想使用常规的四舍五入,可以事先调用 SetRoundMode 函数,使用 rmUp 参数即可。

var
  F1, F2, f3, f4: Extended;
  str: string;
begin
  F1 := 0.115;
  F2 := 0.105;

  SetRoundMode(rmUp); // rmNearest 模式即为银行家方式

  f3 := RoundTo(F1, -2);
  f4 := RoundTo(F2, -2);
  str := Format('RoundTo(%.3f, -2)= %.3f '#13#10'RoundTo(%.3f, -2)= %.3f', [F1, F3, F2, F4]);
  ShowMessage(str);
  // 运行结果
  // RoundTo(0.115, -2)= 0.120 
  // RoundTo(0.105, -2)= 0.110
end;

经实践检验,上述方法无法解决问题,虽然对 0.115 0.105 取两位小数都正确,但是对 0.594 取两位小数却得到 0.60!
网上各种实现方法都不够优雅,最后实验出以下方法,应该是最好的答案了,简单总结一下,就是 SetRoundMode(rmUp)SimpleRoundTo 配合使用,单独使用 SimpleRoundTo 是不行的。

var
  F1, F2, f3, f4, f5, f6: Extended;
  str: string;
begin
  SetRoundMode(rmUp);
  F1 := 0.115;
  F2 := 0.105;
  f3 := SimpleRoundTo(F1, -2);
  f4 := SimpleRoundTo(F2, -2);
  f5 := 0.594;
  f6 := SimpleRoundTo(f5, -2);
  str := Format('SimpleRoundTo(%.3f, -2)= %.3f '#13#10'SimpleRoundTo(%.3f, -2)= %.3f '#13#10'SimpleRoundTo(%.3f, -2)= %.3f', [F1, F3, F2, F4, F5, f6]);
  memo1.Text := str;
  // 运行结果
  //SimpleRoundTo(0.115, -2)= 0.120
  //SimpleRoundTo(0.105, -2)= 0.110
  //SimpleRoundTo(0.594, -2)= 0.590
end;

总结:
虽然按文档中的说法 SimpleRoundTo 使用的是传统的四舍五入算法,满五总进一,但是经验证, SimpleRoundTo(0.105, -2) = 0.10(win7, Delphi7)。只有在调用 SimpleRoundToSetRoundMode() 才行,其中 SetRoundMode() 的参数如果使用 rmNearest 会使 SimpleRoundTo 仍然得到银行家四舍五入的结果,而其余三个参数 rmDown rmUp rmTruncate 都可以取得常规四舍五入法的结果。

posted @ 2024-10-02 14:49  汉学  阅读(320)  评论(2)    收藏  举报