一、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. python学习笔记概述

    第一次接触python是因为一个项目需要做自动化测试,因为各种限制没有使用QTP,选择了开源的比较流行的selenium,但如果只是靠selenium进行录制脚本.修改脚本这个很多时候没办法满足需求, ...

  2. SQL Server 之 在数据库之间进行数据导入导出

    1.同一服务器上数据库之间进行数据导入导出 (1).使用 SELECT INTO 导出数据 在SQL Server中使用最广泛的就是通过SELECT INTO语句导出数据,SELECT INTO语句同 ...

  3. Npoi 导出Excel 下拉列表异常: String literals in formulas can't be bigger than 255 Chars ASCII

    代码: public static void dropDownList(string[] datas, string filePath) { HSSFWorkbook workbook = new H ...

  4. 安装DirectX SDK时出现Error Code:s1023 的解决方案

    刚刚安装DXSDK_Jun10时(下载地址:http://download.microsoft.com/download/A/E/7/AE743F1F-632B-4809-87A9-AA1BB3458 ...

  5. 使用JS制作一个鼠标可拖的DIV(三)——移动带图片DIV

    当DIV元素里,存在图片元素的时候,会使拖动出现异常. 因为图片元素本身就支持拖动,所以,只要在图片标签加入:draggable='false'. 如下: <div onmousedown=&q ...

  6. Linux 源码安装apache 与常见错误解决

    文档原位置 一.编译安装apache 1.解决依赖关系 httpd-2.4.4需要较新版本的apr和apr-util,因此需要事先对其进行升级. 升级方式有两种,一种是通过源代码编译安装,一种是直接升 ...

  7. 通过使用精简客户端,且不需要安装的客户端,配合PLSQL连接oracle数据库

    通过使用精简客户端,且不需要安装的客户端,配合PLSQL连接oracle数据库. 首先下载安装包在Oralce官方网站上下载Oracle Instantclient Basic package.地址如 ...

  8. [转]C# 获取系统文件图标

    //获取文件的图标 this.Icon = GetFileIcon(@"c:\gdiplus.dll"); //获取文件夹图标 this.Icon = GetDirectoryIc ...

  9. Spring(3.2.3) - Beans(6): 作用域

    Spring 支持五种作用域,分别是 singleton.prototype.request.session 和 global session. 作用域 说明  singleton (默认作用域)单例 ...

  10. Spring(3.2.3) - Beans(9): @Resoure & @Autowired

    @Resource 和 @Autowired 都是用来装配依赖的,它们之间有些异同. @Resoure @Resource 是 JSR-250 规范的注解. @Resource 可以标注在字段.方法上 ...