一、四舍五入法
    四舍五入是一种应用非常广泛的近似计算方法,其有算术舍入法和银行家舍入法两种。
    所谓算术舍入法,就是我们通常意义上的四舍五入法。其规则是:当舍去位的数值大于等于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      //经过试用,其实是:只要有小数,无条件进1
嗯,这样对了吧,如果我设置成其它RoundMode会怎样呢?
举例:
i := RoundEx(11.5, rmTruncate)    结果: i=11  //经过试用,其实是:无条件舍去小数位
i := RoundEx(10.5, rmTruncate)    结果: i=10
RoundEx函数华丽的变身为Trunc函数了,是不是很有趣啊,哈哈!

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

  1. Delphi中的四舍五入函数

    一.Delphi中的四舍五入法     四舍五入是一种应用非常广泛的近似计算方法,针对不同的应用需求,其有算术舍入法和银行家舍入法两种.     所谓算术舍入法,就是我们通常意义上的四舍五入法.其规则 ...

  2. Delphi常用系统函数总结

    Delphi常用系统函数总结 字符串处理函数 Unit System 函数原型 function Concat(s1 [, s2,..., sn]: string): string; 说明 与 S : ...

  3. Delphi 使用之函数

    函数由一句或多句代码组成,可以实现某个特定的功能.使用函数可以使代码更加易读.易懂,加快编程速度及减少重复代码.过程与函数类似,过程与函数最重要的区别在于,过程没有返回值,而函数能有返回值.     ...

  4. Delphi Win API 函数 MulDiv

    Delphi Win API 函数 MulDiv 原型:function MulDiv(nNumber, nNumerator, nDenominator: Integer): Integer; st ...

  5. Delphi的Trim函数

    三个Trim函数简介 函数原型 function Trim(const S: string): string; 将字符串前后的空白及控制字元清掉. 注意Trim函数只能清掉字符串前后的空格及控制字元, ...

  6. Delphi中的函数指针判断是否为空

    delphi函数指针 只有@@p才代表了函数指针本身的地址   assigned(p) 判断是否为空 或者用 @p=nil 来判断函数指针是不是为空 Delphi中的函数指针实际上就是指针,只是在使用 ...

  7. (转)JS保留两位小数 四舍五入函数

    本文转载自:http://www.cnblogs.com/446557021/archive/2011/10/13/2211047.html js 四舍五入函数 toFixed(),里面的参数 就是保 ...

  8. JS保留两位小数 四舍五入函数

    js 四舍五入函数 toFixed(),里面的参数 就是保留小数的位数. <script language="javascript"> document.write(& ...

  9. php四舍五入函数(floor、ceil、round与intval)

    原文链接:php四舍五入函数(floor.ceil.round与intval) PHP(外文名: Hypertext Preprocessor,中文名:“超文本预处理器”)是一种通用开源脚本语言.语法 ...

随机推荐

  1. 封装一个自己的 Ajax小框架

    框架代码如下: // 使用封装方法的人只关心提供http的请求方法,url地址,数据,成功和失败的回调方法 // 类的构造定义,主要职责就是新建出 XMLHttpRequest 对象 var MyXM ...

  2. jQuery 快速入门教程

    内容目录 jQuery 入门 什么是jQuery 如何使用jQuery jQuery的运行原理 如何选择jQuery版本 ready() 准备就绪时执行代码 jQuery 核心:选取元素 使用jQue ...

  3. shell中的双引号,单引号,反引号

    在shell中引号分为三种:单引号,双引号和反引号. 单引号 ‘ 由单引号括起来的字符都作为普通字符出现.特殊字符用单引号括起来以后,也会失去原有意义,而只作为普通字符解释.单引号用于保持引号内所有字 ...

  4. [C#]Base使用小记

    base 关键字用于从派生类中访问基类的成员: • 调用基类上已被其他方法重写的方法. • 指定创建派生类实例时应调用的基类构造函数. 基类访问只能在构造函数.实例方法或实例属性访问器中进行. 从静态 ...

  5. less学习-语法(二)

    变量 @color1:#fff; 选择器  // Variables @mySelector: banner; // Usage .@{mySelector} { font-weight: bold; ...

  6. centos下redis安装

    下载redis http://www.redis.cn/download.html 下载php的redis扩展 https://github.com/phpredis/phpredis#install ...

  7. 斗地主你什么时候才会托管?(.NET中的托管于非托管)

    文章部分引自<.NET4.0面向对象编程漫谈(基础篇)>第1章.NET面向对象编程基础(作者:金旭亮) 无意间看到一位四五岁左右小朋友在玩斗地主,总开始到结束,她一直都在使用“提示”(托管 ...

  8. HDU1005 数列找规律

    Problem Description A number sequence is defined as follows: f(1) = 1, f(2) = 1, f(n) = (A * f(n - 1 ...

  9. Consistent Hashing原理与实现

    原理介绍: consistent hashing原理介绍来自博客:http://blog.csdn.net/sparkliang/article/details/5279393, 多谢博主的分享 co ...

  10. Servlet+Tomcat制作出第一个运行在Tomcat上的Java应用程序

    转载自:http://www.linuxidc.com/Linux/2011-08/41685.htm [日期:2011-08-27] 来源:csdn  作者:Cloudyxuq     1.IDE工 ...