我在Delphi 2010中有一个四舍五入的奇数,其中一些数字在取整中四舍五入,但在formatfloat中向上取整。
我完全知道十进制数字的二进制表示形式有时会产生误导性的结果,但是在那种情况下,我希望formatfloat和roundto会给出相同的结果。
我也看到建议,这是应该使用“ Currency”的东西,但是正如您在下面看到的,Currency和Double给出的结果相同。
program testrounding; {$APPTYPE CONSOLE} {$R *.res} uses System.SysUtils,Math; var d:Double; c:Currency; begin d:=534.50; c:=534.50; writeln('Format: ' +formatfloat('0',d)); writeln('Roundto: '+formatfloat('0',roundto(d,0))); writeln('C Format: ' +formatfloat('0',c)); writeln('C Roundto: '+formatfloat('0',roundto(c,0))); readln; end.
结果如下:
Format: 535 Roundto: 534 C Format: 535 C Roundto: 534
我已经看过为什么RoundTo(87.285,-2)=> 87.28的结果,建议的补救措施似乎并不适用。
首先,我们可以Currency
从问题中删除,因为您使用的两个函数没有Currency
重载。该值将转换为IEEE754浮点值,然后遵循与Double
代码相同的路径。
首先让我们看一下RoundTo
。它是快速检查,使用调试器,或附加Writeln
说RoundTo(d,0) = 534
。这是为什么?
那么,该文档的RoundTo
说:
使用“银行家的舍入”将浮点值舍入为指定的数字或10的幂。
实际上,在实现过程中,RoundTo
我们看到舍入模式TRoundingMode.rmNearest
在恢复到其原始值之前已暂时切换到。仅当值恰好位于两个整数之间的一半时才应用舍入模式。我们这里就是这种情况。
因此,适用于银行家的四舍五入。这意味着当值恰好位于两个整数之间的一半时,舍入算法会选择相邻的偶数整数。
因此,有意义的是RoundTo(534.5,0) = 534
,您同样可以进行检查RoundTo(535.5,0) = 536
。
理解FormatFloat
是另一回事。坦率地说,它的行为有些模糊。它以不同平台不同的代码执行临时取整。例如,它在32位Windows上是汇编程序,但在64位Windows上是Pascal。总体方法似乎是采用浮点值的尾数,将其转换为整数,将其转换为文本数字,然后基于这些文本数字进行舍入。在执行舍入时,不会考虑当前的舍入模式,并且该算法似乎实现了从零策略开始的舍入一半。但是,即使对于所有可能的浮点值也无法稳健实现。它对于您的值正确运行,但对于尾数中位数更多的值,算法将崩溃。
实际上,众所周知,在设计上根本上破坏了在浮点值和文本之间进行转换的Delphi RTL例程。Delphi RTL中没有例程可以正确地从文本转换为浮点,或从浮点转换为文本。实际上,我最近已经实现了自己的转换例程,该例程可以根据其他语言运行时使用的现有开放源代码正确执行此操作。这些日子之一,我将四处散布此代码供他人使用。
我不确定您的确切需求是什么,但是如果您希望对舍入进行一些控制,那么如果您负责舍入,就可以这样做。尽管RoundTo
始终使用Banker的舍入,但是您可以改为使用Round
当前舍入模式。这将允许您使用所选的舍入算法(通过调用SetRoundMode
)执行舍入,然后可以将舍入后的值转换为文本。那是关键。将值保留为算术类型,进行四舍五入,并且仅在应用正确的舍入后的最后一刻才转换为文本。