如何在 Perl 中比较浮点数“相等”

问题描述 投票:0回答:2

请注意,我已将“相等”放在双引号中,因此请继续阅读详细信息:

我编写了一些代码,将 Perl 对象存储在文件中并从那里检索它们。 我正在使用自己的基于 JSON 的代码,该代码使用独立于 Perl 的文本表示形式。 当我写一个自检检查加载的对象是否与存储的对象相同时,我得到了一个惊喜:

  DB<14> x $self->time() == $other->time()
0  ''
  DB<15> x $self->time()
0  4842276.32536854
  DB<16> x  $other->time()
0  4842276.32536854
  DB<17> x $self->time() == $other->time()
0  ''
  DB<18> x $self->time() - $other->time()
0  '-2.79396772384644e-09'

因此,虽然输出的数字看起来相同,但它们的内部表示却不同。 然而,当我为两者创建 JSON 表示时,没有区别(这次数字不同(随着时间的推移);另请注意,字段的顺序有所不同,因为它是 Perl HASH):

  DB<1> x $self->flat
0  '{"time":4842854.29538268,"crc":23407,"use_counter":19}'
  DB<2> x $other->flat
0  '{"use_counter":19,"crc":23407,"time":4842854.29538268}'

(实物比较复杂,但这说明了原理;时间值来自

clock_gettime(CLOCK_MONOTONIC) # useTime::HiRes qw(clock_gettime CLOCK_MONOTONIC)

现在我想知道: 我应该简单地尝试将数字作为字符串进行比较(作为解决方法),还是应该使用精心设计的(“正确”)解决方案,例如https://stackoverflow.com/a/33024979/6607497

perl floating-point
2个回答
0
投票

您的帖子没有显示任何需要使用某种替代比较来实现相等性的原因。相反,它表明到 JSON 的转换没有产生足够的数字来区分不同的数字。解决方案是在转换为 JSON 时生成更多数字。产生 17 位有效十进制数字足以区分 IEEE-754 二进制 64 格式(也称为“双精度”)中的数字。

您的示例代码显示了时间比较。在这种情况下,不同的时间实际上是不同的时间,因此相等的比较应该将它们报告为不同的。当执行带有舍入误差的计算时,就会出现接受不同数字相等的原因,因此两个计算结果不同,但如果使用精确的实数算术计算,则它们会相等。然而,没有通用的解决方案来比较包含先前操作错误的浮点数。所有测试“近似相等”的方法都有可能错误地接受相等的数字,而如果使用精确的实数算术计算,则这些数字将不相等。可以接受的误差量(因为应用程序及其用户可以容忍)和必须接受的误差量(因为舍入误差可能累积到该量)特定于应用程序及其用途以及涉及具体数据。


0
投票

每个计算机科学家都应该了解浮点运算

通常,您不会比较浮点数是否相等,因为它们无法准确表示所有(大多数?)值。当该值试图在硬件精度内接近您想要的值时,低位数字总是有一点摆动空间。你甚至可能有很多毫无意义的额外数字,因为准确性已经发生了漂移。

相反,您可以比较它们,看看它们在某个阈值(有时称为“epsilon”)内是否足够接近:

if( abs($self->time - $other->time) < $epsilon ) { ... }

人们尝试用其他方法来处理这个问题。一种方法是将所有内容都设为整数,这样就没有小数值。这一直有效,直到您遇到整数的最大值为止。这让你的序列化器变得更容易一些。

您还可以使用更重量级的库,例如Math::BigRat,但是您仍然必须正确地序列化和反序列化它。

© www.soinside.com 2019 - 2024. All rights reserved.