一、Delphi中的四舍五入法

    四舍五入是一种应用非常广泛的近似计算方法,针对不同的应用需求,其有算术舍入法和银行家舍入法两种。

    所谓算术舍入法,就是我们通常意义上的四舍五入法。其规则是:当舍去位的数值大于等于5时,在舍去该位的同时向前位进一;当舍去位的数值小于5时,则直接舍去该位。

    所谓银行家舍入法,其实质是一种四舍六入五留双(又称四舍六入五奇偶)法。其规则是:当舍去位的数值小于5时,直接舍去该位;当舍去位的数值大于等于6时,在舍去该位的同时向前位进一;当舍去位的数值等于5时,如果前位数值为奇,则在舍去该位的同时向前位进一,如果前位数值为偶,则直接舍去该位。

    综上所述,两种舍入法所得结果不尽一致,因此在使用时必须根据实际需要加以区别。否则会出现一些莫明其妙的偏差。

    二、Delphi中的四舍五入函数

    众所周知,Delphi中有一个四舍五入取整函数Round。但它是按银行家舍入法的规则实施舍入操作的,Delphi中没有按算术舍入法规则实施舍入操作的四舍五入取整函数。

在Delphi中使用四舍五入函数一直是使用Round,可是有时候发现,使用它得到的答案与我们预期的会不太一样。

举例:

i := Round(11.5)    结果: i=12

i := Round(10.5)    结果: i=10

是的,按照我们的预期,第二个函数应该返回11才对,可是,为什么会这样呢?

对于XXX.5的情况,整数部分是奇数,那么会Round Up,偶数会Round Down。难道是Delphi的bug?

No!! 让我们看看<<Pascal精要>>上的一句话:

"在最近版本的Delphi Pascal 编译器中,Round 函数是以 CPU 的 FPU (浮点部件) 处理器为基础的。这种处理器采用了所谓的 "银行家舍入法",即对中间值 (如 5.5、6.5) 实施Round函数时,处理器根据小数点前数字的奇、偶性来确定舍入与否,如 5.5 Round 结果为 6,而 6.5 Round 结果也为6, 因为 6 是偶数"。

Round函数其实使用的银行家算法进行运算的,统计学上一般也是使用这种算法的,这比我们传统的四舍五入方法要科学,可是,如果我们要使用传统的四舍五入的方法,该如何解决呢?

有人是这样解决的,给10.5加上一个很微小的数值,再调用Round函数,这样在不影响精度的同时,就得到了正确的结果,貌似不错,可这始终是治标不治本的方法,有没有更正统的解决方法呢?

在网上又搜到了一个函数:

function DoRound(Value: Extended): Int64;
procedure Set8087CW(NewCW: Word);
asm
MOV Default8087CW,AX
FNCLEX
FLDCW Default8087CW
end;
const
RoundUpCW = $1B32;
var
OldCW : Word;
begin
OldCW := Default8087CW;
try
Set8087CW(RoundUpCW);
Result := Round(Value);
finally
Set8087CW(OldCW);
end;
end;

先解释一下8087CW, 全称是8087 control word。它是CPU中浮点单元(FPU)控制器控制字的值,设置8087CW,会改变FPU的精度,舍入模式,以及运算出错时是否产生异常。

上面程序的思路很简单,就是先保存8087CW,然后设置它为Round Up,这样偶数时就不会Round Down了,最后再还原8087CW。

其实上面的函数还可以简化,因为System单元里已经提供了Set8087CW的实现,所以程序简化为

function DoRound(Value: Extended): Int64;
const
RoundUpCW = $1B32;
var
OldCW : Word;
begin
OldCW := Default8087CW;
try
Set8087CW(RoundUpCW);
Result := Round(Value);
finally
Set8087CW(OldCW);
end;
end;

到这里为止,这篇文章可以告一段落了,可是,最后才发现其实Borland早就想到我们会遇到这样的问题,想到我们需要定制FPU的舍入模式,所以它提供了现成的函数供我们使用。在Math单元里,有一个SetRoundMode函数。于是我们可以做一下封装:

function RoundEx(Value: Extended; RoundMode: TFPURoundingMode = rmUp): Int64;
var
RM: TFPURoundingMode;
begin
RM := GetRoundMode;
try
SetRoundMode(RoundMode);
Result := Round(Value);
finally
SetRoundMode(RM);
end;
end;

举例:

i := RoundEx(11.5)    结果: i=12

i := RoundEx(10.5)    结果: i=11

嗯,这样对了吧,如果我设置成其它RoundMode会怎样呢?

举例:

i := RoundEx(11.5, rmTruncate)    结果: i=11

i := RoundEx(10.5, rmTruncate)    结果: i=10

版权声明:本文为博主原创文章,未经博主允许不得转载。

