gpt4 book ai didi

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

转载 作者:行者123 更新时间:2023-12-01 10:19:33 24 4
gpt4 key购买 nike

作为作业,我实现了以下 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

为什么会出现这种差异?

编辑

问题Why does changing the sum order returns a different result?为我指明了正确的方向,但我认为这个问题的实际答案(OTP 中的实现细节)也可能对某些人很有趣。

最佳答案

这个问题的答案Why does changing the sum order returns a different result?启发我去源代码看看实现是如何的,当然,当参数是 list 时它使用 Erlang's implementation of foldl ,它按 head + accumulator 的顺序应用函数,而不是像我的实现中那样 accumulator + head :

https://github.com/elixir-lang/elixir/blob/master/lib/elixir/lib/enum.ex

@spec sum(t) :: number
def sum(enumerable) do
reduce(enumerable, 0, &+/2)
end

@spec reduce(t, any, (element, any -> any)) :: any
def reduce(enumerable, acc, fun) when is_list(enumerable) do
:lists.foldl(fun, acc, enumerable)
end

https://github.com/erlang/otp/blob/master/lib/stdlib/src/lists.erl

-spec foldl(Fun, Acc0, List) -> Acc1 when
Fun :: fun((Elem :: T, AccIn) -> AccOut),
Acc0 :: term(),
Acc1 :: term(),
AccIn :: term(),
AccOut :: term(),
List :: [T],
T :: term().

foldl(F, Accu, [Hd|Tail]) ->
foldl(F, F(Hd, Accu), Tail); %% here!
foldl(F, Accu, []) when is_function(F, 2) -> Accu.

编辑

@Sneftel 的评论让我做了以下实验:

@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

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

关于floating-point - Enum.Sum 和我的自定义求和函数之间的 Elixir 意外浮点结果差异,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55067009/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com