Баг или фича

Вопросы программирования на Free Pascal, использования компилятора и утилит.

Модератор: Модераторы

Баг или фича

Сообщение Osmiy » 11.04.2025 07:08:32

В консольной программе этот код вылетает с ошибкой
Код: Выделить всё
program Project1;

uses Math;

var
  R: Double =0;
begin
  if R=NaN then;
  ReadLn;
end. 


Это так и должно быть или это баг?

Lazarus 3.8 (rev lazarus_3_8) FPC 3.2.2 x86_64-win64-win32/win64
Osmiy
новенький
 
Сообщения: 44
Зарегистрирован: 07.05.2016 21:18:39

Re: Баг или фича

Сообщение Alexander » 11.04.2025 08:40:24

Это не простой вопрос, ведь формально тогда сравниваются нечисло и число. Да и зачем-то функция https://lazarus-ccr.sourceforge.io/docs ... isnan.html есть. В документации о сравнении с числами ничего не сказано.

FPC 3.3.1, Linux, gdb:

Код: Выделить всё
Reading symbols from ./Project1...
(gdb) r
Starting program: /tmp/Project1
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".

Program received signal SIGFPE, Arithmetic exception.
0x00005555555800ab in $main () at Project1.pas:8
8         if R=NaN then;
(gdb) bt
#0  0x00005555555800ab in $main () at Project1.pas:8


При такой проверке ошибки не происходит:

Код: Выделить всё
program Project1;
uses Math;
begin
if (0/0)=NaN then WriteLn('*');
end.
Аватара пользователя
Alexander
энтузиаст
 
Сообщения: 821
Зарегистрирован: 18.12.2005 19:10:00
Откуда: оттуда

Re: Баг или фича

Сообщение Osmiy » 11.04.2025 10:04:33

Alexander писал(а):функция https://lazarus-ccr.sourceforge.io/docs ... isnan.html есть.
Спасибо, то что надо.

Alexander писал(а):Это не простой вопрос, ведь формально тогда сравниваются нечисло и число.
Просто мой вычислительный код спокойно работал в LCL приложении, а когда я его в консоли стал использовать, все эти проверки посыпались. Не хорошо это.
Osmiy
новенький
 
Сообщения: 44
Зарегистрирован: 07.05.2016 21:18:39

Re: Баг или фича

Сообщение iskander » 11.04.2025 10:07:49

Osmiy писал(а):Это так и должно быть или это баг?

NaN - одно из особых состояний числа с плавающей точкой(нечисло), при попытке сравнения обычного числа с NaN возникает исключительная ситуация FPU.
Для проверки на NaN можно использовать хелпер из модуля SysUtils
Код: Выделить всё
  if d.IsNaN then
    ...

Если замаскировать исключение средствами модуля Math
Код: Выделить всё
  SetExceptionMask(GetExceptionMask + [exInvalidOp]);
  if d = NaN then
    ...   

то возникать оно не будет, но результатом такого сравнения всегда будет False.
iskander
энтузиаст
 
Сообщения: 614
Зарегистрирован: 08.01.2012 18:43:34

Re: Баг или фича

Сообщение Alex2013 » 11.04.2025 17:32:18

Мдя "тихие нечисла" вещь в себе . (Честно говоря никогда не думал, что есть возможность добраться до них из кода на паскале )
Зы
В чем смысл сравнения ? Это же состояние FPU по идее его вообще нельзя записать в переменную. :roll:
То есть после какой-то операции оно может вылезти "как фаг" но пустая примерная которая просто лежит в памяти ни как содержать нечисло не может . Именно в этом смысл этого кода if (0/0)=NaN then WriteLn('*'); что-то сделано и это можно проверить но записать нечисло в переменную по идее нельзя (впрочем я опыта использования NaN не имею так что могу ошибаться )
Зы Зы

IsNan (А)
https://lazarus-ccr.sourceforge.io/docs ... isnan.html
По идее верно так
Код: Выделить всё
program Project1;

uses Math;

var
  R: Double =0;
begin
  if IsNan( R ) then  WriteLn('*');
  ReadLn;
end.
Alex2013
долгожитель
 