Delphi中的四舍五入函数的更多相关文章

  1. Delphi中使用@取函数地址的问题(转)

    Delphi中使用@取函数地址的问题   例如以下代码:unit Unit1;interfaceuses  Windows, Messages, SysUtils, Variants, Classes ...

  2. delphi中Application.MessageBox函数用法详解

    delphi中Application.MessageBox函数用法详解 Application.MessageBox是TApplication的成员函数,声明如下:functionTApplicati ...

  3. Delphi中的Val函数和iif函数(出错的时候,会有索引值)

    在delphi中Val是一个将字符串转换为数字的函数,Val(S; var V; var Code: Integer)第一个参数是要转换的字符串,第二个参数存放转换后的数字,可以是整数或浮点数,第三个 ...

  4. DELPHI 中的Delay函数,利用GetTickCount和Application.ProcessMessages构建

      作者 关劲松           delphi 开发中有些时候需要停留片刻,等待界面输入,或异步操作完成,如果使用sleep函数的话,整个程序都会停顿,界面还会出现冻结的情况.因此需要自行编写一个 ...

  5. FastReport调用Delphi中的自定义函数(人民币大写金额)mtm

    1. 在 FormCreate 中向FastReprot添加函数 (fPrint)窗口 procedure TfPrint.FormCreate(Sender: TObject); frxReport ...

  6. Delphi中的Rtti函数

    TTypeKind,类型类别,tkclass,tkinteger,tkstring等.类,属性都是一种类型. ttypedata,是一个record包括ttypekind.是一个类的描述.TTypeK ...

  7. 在Delphi中实现HexToStr函数和StrToHex函数

    function TransChar(AChar: Char): Integer; begin '] then Result := Ord(AChar) - Ord(') else Result := ...

  8. Delphi 中的 IfThen 函数

    问题来源: http://www.cnblogs.com/del/archive/2008/11/14/1120015.html#1370413 StrUtils 单元和 Math 单元 分别有一个 ...

  9. js中的四舍五入函数

    刚学到这部分时,感觉特别简单.可最近写个ajax分页时,忽然忘记应该怎么使用哪种函数来计算总的页数...哎,好记星不如烂笔头啊,还是老老实实的写下来吧.随时查看. 1.Math.ceil(x):对x向 ...

随机推荐

  1. 关于通过adb启动Activity、activity、service以及发送broadcast的命令

    一.启动activity: $ adb shell$ am start -n {包名}/{包名}.{活动名称} 如:启动一个名叫MainActivity的活动 # am start -n com.ex ...

  2. qt widget设置Qt::FramelessWindowHint和Qt::WA_TranslucentBackground, 会出现一个bug: 在最小化后还原时界面停止刷新

    qt widget设置Qt::FramelessWindowHint和Qt::WA_TranslucentBackground, 会出现一个bug: 在最小化后还原时界面停止刷新 Widget wit ...

  3. Android进阶笔记11:ListView篇之ListView显示多种类型的条目(item)

    ListView可以显示多种类型的条目布局,这里写显示两种布局的情况,其他类似. 1. 这是MainActivity,MainActivity的布局就是一个ListView,太简单了这里就不写了,直接 ...

  4. hello world from hibernate

    初次学习hibernate,第一个程序,用的是XML配置文件 准备工作:下载hibernate的JAR包,我下的是当前最新的4.2.3,SQL的数据库驱动文件SQLJDBC,导入上述架包.

  5. [改善Java代码]不使用stop方法停止线程

    线程启动完毕后,在运行可能需要终止,Java提供的终止方法只有一个stop,但是不建议使用此方法,因为它有以下三个问题: (1)stop方法是过时的 从Java编码规则来说,已经过时的方式不建议采用. ...

  6. Wince 文本函数和字体应用

    好像又进入了一个疲惫期了,晚上状态不好,但是还是想继续更新下博客,继上次分析了wince下设备环境以及怎么绘制相关图像后,,笔者在这片文章中讲到文本函数以及其相关应用.文本输出函数是输出文本的内容,也 ...

  7. Dubbo服务调用的动态代理和负载均衡

    Dubbo服务调用的动态代理及负载均衡源码解析请参见:http://manzhizhen.iteye.com/blog/2314514

  8. HttpClient(4.3.5) - HTTP Entity

    HTTP entity HTTP messages can carry a content entity associated with the request or response. Entiti ...

  9. 关于蓝牙设备与ios连接后,自动打开一个app

    How to launch an iphone app when an external accessory is either paired over BT or plugged into dock ...

  10. PHP 和Apache的安装和配置

    1. apache官网安装指南: http://jingyan.baidu.com/article/29697b912f6539ab20de3cf8.html 2. 官方安装包下载地址: http:/ ...