Функция ABS не работает в Delphi

В Delphi6 или Delphi 2010 объявите две переменные типа Currency (vtemp1, vtemp2) и подайте им значение 0.09. Вставьте одну из переменных с функцией ABS и сравните ее с другой. Вы ожидали бы, что сравнение даст положительный результат, поскольку компилятор часы показывают одинаковое значение для abs (vtemp1) и vtemp2. Как ни странно, оператор if терпит неудачу.

Примечания: -Эта проблема возникает только при работе с число 0,09 (попытка нескольких других близких значений выявила нормальные результаты) -Удача переменной как Двойного вместо валюты, проблема перестает существовать.

3 ответа

Я думаю, что причина - это преобразования типов. Функция Abs() возвращает real результаты, поэтому переменная currency переходит к real. Взгляните на документацию:

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

поэтому валюта фиксирована, а реальная - с плавающей запятой. Пример кода для вашего вопроса:

program Project3;
{$APPTYPE CONSOLE}
const VALUE = 0.09;
var a,b : currency;
begin
 a := VALUE;
 b := VALUE;
 if a = Abs(b) then writeln('equal')
 else writeln('not equal', a - Abs(b));
 readln;
end.

дает не равный результат из-за преобразования типов;

часы-компиляторы показывают одинаковое значение для abs (vtemp1) и vtemp2

Попробуйте добавить x : real, затем вызовите x := abs(b);, добавьте x в список часов, выберите его и нажмите Edit watch, затем выберите пункт с плавающей запятой. x становится 0.899...967.

не только значение 0.09 дает такой результат. вы можете попробовать этот код, чтобы проверить:

for i := 0 to 10000 do begin
 a := a + 0.001;
 b := a;
 if a <> abs(b) then writeln('not equal', a);
 end;

поэтому, если вам нужно абсолютное значение переменной Currency - просто сделайте это. не используйте плавающие точки Abs():

function Abs(x : Currency):Currency; inline;
 begin
 if x > 0 then result := x
 else result := -x;
 end;


Немного разъяснений. "Проблема" появляется, если сравнивать значения float:

var
 A: Currency;
begin
 A:= 0.09;
 Assert(A = Abs(A));
end;

Это связано с тем, что Abs(A) возвращает значение float, а A = Abs(A) реализуется как сравнение с плавающей точкой.

Я не мог воспроизвести его, если сравниваются значения Currency:

var
 A, B: Currency;
begin
 A:= 0.09;
 B:= Abs(A);
 Assert(A = B);
end;

Но второй образец также является потенциальной ошибкой, потому что B:= Abs(A) внутренне является поплавковым делением/умножением на 10000 с округлением до Currency (int64) и зависит от режима округления FPU.

Я создал отчет qС# 107893, он был открыт.


Я только что обнаружил, что функция Delphi XE2 Abs не перегружает тип валюты.

Смотрите Delphi XE2 docwiki

это единственные типы, поддерживаемые abs

  • функция Abs (X:): Real; перегрузки;
  • функция Abs (X:): Int64; перегрузки;
  • function Abs (X:): Integer; перегрузки;

licensed under cc by-sa 3.0 with attribution.