Хорошо, @Henning Makholm уже сказал это в своем комментарии, но он не объяснил, почему это на самом деле лучшее решение.
Первое, что нужно сказать: имея дело с плавающей точкой, мы должны всегда знать о возможных ошибках округления.Когда мы пишем [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 ошибка меньшечем один процент.Конечно, не точно, но это присуще методу интегрирования.
Предположим теперь, что диапазоны с плавающей точкой были определены так, чтобы всегда заканчиваться при пересечении правой границы.Тогда было бы возможно (но мы не можем быть уверены в этом!), Что в сумме вычисляется только 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
помогает запомнить эти проблемы!