Почему мы получаем странные результаты, когда используем float в диапазоне в Haskell? [Дубликат]

Используйте абсолютное позиционирование и z-index для создания липкого нижнего колонтитула с любым разрешением, используя следующие шаги:

  • Создайте нижний колонтитул с position: absolute; bottom: 0; и желаемая высота
  • Установите дополнение нижнего колонтитула, чтобы добавить пробел между нижней частью содержимого и нижней частью окна
  • Создать контейнер div , который обертывает содержимое тела с помощью позиции : relative; min-height: 100%;
  • Добавить нижнее дополнение к основному содержимому div , равное высоте и дополнению нижнего колонтитула
  • Установите нижний колонтитул z [] d4] больше, чем контейнер div , если нижний колонтитул обрезается

Вот пример: [ ! d15]

& lt;! doctype html & gt; & Lt; & HTML GT; & Lt; & головка GT; & lt; title & gt; Липкий нижний колонтитул & lt; / title & gt; & lt; meta charset = "utf-8" & gt; & Lt; & стиль GT; .wrapper {position: relative; мин-высота: 100%; } .footer {position: absolute; внизу: 0; ширина: 100%; высота: 200 пикселей; padding-top: 100px; background-color: серый; } .column {height: 2000px; padding-bottom: 300px; фон-цвет: зеленый; } / * Установите `html`,` body` и container `div` в` height: 100% `для IE6 * / & lt; / style & gt; & Lt; / головка & GT; & Л; тело & GT; & lt; div class = "wrapper" & gt; & lt; div class = "column" & gt; & Lt; & диапазона GT; Привет & л; / & продолжительность GT; & Lt; / дел & GT; & lt; div class = "footer" & gt; & lt; p & gt; Это испытание. Это всего лишь тест ... & lt; / p & gt; & Lt; / дел & GT; & Lt; / дел & GT; & Lt; / тело & GT; & lt; / html & gt;

39
задан undur_gongor 25 November 2014 в 10:04
поделиться

2 ответа

Синтаксис [e1, e2 .. e3] - действительно синтаксический сахар для enumFromThenTo e1 e2 e3, который является функцией в Enum typeclass.

Стандарт Haskell определяет его семантику следующим образом :

Для типов Int и Integer функции перечисления имеют следующее значение:

  • Последовательность enumFrom e1 - это список [e1,e1 + 1,e1 + 2,…] ].
  • Последовательность enumFromThen e1 e2 - это список [e1,e1 + i,e1 + 2i,…], где приращение i равно e2 − e1. Инкремент может быть нулем или отрицательным. Если приращение равно нулю, все элементы списка одинаковы.
  • Последовательность enumFromTo e1 e3 - это список [e1,e1 + 1,e1 + 2,…e3]. Список пуст, если e1 > e3.
  • Последовательность enumFromThenTo e1 e2 e3 - это список [e1,e1 + i,e1 + 2i,…e3], где приращение i равно e2 − e1. Если приращение положительное или ноль, список заканчивается, когда следующий элемент будет больше, чем e3; список пуст, если e1 > e3. Если приращение отрицательное, список заканчивается, когда следующий элемент будет меньше e3; список пуст, если e1 < e3.

Это в значительной степени то, что вы ожидаете, но экземпляры Float и Double определяются по-разному:

Для Float и Double семантика семейства enumFrom задана правилами для Int выше, за исключением того, что список заканчивается, когда элементы становятся больше e3 + i∕2 для положительного приращения i или когда они становятся меньше e3 + i∕2 для отрицательных i.

Я не совсем уверен, что оправдание для этого, поэтому единственный ответ Я могу дать вам то, что это так, потому что он определен таким образом в стандарте.

Вы можете обойти это, перечислив использование целых чисел и преобразовывая его в Float.

Prelude> map fromIntegral [1, 3 .. 10] :: [Float]
[1.0,3.0,5.0,7.0,9.0]
33
ответ дан hammar 16 August 2018 в 00:32
поделиться
  • 1
    Вероятно, намерение состоит в том, чтобы сделать такие вещи, как [0.0, 0.1 .. 1.0], работать более или менее, как то, что наивно ожидало бы, не зная о неточности с плавающей запятой. – Henning Makholm 3 September 2011 в 02:21
  • 2
    @Henning: Я думаю, это имеет смысл, если предположить, что в обычном случае e3 будет в последовательности [e1, e1+i ..], игнорируя неточности. В примере OP e3 находился на полпути между двумя значениями, что в некотором смысле является наихудшим сценарием для этого предположения. – hammar 3 September 2011 в 02:39
  • 3
    Я бы далее утверждал, что любой, кто не знает о проблемах с нечеткой точкой с плавающей запятой, должен не использовать значения с плавающей запятой . В общем, используйте рациональные методы произвольной точности, в которых производительность не является серьезной проблемой, и научитесь правильно использовать поплавки там, где это необходимо. – C. A. McCann 3 September 2011 в 03:57
  • 4
    Это неприятная ошибка в спецификации Haskell. Он пытается скрыть присущие проблемы с плавающей точкой, но это просто означает, что проблема возникает в другом месте. – augustss 3 September 2011 в 08:29
  • 5
    @leftaroundabout Я знаю, как работает точка с плавающей запятой, и попытки обойти проблемы просто вызывают у меня больше проблем. Я ожидаю, что enumFromThenTo будет продолжать добавлять приращение и принимать элементы, пока они равны & lt; = верхняя граница. Получение элемента над верхней границей очень сбивает с толку. – augustss 5 September 2011 в 11:21

