Previous Entry Share Next Entry
2016-01

Шутка для узких кругов

Что должна возвращать функция min(NaN, -Infinity), и почему?

UPD: уиии, я знаю уже 8 вариантов (не считая "implementation-defined" и "падаем с исключением")

1. NaN
"Если в вычислениях родился NaN, результат любых вычислений типа-неверен, заражаем результаты ошибкой дальше"

2. Оставшийся операнд
"Если NaN означает отсутствие результата, то min(пусто, что_то) = что_то"
(С99, IEEE754-2008)

3. Операнд №2
При свёртке (fold) трактуем NaN во втором аргументе как "неверно", а в первом как "пусто"
(x86 minss/minps/minsd/minpd)

4. Операнд №1 (то же самое как и вариант 3, но наоборот)
(нигде не встречается)

5. Если любой из операндов NaN, возвращать default NaN
"заражаем ошибкой, но константой, ибо так проще"
(ARM NEON - VMIN.F32)

6. Если один из операндов QNaN, возвращать операнд №2. Если один из операндов SNaN, возвращать его же, сконвертированного в QNaN
"QNaN - пусто, SNaN - неверно"
(IBM POWER, VSX)
Ремарк: При этом VMX/AltiVec работают по варианту 1 (возвращать SNaN), хотя intrinsics у них такие же. Т.е. это как если бы Интел решил в AVX переделать min из SSE

7. ни NaN, ни Inf не поддерживаются, результат - undefined (по ISA). На практике возвращается -FLT_MAX (внезапно!), но вообще может быть хоть битовый мусор
"для 3д графики всё равно надо в итоге что-то нарисовать, не кормите туда NaNы"
(AMD 3dnow!, также какие-то старые GPU)

8. min(NaN, -Infinity) = NaN, max(NaN, -Infinity) = -Infinity
"Для стабильной сортировки floating-point колонок в БД"

Вот, вроде всё. Источники: Marat Dukhan, vit_r, IEEE 754 :)

This entry was originally posted at http://wizzard.dreamwidth.org/409296.html. It has comment count unavailable comments. Please comment there using OpenID.

vit_r December 13th, 2014
NaN должна возвращать любая операция, вывалившаяся из множества чисел.

wizzard0 December 13th, 2014
Я вот думаю, скрыть комменты или поглядеть на дискуссию? :)

sassa_nf December 13th, 2014
NaN, чтоб неповадно впредь было.

а который из NaN должна возвращать min(NaN,NaN)?

_winnie December 13th, 2014
По хорошему, NaN должен всё заражать. Как ради отладки, так и ради оптимизации (удаления проверок аргументов на каждом шаге).

На практике, типичная реализация (a < b) ? a : b выдаст второй результат ( -Inifinity ).

У меня сейчас g++ печатает наоборот, видимо поменял местами a и b. Добавил флаги оптимизации и sse - стал печатать оба раза NaN. Тонко!


► g++ -O3   x.cpp && ./a.exe
        nan
        -inf

 
► g++ -O3  --fast-math x.cpp && ./a.exe
        nan
        nan

 
► g++ -O0  --fast-math x.cpp && ./a.exe
        nan
        -inf

 
► cat x.cpp
        #include <iostream>
        #include <algorithm>

        double inf = 1.0;
        double nan = 0.0;

        int main() {
            inf /= 0.0;
            nan /= 0.0;

            std::cout << std::min(nan, -inf) << std::endl; // -inf
            std::cout << std::min(-inf, nan) << std::endl; // -nan
        }



amarao_san December 13th, 2014
Баг. Надо бы репортить.

amarao_san December 13th, 2014
NaN. Хотя если бы я был разработчиком, то у меня бы операции с NaN возвращали произвольный эмоджи из юникода.

_winnie December 13th, 2014

NaN используется:

1) для того, чтобы не писать if-ы. Чтобы в коде вида проверить только результат, а не каждый шаг. Так же, как errno.

Например, чтобы писать простой код без if-ов типа такого:

for (int i = 0; i < 10; ++i) {
  x = ln(tg(sqrt(sin(1/x))));
}
if (isNaN(x)) { 
   printf("функция не определена");
} else {
   printf("результат: %g", x);
}


2) для отладки. Чтобы увидеть NaN в выводе программы и понять, что что-то в середине пошло не так.

3) для отладки. Чтобы операции с NaN - останавливали программу или кидали исключение. На всякий случай, http://stackoverflow.com/questions/18118408/what-is-difference-between-quiet-nan-and-signaling-nan


jakobz December 13th, 2014
Функция min должна зарабатывать деньги на долбоёбах.

jakobz December 13th, 2014
Ничего при этом, понятно, не возвращая.

permea_kra December 13th, 2014
Вариант "падать с исключением" был?

wizzard0 December 13th, 2014
Это не считается :)

Добавил список в пост)

spamsink December 13th, 2014
Интересно, что поведение инструкции minsd не соответствует ее описанию:
http://x86.renejeschke.de/html/file_module_x86_id_173.html

wizzard0 December 13th, 2014
Хз, Marat Dukhan говорит, что соответствует, и в тредах обсуждения LuaJIT тоже говорят, что соответствует. Возможно, флажки какие есть?

sab123 December 13th, 2014
Только правильного варианта нету. Правильный варинт: NaN - это неопределенность, то есть, какое-то неизвестное число. Заведомо известно, что какое бы число ни было, оно не может быть меньше -Inf и больше +Inf. Поэтому

min(-Inf, что_угодно_включая_NaN) = -Inf
max(+Inf, что_угодно_включая_NaN) = +Inf

В остальных случаях min и max должны возвращать NaN если один из операндов NaN, поскольку результат сравнения - опять неопределенность.

