Warning: file_get_contents(/data/phpspider/zhask/data//catemap/7/elixir/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Floating point Elixir Enum.Sum和我的自定义和函数之间意外的浮点结果差异_Floating Point_Elixir - Fatal编程技术网

Floating point Elixir Enum.Sum和我的自定义和函数之间意外的浮点结果差异

Floating point Elixir Enum.Sum和我的自定义和函数之间意外的浮点结果差异,floating-point,elixir,Floating Point,Elixir,作为家庭作业,我实现了以下sum函数来返回数字列表的总和: defmodule Homework do @spec sum(list(number())) :: number() def sum(l) do case l do [] -> 0 [a | as] -> a + sum(as) end end end 作为单元测试,我使用了以下比较: [-2, -2.1524700989447303, 1] |> fn(l) -

作为家庭作业,我实现了以下
sum
函数来返回数字列表的总和:

defmodule Homework do
  @spec sum(list(number())) :: number()
  def sum(l) do
    case l do
      [] -> 0
      [a | as] -> a + sum(as)
    end
  end
end
作为单元测试,我使用了以下比较:

[-2, -2.1524700989447303, 1] |> fn(l) -> Enum.sum(l) === Homework.sum(l) end.()
此测试失败,返回
false
。当我在
iex
中运行函数时,我得到了以下结果,这让我感到惊讶:

iex(1)> [-2, -2.1524700989447303, 1] |> Enum.sum
-3.1524700989447307
iex(2)> [-2, -2.1524700989447303, 1] |> Homework.sum
-3.1524700989447303
此外,这两个函数分别一致地生成
-3.1524700989447307
-3.1524700989447303

为什么会出现这种差异

编辑


这个问题为我指明了正确的方向,但我认为这个问题的实际答案(OTP中的一个实现细节)可能也会引起一些人的兴趣。

这个问题的答案启发我去源代码看看实现是如何的,当然,当参数是它使用的,它在命令head+累加器中应用函数,而不是在我的实现中应用累加器+head:

编辑

@斯奈夫特尔的评论让我做了以下实验:

@spec sum(list(number())) :: number()
def sum(l) do
  case Enum.reverse(l) do # reversing first
    [] -> 0
    [a | as] -> a + sum(as)
  end
end
此新版本的结果与
Enum.sum
相同:

iex(1)> Homework.sum([-2, -2.1524700989447303, 1])
-3.1524700989447307
iex(2)> Enum.sum([-2, -2.1524700989447303, 1])
-3.1524700989447307
看来这是关于订单的

编辑2

当列表顺序不相反时,将
a+sum(as)
更改为
sum(as)+a
对结果没有影响

def sum(l) do
  case l do
    [] -> 0
    [a | as] -> sum(as) + a
  end
end

iex(1)> Homework.sum([-2, -2.1524700989447303, 1])
-3.1524700989447303
iex(2)> Enum.sum([-2, -2.1524700989447303, 1])
-3.1524700989447307

因此,当我们谈论“顺序”的相关性时,它是发生的顺序,而不是操作数的顺序

我认为这里真正的问题是,在处理浮点数时,不应该期望完全相等,因为它们本质上是不精确的。如果需要精度,则可以使用诸如的模块

defmodule Homework do
  def sum([]), do: Decimal.new(0)
  def sum([a | as]), do: Decimal.add(a, sum(as))
end
测试会话:

iex(1)> test = [Decimal.new(-2), Decimal.new("-2.1524700989447303"), Decimal.new(1)]
iex(2)> Homework.sum(test) === :lists.foldl(&Decimal.add/2, Decimal.new(0), test)
true

提供了如何有效使用浮点数的一个很好的概述。

我认为它的顺序是:
[-2,-2.1524700989447303,1]|>Enum.reverse |>Enum.sum
生成
-3.1524700989447303
@denis.peplin???这是出乎意料的:与
acc+head
相比,差异的可能重复不是
head+acc
。浮点加法是可交换的,因此这两种方法将产生相同的结果。相反,区别在于他们使用的是累加器。如果您还没有学习“尾部递归”,那么值得花时间研究一下,以理解为什么Erlang的方法比您的方法要有效得多。@Sneftel我做了一个实验,它似乎是关于顺序的。。。我肯定会看看“尾部递归”,我正在读的这本书是关于这些事情的:)它是关于顺序的,但在左折叠与右折叠的意义上,不是在操作数顺序的意义上。尝试将
a+sum(as)
更改为
sum(as)+a
,您将看到它对结果没有影响。@Sneftel您是对的:)。我想我现在的心态是对的。
defmodule Homework do
  def sum([]), do: Decimal.new(0)
  def sum([a | as]), do: Decimal.add(a, sum(as))
end
iex(1)> test = [Decimal.new(-2), Decimal.new("-2.1524700989447303"), Decimal.new(1)]
iex(2)> Homework.sum(test) === :lists.foldl(&Decimal.add/2, Decimal.new(0), test)
true