Есть ли настройка компилятора для управления тем, как литералы с плавающей точкой набираются в Delphi? - PullRequest
0 голосов
/ 01 июня 2018

Хотя случай для e работает по умолчанию, я бы хотел изменить приведение по умолчанию литерала 0.1, чтобы позволить r работать без каких-либо изменений кода.Возможно ли это через опцию компилятора, директиву компилятора или что-то еще?

procedure Test;
var
  s : Single;
  r : Real;
  d : Double;
  e : Extended;
begin
  s := 0.1;
  if (s = 0.1) then ShowMessage('s matched'); // fail

  r := 0.1;
  if (r = 0.1) then ShowMessage('r matched'); // fail

  d := 0.1;
  if (d = 0.1) then ShowMessage('d matched'); // fail

  e := 0.1;
  if (e = 0.1) then ShowMessage('e matched'); // pass
end;

Ответы [ 3 ]

0 голосов
/ 01 июня 2018

Проблема не в приведении, а в самом сравнении и в том, как кодируются числа с плавающей запятой.См., Например, эту статью блога, в которой подробно рассматривается значение 0,1 .Короче говоря, это значение 0,1 не кодируется как 0,1, а как 0,100000001490116119384765625 (одиночное) или 0,10000000000000000555111512312578270211815834045 (двойное) в формате IEEE-754 ...

Фактически, следующая строка

  if (s = 0.1) then ShowMessage('s matched'); // fail

компилируется (по крайней мере, в x87, т.е. Delphi 32-bit) как

  s := 0.1;
  e := 0.1;
  if s=e then
    writeln('ok');

И в стандартном кодировании IEEE 0.1 не сохраняется как расширенная или одинарная точность:

  s := 0.1;
  e := 0.1;
  writeln('s=',BinToHex(@s, SizeOf(s)));
  writeln('e=',BinToHex(@e, SizeOf(e)));
  //s=CDCCCC3D
  //e=CDCCCCCCCCCCCCCCFB3F

Принимая во внимание, что для 0.5 нет проблемы округления:

  s := 0.5;
  e := 0.5;
  writeln('s=',BinToHex(@s, SizeOf(s)));
  writeln('e=',BinToHex(@e, SizeOf(e)));
  if s=e then
    writeln(ok); // it works!
  // s=0000003F
  // e=0000000000000080FE3F

Вы можете принудительно выполнить сравнение следующим образом:

var s, s2: single;

  s := 0.1;
  s2 := 0.1;
  if s = s2 then
    writeln('ok');

Но в любом случае, для правильного сравнения значений с плавающей запятой, SameValue() можно использовать методы Math.pas с соответствующим эпсилоном.

0 голосов
/ 01 июня 2018

Нет переключателя компилятора, который делает то, что вы хотите.Проблема заключается в том, что литерал с плавающей запятой представляется как 10-байтовое значение расширенной точности 32-битным компилятором Windows.И поскольку 0.1 не является точно представимым, это не равно 8-байтовому представлению двойной точности 0.1.

В документации говорится:

Если constantExpression является вещественным, его тип равен Extended .

Однако вы можете достичь желаемого результата, используя введенную константу.Например:

const 
  TenthDouble: Double = 0.1;
var
  d: Double;
....
d := 0.1;
if d = TenthDouble then
  .... 

Используя типизированную константу, мы можем заставить компилятор сделать константу 8-байтовым представлением двойной точности 0.1.

0 голосов
/ 01 июня 2018

Не то чтобы я знал.По умолчанию сопроцессор x87 работает в расширенном режиме, поэтому компилятор адаптируется.

Обратите внимание, что если вы скомпилируете эту программу для совпадения 64-битных r, d и e (вероятно, потому что на 64-битном вещественном = двойном = расширенном).64-битный код не использует x87 для этого, но SSE2 согласно 64-битному ABI.

...