wizzard0 December 13th, 2014
Неопределенность "неизвестное число" и неопределенность "известно что всё выражение стоит выкинуть на свалку" - разные вещи.

maxim December 13th, 2014
> decimal:compare(decimal:to_decimal("NaN"),decimal:to_decimal("-Infinity")).
{d,0,0,'NaN'}

> decimal:lt(decimal:to_decimal("-Infinity"),decimal:to_decimal("NaN")).
           ** exception throw: "Comparison involving NaN"
     in function  decimal:gcmp/5 (src/decimal.erl, line 489)


Edited at 2014-12-13 02:08 am (UTC)

sebbenth December 13th, 2014
Если внимательно почитать все варианты ответа, то они распадутся на "варианты с жёсткой типификацией с NaN в качестве одного из вариантов значения параметра", "варианты с жёсткой типификацией в качестве одного из вариантов исключения" и "варианты без типификации".

Приведу пример: у нас есть функция corr(A,B), где A и B - массивы чисел (более сложный пример). Допустим, один из этих массивов содержит NaN, а второй - -Inf, к примеру:

corr([1, 2, NaN], [1, 2, 3])

У нас получаются следующие варианты:

1) NaN является значением в смысле "нет данных". В таком случае корреляция должна составлять 1.

2) NaN является значением в смысле "исключение: ошибка в вычислениях". В таком случае мы должны возвращать NaN как значение в смысле "произвести вычисление невозможно"

3) NaN является тем, как функция, которая ожидает видеть массив чисел, видит что-то совсем другое (по сути, мусором). При этом мы должны открыто бросать исключение - или, в случае чего-то совсем низкоуровнего, делать действие, которое приведёт к наиболее предсказуемым последствиям.

Вариант 1 явно используется языками типа Matlab, где это просто одно из возможных значений на вход, работа с которым определяется логикой исполнения функции типа min.

supermega December 13th, 2014
Функция должна работать в соответствии с логикой приложения. Как я понимаю, приложение сравнивает числа.
Если в функцию, сравнивающую числа, попал NaN - это исключительная ситуация, т.е. exception должен выбрасываться. По крайней мере если какай-то другой программист при отладке получит такой эксепшн, он не удивится этому.
И уж точно не должно зависить от порядка аргументов, это бред вообще.
Кстати, в javascript возвращается NaN:

> Math.min(NaN, -Infinity)
< NaN

deni_ok December 13th, 2014
В развитие темы: должна ли min(NaN, -Infinity) возвращать то же самое, что min(-Infinity, NaN)?
Prelude> (-5/0)
-Infinity
Prelude> sqrt (-5)
NaN
Prelude> min (-5/0) (sqrt (-5))
NaN
Prelude> min (sqrt (-5)) (-5/0)
-Infinity
Prelude> max (-5/0) (sqrt (-5))
-Infinity
Prelude> max (sqrt (-5)) (-5/0)
NaN

nponeccop December 13th, 2014
Вы ещё уточните, должны ли min NaN bang и max NaN bang работать одинаково :)

where bang = bang

Edited at 2014-12-13 06:09 am (UTC)

nponeccop December 13th, 2014
Мой ответ: NaN нужна как дешёвое с точки зрения аппаратных затрат средство реализации нетотальных функций типа sqrt.

Во всех остальных случаях NaN не нужен и используется только по причине глупости людей или компиляторов.

min же нет в стандарте? Соответственно, требовать от min a b какого-то поведения не нужно, это заведомо application code (а не реализация флоата) и соответственно как кому надо - так тот и реализовывает. Стандарт нам всего лишь дает поведение стандартных меньше-больше. Если кому-то надо нестандартные - делайте отдельно.

NaN - ещё довольно удачный выбор для стандарта, потому что флаги, например, добавляют лишний state.

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

По-хорошему в приложении over 99% случаев надо явный кастомный 1 + Float использовать

data F1 = F1 Float | MissingData
data F2 = F1 Float | Error

и т.д. с явно указанной семантикой, например в виде функций liftF2, liftF1 и т.п.

А не используют по тому же, почему широко используют magic values и enum вместо sum types - из-за уебищных языков и уёбищных реализаций неуебищных языков.

Ещё кстати возможны несимметричные варианты min:

min :: F1 -> F2 -> F2

использовать их для свёртки конечно дебилизм (если это не ISA)

Настоящая CBV свертка должна сама обрабатывать пустоту, но делегировать логику сравнения наружу:

minL min [] = Nothing
minL min (h:t) = Just $ foldl min h t

Т.е. возвращать две разных ощибки, а не лепить пустой список и пустой результат min в одну кучу

Вот ещё вопрос: какой должен быть instance Monoid (Maybe a)? Ответ: их более одного, и все нужны включая константный _ `mappend` _ = Nothing )))

Edited at 2014-12-13 06:36 am (UTC)

sorhed December 13th, 2014
NaN — это хак. Соответственно, поведение хаков зависит от имплементации. Тут нет никакого «как должно быть».

То же можно сказать про округление FP и другие хаки.

wizzard0 December 13th, 2014

До exact real number calculations нам настолько далеко, что тогда уже всё впору считать хаком %)


Да и до моделирования предметки вообще (N+Float лучше NaN, но совсем чуть-чуть)


enternet December 13th, 2014
> 8. min(NaN, -Infinity) = NaN, max(NaN, -Infinity) = -Infinity
> "Для стабильной сортировки floating-point колонок в БД"

Да ну? Это что за СУБД такая? Что позволяет сразу все 4 варианта работы с NaN:
1) Работу с NaN в вычислениях
2) Проверку на NaN
3) Хранение NaN
4) Передачу NaN клиенту

Так-во с ходу поставил бы только на DB2, но нет под рукой проверить.


?

Log in

No account? Create an account