博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
改进delphi中的RoundTo函数
阅读量:4676 次
发布时间:2019-06-09

本文共 2538 字,大约阅读时间需要 8 分钟。

    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;

 

 

转载于:https://www.cnblogs.com/tangqs/archive/2012/11/30/2796782.html

你可能感兴趣的文章
深入理解正则表达式
查看>>
php学习笔记-定义常量
查看>>
七周七语言:Io Day 2
查看>>
robotframework 下对于web弹出框的处理
查看>>
HTML5 Web 存储
查看>>
3.29上午
查看>>
spring(三)
查看>>
ofbiz最新版13.07.01环境搭建、安装(linux环境下)
查看>>
userlist.js:1 Uncaught ReferenceError: $ is not defined
查看>>
C语言-常量指针与指针常量
查看>>
20145303 《Java程序设计》第7周学习总结
查看>>
Linux内核如何装载和启动一个可执行程序
查看>>
Socket网络编程--epoll小结
查看>>
数据库索引的实现原理
查看>>
32-3Sum
查看>>
用MySQL实现微博关注关系的方案分析
查看>>
99个Gmail邀请函
查看>>
android入门之: SharedPreferences
查看>>
C语言文件操作
查看>>
python文件结构与import用法
查看>>