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

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

StrToFloat('1e99999999')

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

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

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

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

проблема была замечена с Delphi 10.2 update 3

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

1 ответов


это дефект, который присутствует во всех версиях Delphi, которые используют PUREPASCAL версию 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-версия этого кода. Как указано выше, при тесте max exponent вне цикла 16-битное целое число со знаком слишком мало для вашего показателя 99999999. По мере чтения экспоненты значение в Result переполняется, и становится отрицательным. Итак, для вашего примера получается, что показатель -7937 используется вместо 99999999. Естественно, это приводит к нулевому значению.

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

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

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

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

обновление:

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