StrToFloat не сообщает о недопустимых числах с плавающей запятой в Delphi 64bits

Следующий код, который пытается преобразовать значение, значительно превышающее диапазон двойной точности

StrToFloat('1e99999999')

правильно сообщает о некорректном значении с плавающей запятой в Delphi 10.2r3 с помощью компилятора Windows 32 bits, но при компиляции с компилятором Windows 64 бит он молча возвращает 0 (ноль).

Есть ли способ заставить StrToFloat сообщать об ошибке, когда значение с плавающей запятой неверно?

Я пробовал TArithmeticException.exOverflow, но в этом случае это не влияет.

Я также попробовал TArithmeticException.exPrecision, но он запускается во многих обычных случаях аппроксимации (fi запускается при преобразовании '1e9').

Проблема была замечена с обновлением Delphi 10.2 3

addendum: для решения этой проблемы я начал альтернативную реализацию чистой строки для двойного преобразования, исходную версию с тестами можно найти в dwscript commit 2ba1d4a

Ответы

Ответ 1

Это дефект, который присутствует во всех версиях Delphi, которые используют версию StrToFloat для StrToFloat. Это сопоставляется с InternalTextToExtended который читает экспоненту следующим образом:

function ReadExponent: SmallInt;
var
  LSign: SmallInt;
begin
  LSign := ReadSign();
  Result := 0;
  while LCurrChar.IsDigit do
  begin
    Result := Result * 10;
    Result := Result + Ord(LCurrChar) - Ord('0');
    NextChar();
  end;

  if Result > CMaxExponent then
    Result := CMaxExponent;

  Result := Result * LSign;
end;

Проблема заключается в

if Result > CMaxExponent then

Этот тест предназначен для внутри цикла, а в версии asm x86 этого кода он есть. Как указано выше, с максимальным показателем экспоненты вне цикла, 16-разрядное значение целочисленного значения со 99999999 слишком велико для вашего показателя 99999999. Когда показатель экспоненты считывается, значение в Result переполняется и становится отрицательным. Итак, для вашего примера получается, что используется показатель -7937 а не 99999999. Естественно, это приводит к нулю.

Это явная ошибка, и я представил отчет об ошибке: RSP-20333.

Что касается того, как обойти эту проблему, я не знаю о другой функции в Delphi RTL, которая выполняет эту задачу. Поэтому я думаю, вам нужно будет сделать одно из следующего:

  • StrToFloat свой собственный StrToFloat.
  • Предварительно обработайте строку и обработайте экспоненты диапазона до того, как они прочитают StrToFloat.
  • Используйте одну из функций из библиотеки времени выполнения C, которая выполняет одну и ту же задачу.

Наконец, я благодарен вам за то, что вы задали этот вопрос, потому что я вижу, что на мою собственную программу влияет этот дефект, и теперь я могу это исправить!

Обновить:

Вам также может быть интересно посмотреть на связанную ошибку, которую я обнаружил при расследовании: RSP-20334. Это может удивить вас, что StrToFloat('߀') при использовании версии StrToFloat возвращает 1936.0. Фокус в том, что символ, который передается StrToFloat является нелатинской цифрой, в данном случае U + 07C0.