• 改进delphi中的RoundTo函数


        delphi 7中自带数值四舍五入函数RoundTo(AVlaue, ADigit)存在一些不确定性情况,并非像帮助或者网络说的四舍六入五凑偶的规则,不信可以随便测试几个数据就会发现与你预期的不一样,比如33.015与33.035,修约2位小数,运行结果却是33.01与33.03。这主要是与浮点数的精度有关(有兴趣可以了解一下浮点数的存储结构,我之前有转载了一篇相关文章),我改进这个问题,较好的解决的前面的问题,同时执行速度较快,用法与RoundTo一样,代码如下:

    function IsVeryNear1(f: double): boolean;
    var    // 判断给定实数的小数部分是否无限接近1,根据浮点数的存储格式来判定
      f1: double;
      zs, i:integer;
      arr: array [1..8] of byte;
      pb: Pbyte;
      pfInt: Pint64;
      fInt, tmp1, tmp2:int64;
      p: Pointer;
    begin
      p := @f;
      pb := Pbyte(p);
      for i := 1 to 8 do
      begin
        arr[9 - i] := pb^;
        inc(pb);
      end;
      zs := ((arr[1] and $7f) shl 4) + ((arr[2] and $F0) shr 4) - 1023; //浮点数的指数
      if zs < -1 then   // 小数部分前几位全是零的情况
      begin
        result := false;
        Exit;
      end;
      pfInt := PInt64(p);
      fInt := pfInt^;
      fInt := ((fInt and $000fffffffffffff) or $0010000000000000);
      if (zs = -1) then
      begin
        if fInt = $001fffffffffffff then result := true
        else result := false;
      end
      else begin
        tmp1 := $000fffffffffffff;
        tmp2 := $001fffffffffffff;
        for i := 0 to zs do
        begin
          tmp2 := (tmp2 and tmp1);
          tmp1 := (tmp1 shr 1);
        end;
        if ((fInt and tmp2) = tmp2) then  result := true // 当小数部分全部为1时,理解为小数无限接近1
        else result := false;
      end;
    end;
    // 新的改进型四舍五入函数
    function NewRoundTo(const AValue: double; const ADigit: TRoundToRange): Double;
    var
      ef, f1, a2:  double;
      i, n: integer;
      a1, intV: int64;
      f_sign: boolean;
    begin
      if AValue = 0 then begin
        Result := 0;
        Exit;
      end;
      if ADigit < 0 then // 修约小数点之后的小数位
      begin
        if AValue > 0 then f_sign := true  // 正数
        else f_sign := false;              // 负数
        a1 := 1;
        for i := 1 to (-ADigit) do a1 := a1 * 10;
        ef := abs(AValue * a1 * 10);
        intV := trunc(ef);
        if isVeryNear1(ef) then inc(intV);  // 这一步是关键
        n := (intV mod 10);
        if (n > 4) then  intV := intV - n + 10
        else intV := intV - n;
        if f_sign then  ef := intV/(a1*10)
        else ef := -1.0*intV/(a1*10);
        result := ef;
        exit;
      end;
      if ADigit = 0 then
      begin
        if frac(AValue) >= 0.5 then ef := trunc(AValue) + 1
        else ef := trunc(AValue);
        result := ef;
        exit;
      end;
      if ADigit > 0 then
      begin
        result := roundTo(AValue, ADigit);
        exit;
      end;
    end;

    这里还有另外一个他人写的解决函数,但是执行速度比前面的函数慢了非常多,只针对小数进行了修约,如下:

    function RoundFloat(f: double; i: integer): double;
    var
      s: string;
      ef: Extended;
    begin
      if f = 0 then begin
        Result := 0;
        Exit;
      end;
      s := '#.' + StringOfChar('0', i);
      if s = '#.' then s := '#';
      ef := StrToFloat(FloatToStr(f)); //防止浮点运算的误差
      result := StrToFloat(FormatFloat(s, ef));
    end;

     

  • 相关阅读:
    大数据处理系列之(二)系统过载保护
    大数据处理系列之(一)Java线程池使用
    js实现递归菜单无限层
    treeTable实现排序
    spring-dm 一个简单的实例
    Equinox OSGi服务器应用程序的配置步骤 (支持JSP页面)
    Spring DM 2.0 环境配置 解决Log4j问题
    Spring.DM web开发环境搭建
    STL容器的排序
    排序例子
  • 原文地址:https://www.cnblogs.com/tangqs/p/2796782.html
Copyright © 2020-2023  润新知