Хорошо, Хеннинг Махольм уже сказал это в своем комментарии, но он не объяснил, почему это на самом деле является лучшим решением.

Прежде всего, чтобы сказать: имея дело с плавающей точкой, мы должны всегда знать о возможных ошибках округления. Когда мы пишем [0.0, 0.1 .. 1.0], мы должны знать, что все эти числа, кроме первого, не будут в точном месте десятых. Где нам нужна эта определенность, мы вообще не должны использовать поплавки.

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

trIntegrate f l r s = sum [ f x | x<-[l,(l+s)..r] ] * s - (f(l)+f(r))*s/2

давайте проверим это: trIntegrate ( \x -> exp(x + cos(sqrt(x) - x*x)) ) 1.0 3.0 0.1 => 25.797334337026466 по сравнению с 25.9144 ошибка меньше, чем один процент. Конечно, не точно, но это присуще методу интеграции.

Предположим теперь, что диапазоны float были определены так, что они всегда заканчиваются при пересечении правой границы. Тогда было бы возможно (но мы не можем быть уверены в этом!), Что в сумме вычисляются только 20 значений, а не 21, потому что последнее значение x оказывается равным 3.000000. Мы можем имитировать этот

bad_trIntegrate f l r s = sum [ f x | x<-[l,(l+s)..(r-s)] ] * s - (f(l)+f(r))*s/2

, после чего получим

bad_trIntegrate ( \x -> exp(x + cos(sqrt(x) - x*x)) ) 1.0 3.0 0.1

=> 21.27550564546988 urgh!

Это не имеет ничего общего со скрытием проблем с плавающим точка. Это всего лишь способ помочь программисту легче обойти эти проблемы. Фактически, противоположный результат [1, 3 .. 10] :: Float помогает запомнить эти проблемы!

13
ответ дан leftaroundabout 16 August 2018 в 00:32
поделиться
  • 1
    Но почему такое же поведение было задано для Rationals? Здесь нет никаких неточностей. – Josephine 14 February 2012 в 14:03
  • 2
    @ Андре, это действительно странно. Это выбор между несовместимостью поведения с целыми числами или с поплавками. ИМО, рациональность скорее близка к поплавкам, поэтому несколько разумно помещать их в это поле, а не в Integral. - В интервале дробных чисел (в отличие от целочисленного) всегда можно выбрать размер шага, размер интервала которого является интегральным кратным. В этом случае диапазоны Rational s и диапазоны Float s ведут себя так, как можно было бы интуитивно ожидать, поэтому мы должны просто убедиться, что это условие всегда выполняется. – leftaroundabout 14 February 2012 в 16:03
  • 3
    Подумав об этом, я согласен с размером интервала. Кто-то из IRC даже считал это ошибкой, если это условие не было выполнено. Сначала я подумал, что это смешно, но теперь я вижу его точку зрения. – Josephine 15 February 2012 в 16:38
  • 4
    Я согласен, что во многих случаях упрощает жизнь. Однако он вводит новые проблемы: sum [dx * sqrt (1 - x) | x <- [0.0, dx .. 1]] возвращает NaN для dx = 0.001, потому что «перерегулирование» оставляет область интегрированной функции. Я не уверен, что это того стоит. – undur_gongor 26 April 2018 в 19:26
  • 5
    @undur_gongor такая сумма обычно также дает неправильный результат, когда последний член опущен; только в особых случаях, таких как sqrt (1 - 1), это не имеет значения. Опять я утверждаю, что явно неверный NaN менее плох, чем разумно звучащий, но неточно-точный номер с плавающей запятой. - Чтобы быть действительно уверенным в таких вещах, использование плавающих элементов, которые могут касаться границ домена, является принципиально неправильным подходом; следует вычислять средние точки ячейки вместо этого или использовать только синтаксис диапазона для межклеточных границ, но отдельно устанавливать внешние границы. – leftaroundabout 26 April 2018 в 19:32
Другие вопросы по тегам:

Похожие вопросы: