Как проходит процедура STR Delphi / Borland Pascal STR? - PullRequest
4 голосов
/ 25 февраля 2010

Оба, Borland Pascal 7 и Delphi 2007, получили процедуру STR, которая принимает число, длину и точность и преобразует ее в строку, подобную этой:

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

Все в порядке, если округление не является неоднозначным, но если оно не (0,5 -> вверх или вниз?), Возникает проблема: кажется, что это зависит от типа данных с плавающей запятой в BP, но, по-видимому, непротиворечиво в Delphi 2007:

BP:

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. Это на самом деле не имеет значения для результата, но очень трудно определить важные различия.

Ответы [ 5 ]

4 голосов
/ 25 февраля 2010

Случаи d = 2.15 и d = 2.25 различны:

2.15 нельзя точно представить в формате с плавающей точкой, и поэтому невозможно сказать, как округляется значение, не анализируя двоичное представление значения с плавающей точкой в ​​данном формате с плавающей запятой;

2.25 точно представлено в формате с плавающей запятой, и результат округления должен быть предсказуемым;

Я проверил округление некоторых значений, которые точно представлены в формате с плавающей запятой, и обнаружил, что 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' }
2 голосов
/ 25 февраля 2010

Я думаю, что проблема, которую вы видите, состоит в том, что многие числа, которые могут быть представлены точно в десятичной записи, могут быть представлены только как повторяющиеся десятичные числа (двоичные числа?) В двоичном виде. Таким образом, может случиться так, что 2.15 не может быть точно представлен двойным числом, и что 2.14999999999234 (или что-то еще) является самым близким, который вы можете получить с двоичным представлением.

Так как ближайшее двоичное представление числа строго меньше 2,15, функция Str округляется вниз, а не вверх.

1 голос
/ 25 февраля 2010

Похоже на ошибку округления с плавающей запятой. Когда вы посмотрите на код сборки, сгенерированный в Delphi, вы увидите, что _Str2Ext вызывается для обеих операций, что преобразует 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 to начинается с. Это довольно распространено в преобразованиях с плавающей точкой. Не уверен, что с этим можно что-то сделать.

0 голосов
/ 25 февраля 2010

Обратите внимание, что здесь есть два аспекта.

Во-первых, ваше десятичное литеральное значение, возможно, округляется до двоичного числа с плавающей запятой. Это означает, что число, указанное в коде сборки, может незначительно отличаться от числа, которое вы записали. Если ближайший машинный номер немного меньше, может показаться, что значения, которые должны быть округлены в большую сторону, округлены до STR.

Во-вторых, результирующее двоичное число с плавающей запятой округляется с использованием округления, сконфигурированного в слове состояния FPU, которое, как мы надеемся, не было изменено внешними библиотеками.

0 голосов
/ 25 февраля 2010

Я исследовал это и обнаружил, что добавление 0,000001 даст правильный результат для двойников.

...