Равна ли переменная самой себе?
Просмотров: 1648
30 марта 2017 года
Если друг
оказался вдруг
И не друг, и не враг,
а - так,
Если сразу не разберёшь,
Плох он или хорош
оказался вдруг
И не друг, и не враг,
а - так,
Если сразу не разберёшь,
Плох он или хорош
- В. Высоцкий «Песня о друге».
Теория
Возможно, в процессе чтения чужого исходного кода, Вы натыкались на конструкции проверки равенства переменной самой себе. Не спешите ужасаться абсурдности проверки и попытке создать видимость большого объёма кода.
Стандарт IEEE 754
широко используемый стандарт IEEE, описывающий формат представления чисел с плавающей точкой. Используется в программных (компиляторы с разных языков программирования) и аппаратных (CPU и FPU) реализациях арифметических действий (математических операций).
среди прочего, описывает особое состояние числа с плавающей запятой - NaN или "нечисло" (Not-a-Number).
Данное состояние может возникнуть в различных случаях, например, когда предыдущая математическая операция завершилась с неопределённым результатом, или если в ячейку памяти попало не удовлетворяющее условиям число.
- NaN
Примечательное свойство NaN - он не равен никому и даже самому себе. В плане семантики, это ещё более неопределённая сущность нежели бесконечность, с которой можно аккуратно работать, используя состояния ±INF.
Так что, охладите свой пыл, юмористы: разделил на ноль и получил абстракцию? - пффф: я вычел одно деление на ноль из другого и получил кое-что покруче!
Детали реализации
Обратите внимание: в большинстве случаев, надо явно указать, что вы делите на ноль, представленный дробным числом. Деление на x и на x.0 (или другое приведение типа переменной к вещественному) - две разные ситуации. На это часто попадаются новички: тип результата деления определяется делителем, поэтому, если вы разделите 5.0 на 2, то получите 2.0.
Математические пакеты, наоборот, могут доконать Вас привычкой оперировать с переменными вещественного типа, из-за чего постоянно будут требовать явно указывать округления для аргументов, подразумевающих целые числа. Причём гигантомания в вопросах точности в совокупности с неудачным типом округления, рано или поздно закончится тем, что где-нибудь после кучи нулей 0.000000000000.. Вы не заметите неприметный шум в виде ..0000001, который спровоцирует округление не в ту сторону и всё рухнет... но это - уже другая история.
C++
Начнём со старых добрых плюсов [?]
ударение на последний слог
:Тест на равенство NaN самому себе на СИ++
1234567891011121314151617 | #include <iostream> using namespace std; int main() { float a=1/0.0; float b=a-a; cout<<"a="<<a<<endl; cout<<"b="<<b<<endl; if(b==b) cout<<"b is eq b"<<endl; else cout<<"b is not eq b"<<endl; return 0; } |
Я не стал смотреть предупреждения от статического анализатора (который, наверняка, заметил деление на ноль), а сразу выполнил код. Результат:
a=inf
b=nan
b is not eq b
Ожидаемо: переменная не равна самой себе.
Blitz Basic
Интерпретатор отказался выполнять код, синонимичный приведённому выше, так как:
Division by zero
Пришлось немного поплутать, спрятав ноль в отдельную переменную:
Тест на равенство NaN самому себе в среде Blitz3D
123456789101112131415 | Local z#=0 Local a#=1/z Local b#=a-a Print "a="+Str(a) Print "b="+Str(b) If b=b Print "b is eq b" Else Print "b is not eq b" EndIf WaitKey() End |
Блитц, парень простой: слева стоит NaN, справа стоит NaN, вывод - равенство.
a=Infinity
b=NaN
b is eq b
Возрадуйтесь, ненавистники: у Блитца своё видение стандарта.
C#
Не буду утомлять Вас повторениями, приведу самую суть:
Тест на равенство NaN самому себе на СИ Шарп
123 | float a = 1 / 0.0f; float b = a - a; if (b == b) |
После явного указания типа знаменателя (
float
double
A comparison made to same variable. Did you mean to compare something else?
Ну и результат:
b is not eq b
соответствует ожиданиям.
MATLAB
Поленимся и, не создавая отдельных файлов, выполним код в консоли.
Тест на равенство NaN самому себе в консоли MATLAB
12345 | >> a=1/0.0; Warning: Divide by zero. >> b=a-a; >> if(b==b)disp('b is eq b');else disp('b is not eq b');end; b is not eq b |
В соответствии со стандартом.
Mathcad
Последний раз работал в Mathcad в институте, но не думаю, что такой концептуальный момент с тех пор изменился.
Ну тут прозаично: делишь на ноль - ошибка деления на ноль. Если попытаться вовлечь такую "испорченную" переменную в вычисления значения другой переменной, значение последней будет не определено. Попытка использовать символ бесконечности даёт такой же результат.
Аналогичные результаты при оформлении вычислений в виде подпрограммы.
Учитывая уровень абстракции среды, вряд ли у Вас получится реализовать сравнение непосредственно: у системы есть иные методы контроля адекватности значений переменных.
Комментарии
Тем не менее, о подобных фокусах неплохо знать, как о шалостях типа 42[x].
Чтобы проверить на NaN, в языках есть специальные функции. Полагаю, это лучше, чем использовать такие неочевидные методы, способные запутать читающего.