Точность реалов через writeln / readln в Delphi - PullRequest
2 голосов
/ 29 сентября 2008

Моё клиентское приложение экспортирует и импортирует довольно много переменных типа real через текстовый файл, используя writeln и readln. Я попытался увеличить ширину написанных полей, чтобы код выглядел следующим образом:

writeln(file, exportRealvalue:30); //using excess width of field
....
readln(file, importRealvalue);

Когда я экспортирую, а затем снова импортирую и экспортирую и сравниваю файлы, я получаю разницу в двух последних цифрах, например (здесь может быть нет действительного количества цифр, но вы получите его):

-1.23456789012E-0002
-1.23456789034E-0002

Это на самом деле имеет значение в приложении, поэтому клиент хочет знать, что я могу с этим поделать. Теперь я не уверен, что это делает только запись / чтение, но я решил бросить быстрый вопрос, прежде чем снова погрузиться в стек. Нужно ли мне идти в этом бинарном?

Это не приложение, имеющее дело с валютой или чем-то, я просто пишу и читаю значения в / из файла. Я знаю, что с плавающей запятой иногда немного странно, и я подумал, что у одной из подпрограмм (writeln / readln) может быть какое-то забавное дело.

Ответы [ 6 ]

7 голосов
/ 29 сентября 2008

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

Из справки Delphi:

Основные типы Win32

                                            | Significant | Size in 
Type     | Range                            | digits      | bytes
---------+----------------------------------+-------------+----------
Real     | -5.0 x 10^–324 .. 1.7 x 10^308   | 15–16       |   8  
Real48   | -2.9 x 10^–39 .. 1.7 x 10^38     | 11-12       |   6   
Single   | -1.5 x 10^–45 .. 3.4 x 10^38     |  7-8        |   4   
Double   | -5.0 x 10^–324 .. 1.7 x 10^308   | 15-16       |   8   
Extended | -3.6 x 10^–4951 .. 1.1 x 10^4932 | 10-20       |  10   
Comp     | -2^63+1 .. 2^63–1                | 10-20       |   8   
Currency | -922337203685477.5808..          |             | 
                    922337203685477.5807    | 10-20       |   8   

Примечание : Шестобайтовый тип Real48 в предыдущих версиях Object Pascal назывался Real Если вы перекомпилируете код, который использует более старый шестибайтовый тип Real в Delphi, вы можете изменить его на Real48 . Вы также можете использовать директиву компилятора {$ REALCOMPATIBILITY ON} для преобразования Real обратно в шестибайтовый тип. Следующие замечания относятся к фундаментальным реальным типам.

  • Real48 поддерживается для обратной совместимости. Поскольку его формат хранения не является родным для архитектуры процессора Intel, это приводит к снижению производительности по сравнению с другими типами с плавающей запятой.
  • Extended предлагает большую точность, чем другие реальные типы, но менее переносимо. Будьте внимательны, используя Extended, если вы создаете файлы данных для совместного использования на разных платформах.

Обратите внимание, что диапазон больше значащих цифр. Таким образом, вы можете иметь число больше, чем может быть точно сохранено. Я бы рекомендовал округлить до значащих цифр, чтобы этого не происходило.

2 голосов
/ 30 сентября 2008

Если вы хотите указать точность реального с WriteLn, используйте следующее:

WriteLn(RealVar:12:3);

Выводит значение Realvar по крайней мере с 12 позициями и точностью до 3.

0 голосов
/ 16 октября 2008

Трудно ответить на этот вопрос, не зная, какого типа ваши ExportRealValue и ImportRealValue. Как уже упоминали другие, все реальные типы имеют разную точность.

Стоит отметить, что вопреки некоторым представлениям, расширенная не всегда более высокая точность. Выдвинутые 10-20 значимых цифр, где двойные 15-16. Поскольку у вас возникли проблемы с десятым сигом фиг, возможно, вы уже используете расширенный.

Чтобы получить больше контроля над чтением и записью, вы можете сами преобразовывать числа в и из строк и записывать их в поток файлов. По крайней мере, в этом случае вам не нужно беспокоиться, если readln и writeln не приносят никакой пользы за вашей спиной.

0 голосов
/ 01 октября 2008

В зависимости от того, сколько обработки вам нужно сделать, альтернативой может быть сохранение чисел в формате BCD для сохранения исходной точности.

0 голосов
/ 29 сентября 2008

Прежде всего, я бы попытался узнать, могу ли я получить какую-либо помощь от использования Str с другими аргументами или повышения точности типов в вашем приложении. (Вы пытались использовать Extended?)

В крайнем случае ( Внимание! Обходной путь !! ) Я бы попытался сохранить строковое представление клиента вместе с двоичным представлением в отсортированном списке. Перед записью значения с плавающей запятой я посмотрю, есть ли уже совпадающее значение в таблице, чье строковое представление уже известно и может использоваться вместо него. Чтобы ускорить поиск, вы можете отсортировать его по числовому значению и использовать двоичный поиск для поиска наилучшего соответствия.

0 голосов
/ 29 сентября 2008

При использовании типов с плавающей запятой вы должны знать об ограничениях точности для указанных типов. Например, 4-байтовый тип IEEE-754 имеет только около 7,5 значащих цифр точности. Восьмибайтовый тип IEEE-754 примерно удваивает количество значащих цифр. По-видимому, реальный тип delphi имеет точность, которая составляет около 11 значащих цифр. Результатом этого является то, что любые дополнительные цифры форматирования, которые вы укажете, могут быть шумом, который может привести к преобразованию между значениями в формате base 10 и значениями в формате base 2

...