Требуется обновление браузера.

Равна ли переменная самой себе?


Просмотров: 1954
30 марта 2017 года
Цитата
Если друг
оказался вдруг
И не друг, и не враг,
а - так,
Если сразу не разберёшь,
Плох он или хорош

Теория


Возможно, в процессе чтения чужого исходного кода, Вы натыкались на конструкции проверки равенства переменной самой себе. Не спешите ужасаться абсурдности проверки и попытке создать видимость большого объёма кода.

Стандарт IEEE 754
Цитата
широко используемый стандарт IEEE, описывающий формат представления чисел с плавающей точкой. Используется в программных (компиляторы с разных языков программирования) и аппаратных (CPU и FPU) реализациях арифметических действий (математических операций).

среди прочего, описывает особое состояние числа с плавающей запятой - NaN или "нечисло" (Not-a-Number).

Цитата
Данное состояние может возникнуть в различных случаях, например, когда предыдущая математическая операция завершилась с неопределённым результатом, или если в ячейку памяти попало не удовлетворяющее условиям число.

Примечательное свойство 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
), VS согласилась на код, но услужливо намекнула на мои интеллектуальные способности:

Цитата
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 в институте, но не думаю, что такой концептуальный момент с тех пор изменился.

Ну тут прозаично: делишь на ноль - ошибка деления на ноль. Если попытаться вовлечь такую "испорченную" переменную в вычисления значения другой переменной, значение последней будет не определено. Попытка использовать символ бесконечности даёт такой же результат.

Аналогичные результаты при оформлении вычислений в виде подпрограммы.

Учитывая уровень абстракции среды, вряд ли у Вас получится реализовать сравнение непосредственно: у системы есть иные методы контроля адекватности значений переменных.

Запись опубликована в категориях:

Алгоритмы и аспекты  
 

Комментарии

Инкогнито
  Загружаем captcha
Александр
20 Александр  10 апреля 2017 03:41
>> 19 Да, пожалуй, использовать подобные конструкции не комильфо. Во-первых, они выглядят алогично, застряв на стыке между абстракцией и её аппаратной реализацией. Во-вторых, переносимость такого решения зависит от полноты поддержки стандарта (о портировании подобного кода или использовании его фрагментов, в том числе, с новыми типами данных - страшно подумать). В-третьих, думается, грамотно спроектированный алгоритм должен обнаруживать угрозу получения NaN.
Тем не менее, о подобных фокусах неплохо знать, как о шалостях типа 42[x].
Дмитрий Маслов
19 Дмитрий Маслов  31 марта 2017 18:18
Матчасть раскрыта, но как это применять на практике?
Чтобы проверить на NaN, в языках есть специальные функции. Полагаю, это лучше, чем использовать такие неочевидные методы, способные запутать читающего.