当前位置: 首页 > 知识库问答 >
问题:

Elixir eNum.Sum与我的自定义求和函数之间的意外浮点结果差异

夏侯承恩
2023-03-14

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

为什么改变求和顺序会返回不同的结果?给我指明了正确的方向,但我认为这个问题的实际答案(OTP中的实现细节)可能也会对某人感兴趣。

共有1个答案

刘弘新
2023-03-14

这个问题的答案是:为什么改变求和顺序会返回不同的结果?启发我去源代码看看实现是如何的,当然,当参数是列表时,它使用Erlang的foldl的实现,该实现在顺序head+累加器中应用函数,而不是在我的实现中应用累加器+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
iex(1)> Homework.sum([-2, -2.1524700989447303, 1])
-3.1524700989447307
iex(2)> Enum.sum([-2, -2.1524700989447303, 1])
-3.1524700989447307
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
 类似资料:
  • 问题内容: 和数据类型之间的区别是什么,应在哪种情况下使用? 对于任何类型的金融交易(例如,薪水领域),首选哪种交易,为什么? 问题答案: 仅当十进制(最多38位)提供的精度不足时才使用浮点或实数数据类型 近似数字数据类型不能存储为许多数字指定的确切值;他们存储的值非常接近。(Technet) 避免在WHERE子句搜索条件中使用浮点或实数列,尤其是=和<>运算符(Technet) 因此通常来说,因

  • 我正在使用neo4j来计算一个数据集上的一些统计数据。为此,我经常在浮点值上使用sum。我得到不同的结果取决于环境。例如,执行以下操作的查询: 差别很小(与)。但使简单的等式检查失败就足够了。另一个例子是数据库的不同实例,使用相同的加载过程加载相同的数据可能会产生相同的问题(dbs可能不是1:1,某些关系的加载顺序可能不同)。我取了neo4j求和的原始值(通过简单地移除),并验证它们在所有情况下都

  • 本文向大家介绍函数与程序之间的差异,包括了函数与程序之间的差异的使用技巧和注意事项,需要的朋友参考一下 函数 在计算机编程语言环境中,功能是一组指令,这些指令需要一些输入并执行某些任务。在SQL中,函数返回一个值。 程序 过程也是一组指令,它们接受输入并执行某些任务。在SQL中,过程不返回值。在Java中,过程和函数是相同的,也称为子例程。 以下是SQL函数和SQL过程之间的重要区别。 序号 键

  • 问题内容: 做一个简单的测试时,我就在用Java编写正则表达式 但是在JavaScript中 这里发生了什么?我可以使我的Java regex模式“ q”的行为与JavaScript相同吗? 问题答案: 在JavaScript中,返回与使用的正则表达式匹配的子字符串。在Java中,检查整个字符串是否与正则表达式匹配。 如果要查找与正则表达式匹配的子字符串,请使用Pattern和Matcher类,例

  • 我们使用意图在两个活动之间切换,片段也是出于相同的目的。那么为什么我们不能总是使用意图而不是片段呢?

  • initial_value_generator将计算一个值,稍后由random_value_generator函数使用该值。该值由一个FILETIME对象和三个常量生成。正在发生缓冲区溢出,但未处理。random_value_generator每次使用时都会修改DWORD64 prng_initial_value。 我成功地构建了initial_value_generator函数。 我想我不能完成