Недействительные значения float и double
От: a9000  
Дата: 22.10.13 06:41
Оценка:
Подскажите как такое сделать:
Нужны некоторае константы типов float и double, которые бы выполняли функции "недействительного числа", по аналогии с NULL для недействительных указателей на объекты.
Такая константа могла бы храниться в переменных; с этим значением были бы допустимы сравнения (равно/неравно) и присваивания.
Но любые арифметические операции с использованием такого значения, а также любые встроенные функции типа sin(), cos() приводили бы к выбрасыванию исключения.

Что нибудь из "специальных" значений float подойдет? (SNAN, QNAN, бесконечности?)
Нашел такую штуку как _controlfp(_EM_INEXACT, _MCW_EM), после которой любая работа с "сигнальным" не-числами приводит к исключению. В частности, простая попытка присвоить не-число переменной вызывает исключение:
float n = std::numeric_limits<float>::signaling_NaN();

Но это мне не нужно! Нужно чтобы исключение вываливалось лишь при попытке арифметических операций с не-числами.
Re: Недействительные значения float и double
От: saf_e  
Дата: 22.10.13 08:18
Оценка:
Здравствуйте, a9000, Вы писали:

A>Подскажите как такое сделать:

A>Нужны некоторае константы типов float и double, которые бы выполняли функции "недействительного числа", по аналогии с NULL для недействительных указателей на объекты.
A>Такая константа могла бы храниться в переменных; с этим значением были бы допустимы сравнения (равно/неравно) и присваивания.
A>Но любые арифметические операции с использованием такого значения, а также любые встроенные функции типа sin(), cos() приводили бы к выбрасыванию исключения.

A>Что нибудь из "специальных" значений float подойдет? (SNAN, QNAN, бесконечности?)

A>Нашел такую штуку как _controlfp(_EM_INEXACT, _MCW_EM), после которой любая работа с "сигнальным" не-числами приводит к исключению. В частности, простая попытка присвоить не-число переменной вызывает исключение:
A>
float n = std::numeric_limits<float>::signaling_NaN();

A>Но это мне не нужно! Нужно чтобы исключение вываливалось лишь при попытке арифметических операций с не-числами.

boost::optional?
Re[2]: Недействительные значения float и double
От: a9000  
Дата: 22.10.13 08:37
Оценка:
Здравствуйте, saf_e, Вы писали:

_>boost::optional?


в проекте миллионы переменных float и double, все менять? Да меня за такое уволят.
Re[3]: Недействительные значения float и double
От: saf_e  
Дата: 22.10.13 08:46
Оценка:
Здравствуйте, a9000, Вы писали:

A>Здравствуйте, saf_e, Вы писали:


_>>boost::optional?


A>в проекте миллионы переменных float и double, все менять? Да меня за такое уволят.


Миллионы менять, конечно не вариант. Но если есть фиксированное кол-во мест, где интересует ткое поведение я бы задумался.

Как я понимаю есть конкретная причина возникновения такого решения, а остальное меняется "за компанию". boost::optional мог бы быть "хот-фикс" с созданием политики дальнейшего кодописания.

Как я понимаю любая операция с NAN (в том числе и сравнение) будет приводить к исключению. И потом вы проверили как с ними обстоят дела на x64?
Re: Недействительные значения float и double
От: watchmaker  
Дата: 22.10.13 11:08
Оценка:
Здравствуйте, a9000, Вы писали:

A>Такая константа могла бы храниться в переменных; с этим значением были бы допустимы сравнения (равно/неравно) и присваивания.

A>Но любые арифметические операции с использованием такого значения, а также любые встроенные функции типа sin(), cos() приводили бы к выбрасыванию исключения.
Ты хочешь странного. Чем сравнение хуже других арифметических операций? Почему вообще сравнение не должно считаться арифметической операцией? Это всё равно как если потребовать чтобы исключение выкидывалось при сложении двух чисел, но не при вычитании. Легкого пути сделать так не будет. Вот, например, на архитектуре x86 невыбрасывание исключения при копировании sNaN можно сделать, ибо это полезное действие с более-менее понятным сценарием пользования. И в таком случае твой код
A> float n = std::numeric_limits<float>::signaling_NaN();

выкинет исключение лишь при попытке использовать n в вычислениях, но не при попытке сохранить его в переменную или скопировать куда-то ещё.
Если же требуется выкидывать исключение только для определённого набора инструкций, а для другого игнорировать, то тебе придётся проверки добавлять самому везде где это нужно.
Re: Недействительные значения float и double
От: Кодт Россия  
Дата: 23.10.13 10:54
Оценка:
Здравствуйте, a9000, Вы писали:

A>Но это мне не нужно! Нужно чтобы исключение вываливалось лишь при попытке арифметических операций с не-числами.


Нужно смотреть в корень проблемы.
Тут есть 3 версии.

1) Захотелось дёшево сделать сигнальные значения и механизмы их обработки. NaN и SEH кажутся приемлемыми, потому что всё, что надо — это в некоторых местах расставить обработчики SEH, а то и централизованно транслировать их в С++ные исключения. Как насчёт юникса, где SEH нет, а signal — это обработчики последнего шанса?

2) Захотелось бить пользователю по пальцам. Чтобы программа не продолжала обрабатывать введённый мусор, а как можно скорее явно отстреливалась.

3) NaN случайно возникают в ходе вычислений, хотелось бы на ранней стадии ловить их и, возможно, исправлять. А расставлять проверки на каждый чих накладно.



Сигнальные значения — это сочетание логического признака и собственно данных.
То есть, хочется делать ветвление в программе.
Таких ветвлений, на самом деле, не должно быть много. Во всяком случае, не на каждую арифметическую операцию.
Даже если у нас есть вектор входных данных, некоторые из которых нам точно не понадобятся и поэтому помечены как отсутствующие. Мы же можем отследить путь этого вектора и его компонент по программе и точки первых проверок каждого компонента. Значит, до этой точки должны быть данные типа optional<float>, а после этой точки — float, которым мы доверяем.
А все остальные числа останутся обычными float.

Кровавый рефакторинг с заменой входных данных на тип, несовместимый с float, — очень поможет.
С его помощью можно как раз найти эти контрольно-пропускные точки.
Либо выяснить, что дизайн программы слишком насыщен макаронами.
Либо выяснить, что принцип GIGO является частью замысла, и принципиально требуется не раннее, а позднее распознавание мусора — провёрнутого через числодробилку. Где там чистый дёготь, где мёд с ложкой дёгтя, а где чистый мёд. Но тут уж медицина бессильна, и нужны не отстреливающие, а тихие NaN-ы.


boost::optional — штука хорошая, но с оверхедом.
Можно заменить её на свой велосипед
class optional_float
{
private:
  float value;

private:
  static void panic() { throw some logic error; }

  // в качестве сигнального значения примем старый добрый NaN, хотя можем и какое-нибудь -123456789.987654321 взять...
  static float signal_value() { std::numeric_limits<float>::quiet_NaN(); }
  static bool is_signal_value(float v) { return isnan(); }

public:
  optional_float()        : value(signal_value()) {}
  optional_float(float v) : value(v) { assert(!is_signal_value(v)); } // нефиг явно передавать сигнальное значение
  bool  check() const { return !is_signal_value(v); }
  void  pass()  const { if(!check()) panic(); }
  float get()   const { pass(); return value; } // никаких неявных операторов! ветвиться - так уж принудительно
};
Перекуём баги на фичи!
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.