Сообщения: 3126
Зарегистрирован: 03.04.2013 11:59:44

Re: Баг или фича

Сообщение xchgeaxeax » 11.04.2025 19:03:35

Alex2013 писал(а):Мдя "тихие нечисла" вещь в себе . (Честно говоря никогда не думал, что есть возможность добраться до них из кода на паскале )
Зы
В чем смысл сравнения ? Это же состояние FPU по идее его вообще нельзя записать в переменную.
То есть после какой-то операции оно может вылезти "как фаг" но пустая примерная которая просто лежит в памяти ни как содержать нечисло не может . Именно в этом смысл этого кода if (0/0)=NaN then WriteLn('*'); что-то сделано и это можно проверить но записать нечисло в переменную по идее нельзя (впрочем я опыта использования NaN не имею так что могу ошибаться )
Зы Зы

Вы правы, что NaN это состояние сопроцессора. Но формат Extended (10-ти байтовое) для x87 вполне может хранить NaN. Тогда как Double или Single такого не могут.
xchgeaxeax
постоялец
 
Сообщения: 177
Зарегистрирован: 11.05.2023 03:51:40

Re: Баг или фича

Сообщение iskander » 11.04.2025 19:11:49

xchgeaxeax писал(а):Вы правы, что NaN это состояние сопроцессора. Но формат Extended (10-ти байтовое) для x87 вполне может хранить NaN. Тогда как Double или Single такого не могут.

Но это не точно? :)
Код: Выделить всё
uses
  SysUtils, Math;
var
  d: Double;
  s: Single;
begin
  SetExceptionMask(GetExceptionMask + [exInvalidOp]);
  d := Double.NaN;
  s := Single.NaN;
  SetExceptionMask(GetExceptionMask - [exInvalidOp]);
  if d.IsNan then
    WriteLn('Ich bin Double NaN!');
  if s.IsNan then
    WriteLn('Ich bin Single NaN!');
end.
iskander
энтузиаст
 
Сообщения: 614
Зарегистрирован: 08.01.2012 18:43:34

Re: Баг или фича

Сообщение Sergei I. Gorelkin » 12.04.2025 07:28:41

Любое число с плавающей запятой, у которого все биты порядка равны 1, а биты мантиссы не равны 0 (за исключением скрытого бита), считается NaN.
Операции с NaN вызывают исключение, а если исключения замаскированы, то результатом будет тоже NaN.
Операции сравнения с NaN при замаскированных исключениях всегда имеют результат false, кроме сравнения на неравенство, которое дает true. В отношении сравнений FPC ведет себя аналогично GCC, но отличается от старых Delphi (насчет новых не в курсе).

Выражения вида if (0/0)=NaN ..., скорее всего, вычисляются во время компиляции и не вызывают исключений при выполнении.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Баг или фича

Сообщение iskander » 27.04.2025 10:46:19

Sergei I. Gorelkin писал(а):Операции с NaN вызывают исключение,

В 64-битной винде:
Код: Выделить всё
{$mode objfpc}
uses
  SysUtils, Math;
var
  d: Double;
begin
  SetExceptionMask(GetExceptionMask - [exInvalidOp]);
  d := Power(Double.NaN, 0);
  WriteLn(d);
  d := Abs(Double.NaN) + 42;
  WriteLn(d);
  ReadLn;
end.

печатает
Код: Выделить всё
1.0000000000000000E+000
                     Nan
iskander
энтузиаст
 
Сообщения: 614
Зарегистрирован: 08.01.2012 18:43:34

Re: Баг или фича

Сообщение Sergei I. Gorelkin » 28.04.2025 15:13:44

Power() - это не совсем вычисление степени в математическом смысле, в ней куча проверок граничных условий. В частности, при показателе степени 0 (а также -0) она ничего не вычисляет и возвращает единицу независимо от основания степени.
Аватара пользователя
Sergei I. Gorelkin
энтузиаст
 
Сообщения: 1407
Зарегистрирован: 24.07.2005 14:40:41
Откуда: Зеленоград

Re: Баг или фича

Сообщение RRYTY » 28.04.2025 16:45:35

:)
RRYTY
постоялец
 
Сообщения: 243
Зарегистрирован: 25.12.2021 10:00:32

