Почему я не могу сравнить реалы в Standard ML?

  1. Почему не 1.0 = 2.0 Работа? Разве это не настоящий тип равенства?

    Это дает ошибку:

    Error: operator and operand don't agree [equality type required]
      operator domain: ''Z * ''Z
      operand:         real * real
      in expression:
        1.0 = 2.0
    
  2. Почему реал в шаблонах не работает так?

    fun fact 0.0 = 1.0
      | fact x = x * fact (x - 1.0)
    

    Это дает ошибку:

    Error: syntax error: inserting  EQUALOP
    

1 ответ

Решение

Почему не 1.0 = 2.0 Работа? Разве это не настоящий тип равенства?

№ Переменная типа ''Z указывает на то, что операнды = должны иметь типы равенства.

Почему не работает реал в шаблонах [...]?

Сопоставление с образцом неявно опирается на проверку на равенство. Загадочное сообщение об ошибке syntax error: inserting EQUALOP указывает на то, что синтаксический анализатор SML/NJ не допускает литералы с плавающей запятой там, где ожидается шаблон, и поэтому программист не может получить более значимую ошибку типа.

Разрабатывать,

С http://www.smlnj.org/doc/FAQ/faq.txt:

Q: Реален ли тип равенства?

A: Это было в SML '90 и SML/NJ 0.93, но не в SML '97 и SML/NJ 110. Так 1.0 = 1.0 вызовет ошибку типа, потому что "=" требует аргументы, которые имеют тип равенства. Кроме того, настоящие литералы нельзя использовать в шаблонах.

От http://mlton.org/PolymorphicEquality:

Один тип грунта, который нельзя сравнивать, реален. Так, 13.0 = 14.0 неверный тип. Можно использовать Real.== сравнивать реальное на равенство, но помните, что это обладает другими алгебраическими свойствами, чем полиморфное равенство.

Например, Real.== (0.1 + 0.2, 0.3) является false,

С http://sml-family.org/Basis/real.html:

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

Эти ограничения мешают программисту SML. Первое подразумевает, что 0 = ~0 истинно, а r/0 = r/~0 ложно. Последнее подразумевает такие аномалии, поскольку r = r ложно или что для контрольной ячейки rr мы могли бы иметь rr = rr, но не иметь! Rr =! Rr. Мы приняли беззнаковое сравнение нулей, но считали, что рефлексивное свойство равенства, структурного равенства и эквивалентности <>, а не o = должно быть сохранено.

Короткая версия: не сравнивайте реалы, используя равенство. Выполните эпсилон-тест. Я бы рекомендовал прочитать статью на http://floating-point-gui.de/errors/comparison. В итоге:

  • Не проверяйте, совпадают ли реалы, но если разница очень мала.

  • Граница ошибки, с которой сравнивается разница, часто называется эпсилон.

  • Не сравнивайте разницу с фиксированным эпсилоном:

    fun nearlyEqual (a, b, eps) = Real.abs (a-b) < eps
    
  • Не просто сравните относительную разницу с эпсилоном:

    fun nearlyEqual (a, b, eps) = abs ((a-b)/b) < eps
    
  • Обратите внимание на крайние случаи:

    • когда b = 0.0 это поднимает Div, (Switching a а также b обеспечивает симметричный крайний случай.)

    • когда a а также b находятся на противоположных сторонах нуля, он возвращает false даже когда они являются наименьшими возможными ненулевыми числами.

    • Результат не коммутативный. Есть случаи, когда nearlyEqual (a, b, eps) не дает такой же результат, как nearlyEqual (b, a, eps),

Руководство предоставляет общее решение; в переводе на стандарт ML это выглядит так:

fun nearlyEqual (a, b, eps) =
    let val absA = Real.abs a
        val absB = Real.abs b
        val diff = Real.abs (a - b)
    in Real.== (a, b) orelse
     ( if Real.== (a, 0.0) orelse
          Real.== (b, 0.0) orelse
          diff < Real.minNormalPos
       then diff < eps * Real.minNormalPos
       else diff / Real.min (absA + absB, Real.maxFinite) < eps )
    end

И он продолжает предупреждать о некоторых крайних случаях:

В некоторых случаях приведенный выше метод все еще дает неожиданные результаты (в частности, гораздо строже, когда одно значение почти равно нулю, чем когда оно точно равно нулю), и некоторые из тестов, которые были разработаны для прохождения, вероятно, указывают на поведение, которое не подходит для некоторых приложений. Перед использованием убедитесь, что он подходит для вашего приложения!

Другие вопросы по тегам