Delphi自带的SimpleRoundTo函数,我跟踪到函数内部,这个函数很简单,所有计算参数都正确,就是得出的结果是错误的
这个是Delphi自带的SimpleRountTo函数代码
  LFactor := IntPower(10, ADigit);
  if AValue < 0 then
    Result := Trunc((AValue / LFactor) - 0.5) * LFactor
  else
    Result := Trunc((AValue / LFactor) + 0.5) * LFactor;跟踪发现   AValue为43.065,  LFactor为0.01那么计算结果:   取整((43.065÷0.01)+0.5),再乘以0.01手工计算为43.07才对,跟中的变量都正确,但是函数返回的结果为43.06,我怀疑Trunc取整函数采取的舍入模式有问题不知道那个FPU寄存器是不是舍入模式的问题,用了SetRoundMode函数仍然得不到正确的结果。

解决方案 »

  1.   

    floor 和 ceil 是 math unit 里的函数,使用前要先 Uses Math。
    trunc 和 round 是 system unit 里的函数,缺省就可以用。
    floor 直接往小的取,比如 floor(-123.55)=-124,floor(123.55)=123
    trunc 直接切下整数,比如 trunc(-123.55)=-123, floor(123.55)=123
    ceil 直接往大的取,比如 ceil(-123.55)=-123, ceil(123.55)=124
    round 计算四舍五入,比如 round(-123.55)=-124,round(123.55)=124
      

  2.   

    我的意思是这段源代码我跟踪了,AValue 43.065,LFactor 0.01
    Result := Trunc((AValue / LFactor) + 0.5) * LFactor;
    那么手工计算的结果为43.07,但是函数返回的是43.06 ????
    不知道Trunc函数采取的舍入模式有没有问题??
      

  3.   

    应该先乘1000,用Trunc取整,除10取余,余数再取整,如果大于5,进位,小于5不进位。
    得自己判断。
    43.065浮点在机器里通常是43.064999999999999876(类似),所以不能进位。
    一些应用最好转换成整数在操作,不容易出错。
    请验证!
      

  4.   

    兄弟伙们,给个代码吧,几乎疯了,Delphi的自带浮点函数是不敢用了,浮点函数凡是涉及的舍入操作都几乎FPU的浮点舍入模式进行操作,就连Trunc函数也是取决于FPU的舍入模式,并不是简单的娶整数部分啊。
    现在只有期望基于字符串的四舍五入函数了。因为这个代码段的入口就是个Double类型的浮点数,在这个基础上进行操作的。网上找的基于字符串的四舍五入函数可以实现,不过不够灵活,不能舍入到任意位数,好想处理负数的四舍五入也有问题啊。
      

  5.   


    Function Roundx(X:real;N:integer):real;
    var i: integer;
    begin
         if N>0 then
          for i:=0 to N-1 do
           begin
             X:=X*10
           end;
            X:=round(X);
          for i:=0 to N-1 do
            begin
             x:=X/10;
             end;
        result:=X;
    end; 
      

  6.   

    round,roundto,simpleround,trunc,formatfloat
    这些函数delphi的编译器全部内部基于一个舍入控制原理(向上,向下,接近,取整,就是取整也是一种采用接近标志控制的舍入操作)实现,简单直接简单应用肯定都存在相同的的问题
      

  7.   


    楼上的代码运算 Roundx(43.065,2)返回结果是错误的43.06
    仍然有问题。我的机器是P4 3.0E,Delphi2007 .10471版本
      

  8.   

    谢谢,已解决,原来在运算过程中编译器使用的生成临时内存浮点变量用IEEE格式存储的时候,同样不是我们所看到的那样“精确”,也是近似存储一个值得,寄存器里面的数值有比较大的误差,只有手工。
    中间运算的结果我用调试器跟踪,得到感官期望的值:AValue 43.065,LFactor 0.01,
    Result := Trunc((AValue / LFactor) + 0.5) * LFactor; 
    但是运算结果不正确。
    充分说明(AValue / LFactor)这个表达式运算的临时变量在内存存储的时候被“近似”化了,紧接着
    Trunc((AValue / LFactor) + 0.5)产生的临时结果存储内存也被“近似”化了,这个只有跟踪寄存器才发现了,
    并不是想象的那样了。
    晕死,感谢大家的回答。不过我还是非常期望以及基于字符串的“四舍五入”解决方案,能个比较灵活的处理舍入任意位数,我做了个,不过遇到那个种“逢5进位“的时候,如果需要连续不断的进位,就不会写代码了。
    比如用基于字符串的四舍五入算法操作9.995,四舍五入到分,就不知道怎么写代码了
      

  9.   

    9.995FormatFloat('0.##',round(9.995*10)/10)
      

  10.   

    赞同 FormatFloat()不管是4.065还是9.995,此函数的返回结果都是正确的!返回类型也是字符串
      

  11.   

    FormatFloat['0.00',43.065]
    结果:43.07
      

  12.   

    直接format就是了,会四舍五入的
    FormatFloat('0.00',数值),两位小数
    FormatFloat('#.00',数值),两位小数,'#'的意思是忽略0,比如0.23将显示为.23
      

  13.   

    FormatFloat['0.##',43.065];
    FormatFloat['0.00',43.065];//非零以零补齐 
      

  14.   

    楼上的方法都貌似正确,不过都不可靠,或者说是错误的。--------------------错误的想当然逻辑,得到错误的运算结果---------------------------------
    比如:43.065四舍五入到分,那么
    1.乘以100,等于4306.5,
    2.再看小数点后面是不是大于0.5,如果是,则进一位,否则不进位,这里肯定要进位,于是结果4307.5
    3.然后取整,得到4307
    4.缩小100倍,于是得到43.07
    楼上的兄弟们的Round,FormatFloat的方案都基于这个正确的逻辑基础来成立,貌似正确,其实根本不可靠的。
    这样的计算根本就是一种对IEEE数据格式存储不了解造成的想当然的错误。--------------------真实存在的运算情况,得到的不可预知的结果-------------------------
    再看另外的一种情况,还是按刚才的步骤
    比如:43.065四舍五入到分,那么
    1.乘以100,等于4306.5,此时FPU的寄存器由于IEEE格式存储,结果可能是4306.50000001,也可能是4306.4999999999999
    2.再看小数点后面是不是大于0.5,如果是,则进一位,否则不进位,这里如果是4306.4999999999则不进位,于是结果4306.4999999
    3.然后取整,得到4306
    4.缩小100倍,于是得到43.06
    结果就是43.065四舍五入到分结果就是43.06分(当然也可能得到43.07分的结果,也就是结果是不可预期的)
    不知道楼上的兄弟们懂我的意思没,不要简单的使用RoundTo,SimpleRoundTo,FormatFloat这些函数,貌似结果正确,但也可能貌似结果完全不正确,所以我一直想找个可靠的基于字符串算法的“四舍五入”函数
      

  15.   

    总是直接简单的使用RoundTo,SimpleRoundTo,FormatFloat这些函数,得到的结果是不可预知或者不可控的。
    内存存储IEEE格式浮点数有误差的哟。
      

  16.   


    关键是你无法保证你的精度
    比如:43.065,你要只保留2位小数,代码运算中间的临时存储,这个是编程无法控制的,感官上你可以控制到小数点后面任意位数,实际上存储的可能是完全不可预知的数据到小数点后若干位,麻烦。
    比如:var f1,f2 : Double;
    f1:=43.065;
    f2:=f1;
    那么f2未必就是43.065知道吗?
    也就是下一步加入进行逻辑判断
    if f1=f2 then
    ...
    这个判断结果可能是True,也可能完全是False,这个是编程无法控制的,
    所以IEEE格式浮点数不能进行=,<>判断,也不提成<,>逻辑判断
      

  17.   


    只要运算过程中每一步的精度都高于结果要求的精度至少1位,就可以保证结果的精度。
    所谓“编程无法控制”,那是你不明道理,你给出一个数43.065,它只精确到小数点后第3位,当然不能拿它和不知道精确到小数点后多少位的数比较(虽然double本身的存储精度可以到小数点后15位,但是你没有装入真正精确到小数点后15位的数也没用)。
    只要控制到确定的精度,浮点数当然也可以进行< = > <>判断,if abs(f1 - f1) < 0.0005... 这就是精确到小数点后第3位的比较。
      

  18.   

    记得这个“BUG”的历史缘由是:
      银行计数规则产生的,什么偶数进位,奇数不进位还是相反,搞忘了