Re: Баг или фича

Сообщение iskander » 28.04.2025 20:34:27

Sergei I. Gorelkin писал(а):Power() - это не совсем вычисление степени в математическом смысле, в ней куча проверок граничных условий. В частности, при показателе степени 0 (а также -0) она ничего не вычисляет и возвращает единицу независимо от основания степени.

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

И, да, функция Power() на осиновом пне видала все эти ваши стандарты:
Код: Выделить всё
{$mode objfpc}
uses
  SysUtils, Math;
var
  d, sn: Double;
begin
  SetExceptionMask(GetExceptionMask - [exInvalidOp]);
  QWord(sn) := (QWord($7ff) shl 52) + 42;//сигнальный NaN
  try
    d := Power(sn, 0);
    WriteLn('Expected error, but got ', d.ToString);
  except
    WriteLn('Seems Ok');
  end;
  try
    d := Power(1, Double.NaN);
    WriteLn('Expected 1, got ', d.ToString);
  except
    WriteLn('Expected 1, but got error');
  end;
  try
    d := Power(2, Double.NaN);
    WriteLn('Expected NaN, got ', d.ToString);
  except
    WriteLn('Expected NaN, but got error');
  end;
  ReadLn;
end.

выхлоп
Код: Выделить всё
Expected error, but got 1
Expected 1, but got error
Expected NaN, but got error

А что насчёт других функций модуля Math?

Модуль System:
Код: Выделить всё
{$mode objfpc}
uses
  SysUtils, Math;
var
  d: Double;
begin
  SetExceptionMask(GetExceptionMask - [exInvalidOp]);
  try
    d := Abs(Double.NaN);
    WriteLn('Abs(): ', d.ToString);
  except
    WriteLn('Abs() unexpected error');
  end;
  try
    d := Sqr(Double.NaN);
    WriteLn('Sqr(): ', d.ToString);
  except
    WriteLn('Sqr() unexpected error');
  end;
  try
    d := Sqrt(Double.NaN);
    WriteLn('Sqrt(): ', d.ToString);
  except
    WriteLn('Sqrt() unexpected error');
  end;
  try
    d := ArcTan(Double.NaN);
    WriteLn('ArcTan(): ', d.ToString);
  except
    WriteLn('ArcTan() unexpected error');
  end;
  try
    d := Ln(Double.NaN);
    WriteLn('Ln(): ', d.ToString);
  except
    WriteLn('Ln() unexpected error');
  end;
  try
    d := Sin(Double.NaN);
    WriteLn('Sin(): ', d.ToString);
  except
    WriteLn('Sin() unexpected error');
  end;
  try
    d := Cos(Double.NaN);
    WriteLn('Cos(): ', d.ToString);
  except
    WriteLn('Cos() unexpected error');
  end;
  try
    d := Exp(Double.NaN);
    WriteLn('Exp(): ', d.ToString);
  except
    WriteLn('Exp() unexpected error');
  end;
  try
    d := Round(Double.NaN);
    WriteLn('Round(): ', d.ToString);
  except
    WriteLn('Round() unexpected error');
  end;
  try
    d := Frac(Double.NaN);
    WriteLn('Frac(): ', d.ToString);
  except
    WriteLn('Frac() unexpected error');
  end;
  try
    d := Int(Double.NaN);
    WriteLn('Int(): ', d.ToString);
  except
    WriteLn('Int() unexpected error');
  end;
  try
    d := Trunc(Double.NaN);
    WriteLn('Trunc(): ', d.ToString);
  except
    WriteLn('Trunc() unexpected error');
  end;
  ReadLn;
end.

печатает
Код: Выделить всё
Abs(): Nan
Sqr(): Nan
Sqrt(): Nan
ArcTan(): Nan
Ln(): Nan
Sin() unexpected error
Cos() unexpected error
Exp(): Nan
Round() unexpected error
Frac(): Nan
Int(): Nan
Trunc() unexpected error
iskander
энтузиаст
 
Сообщения: 614
Зарегистрирован: 08.01.2012 18:43:34


Вернуться в Free Pascal Compiler

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 4

Рейтинг@Mail.ru