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)。只有在调用 SimpleRoundTo 前 SetRoundMode() 才行,其中 SetRoundMode() 的参数如果使用 rmNearest 会使 SimpleRoundTo 仍然得到银行家四舍五入的结果,而其余三个参数 rmDown rmUp rmTruncate 都可以取得常规四舍五入法的结果。

浙公网安备 33010602011771号