5

Borland Pascal 7 和 Delphi 2007 都有程序 STR,它接受一个数字、一个长度和精度,并将其转换为如下所示的字符串:

str(9.234:5:1, s); // -> s = '  9.2'

如果舍入没有歧义,一切都很好,但如果不是(0.5 -> 向上或向下?),则存在问题:它似乎取决于 BP 中的浮点数据类型,但在 Delphi 2007 中显然是一致的:

血压:

var
  e: extended;
  d: double;
begin
  d := 2.15;
  e := 2.15;
  str(d:5:1, s); { -> s = '  2.1' }
  str(e:5:1, s); { -> s = '  2.2' }
  { but: }
  d := 2.25
  e := 2.25
  str(d:5:1, s); { -> s = '  2.3' }
  str(e:5:1, s); { -> s = '  2.3' }

我找不到任何关于如何四舍五入的规则,而显然扩展总是四舍五入。

Delphi 2007 显然总是独立于数据类型四舍五入。

有人知道如何在 BP 中对双值进行舍入吗?

我想知道,因为我正在将一些使用双精度的 Borland Pascal 代码移植到 Delphi 2007,当我比较输出时,我得到了 STR 过程中舍入导致的不一致。这些对结果并不重要,但很难发现重要的差异。

4

5 回答 5

4

d = 2.15 和 d = 2.25 的情况不同:

2.15 不能精确地以浮点格式表示,因此如果不分析给定浮点格式中浮点值的二进制表示,就不可能说出该值是如何四舍五入的;

2.25以float格式精确表示,四舍五入的结果必须是可预测的;

我测试了一些以浮点格式精确表示的值的舍入,发现 STR 总是向上舍入正值,向下舍入负值。STR 不遵循“银行家的四舍五入”,例如:

  d := 2.25;
//  d:= roundto(d, -1);  banker's rounding is 2.2
  str(d:5:1, s); { -> s = '  2.3' }

  d:= 2.75;
//  d:= roundto(d, -1);  banker's rounding is 2.8
  str(d:5:1, s); { -> s = '  2.8' }
于 2010-02-25T17:12:16.817 回答
2

我认为您看到的问题是,许多可以用十进制表示法精确表示的数字只能表示为二进制的重复小数(二进制?)。所以,可能 2.15 不能用双精度表示,而 2.14999999999234 (或其他东西)是用二进制表示可以得到的最接近的值。

由于数字的最接近的二进制表示严格小于 2.15,因此 Str 函数向下舍入而不是向上舍入。

于 2010-02-25T15:39:13.930 回答
1

看起来像浮点舍入错误。当您查看在 Delphi 中生成的汇编代码时,您可以看到两个操作都调用了 _Str2Ext,它将 Extended 转换为字符串。因此,为了做到这一点,它必须在幕后将您的 Double 转换为 Extended:

Project1.dpr.16: str(d:5:1, s); { -> s = '  2.1' }
0040E666 DD45E8           fld qword ptr [ebp-$18]
0040E669 83C4F4           add esp,-$0c
0040E66C DB3C24           fstp tbyte ptr [esp]
0040E66F 9B               wait 

在从 Double 到 Extended 的转换中的某个地方,您会失去一点精度,并且最终得到的数字与您在开始时声明相同的数字(如我们所读的那样)为 Extended 时略有不同。这在浮点转换中很常见。不确定您是否可以做些什么。

于 2010-02-25T15:52:18.247 回答
0

请注意,这里有两个方面。

首先,您的十进制文字值可能会四舍五入为二进制浮点数。这意味着放在汇编代码中的数字可能与您写下的数字略有不同。如果最近的机器编号略小,则看起来应该向上舍入的值被 STR 向下舍入。

其次,使用 FPU 状态字中配置的舍入对生成的二进制浮点数进行舍入,希望外部库不会更改该舍入。

于 2010-02-25T16:06:59.920 回答
0

我对此进行了调查,发现添加 0.000001 会产生正确的双打结果。

于 2010-02-25T15:45:36.220 回答