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

浮点相等

莫乐
2023-03-14

众所周知,在比较浮点值时必须小心。通常,我们不使用=,而是使用一些基于epsilon或ULP的相等性测试。

然而,我想知道,当使用==时,是否存在任何情况?

看看这个简单的片段,哪些案例可以保证成功?

void fn(float a, float b) {
    float l1 = a/b;
    float l2 = a/b;

    if (l1==l1) { }        // case a)
    if (l1==l2) { }        // case b)
    if (l1==a/b) { }       // case c)
    if (l1==5.0f/3.0f) { } // case d)
}

int main() {
    fn(5.0f, 3.0f);
}

注意:我已经检查了这个和这个,但是它们不包括我的(所有)病例。

注2:似乎我必须添加一些附加信息,因此答案在实践中可能很有用:我想知道:

  • C标准的内容

这是我在当前标准草案中发现的唯一相关声明:

浮点类型的值表示是实现定义的。[注:本文件对浮点运算的准确性没有任何要求;另见[support.limits]。-结束说明]

那么,这是否意味着,即使是“案例a)”也定义了实现?我的意思是,l1==l1绝对是一个浮点运算。那么,如果一个实现是不准确的,那么l1==l1会是false吗?

我认为这个问题不是浮点==是否正常?的重复?。这个问题没有解决我所问的任何案例。相同的主题,不同的问题。我想知道案例a)-d)的具体答案,我在重复的问题中找不到答案。

共有3个答案

扶文光
2023-03-14

只有a)和b)保证在任何正常的实现中成功(详情请参阅下面的法律术语),因为它们比较了以相同方式派生并四舍五入为浮动精度的两个值。因此,两个比较值都保证与最后一位相同。

案例c)和d)可能会失败,因为计算和随后的比较可能比浮动具有更高的精度。不同的舍入应该足以使测试失败。

请注意,如果涉及无穷大或NAN,情况a)和b)仍然可能失败。

使用标准的N3242 C 11工作草案,我发现以下内容:

在描述赋值表达式的文本中,明确说明发生类型转换[expr.ass]3:

如果左操作数不是类类型,则表达式将隐式转换(第4条)为左操作数的cv非限定类型。

第4条涉及标准转换[conv],其中包含以下关于浮点转换的[conv.double]1:

浮点类型的prvalue可以转换为另一种浮点类型的prvalue。如果源值可以在目标类型中精确表示,则转换的结果就是该精确表示。如果源值位于两个相邻的目标值之间,则转换的结果是实现定义的这些值中的任何一个的选择。否则,行为是未定义的。

(我的重点。)

因此,我们可以保证转换的结果是实际定义的,除非我们处理的是可表示范围之外的值(如float a=1e300,即UB)。

当人们想到“内部浮点表示可能比代码中可见的更精确”时,他们会想到标准[expr]11中的以下句子:

浮点操作数的值和浮点表达式的结果可以比类型要求的精度和范围更高;这些类型不会因此而改变。

请注意,这适用于操作数和结果,而不适用于变量。所附脚注60强调了这一点:

强制转换运算符和赋值运算符仍必须执行5.4、5.2中所述的特定转换。9和5.17。

(我猜,这是Maciej Piechotka在评论中的脚注——在他一直使用的标准版本中,编号似乎发生了变化。)

所以,当我说float a=double\u表达式,我保证表达式的结果实际上是四舍五入的,可以用浮点表示(只有当值超出边界时才调用UB),并且a之后将引用该四舍五入值。

实现确实可以指定四舍五入的结果是随机的,从而打破情况a)和b)。不过,理智的实现不会这样做。

阴永逸
2023-03-14

人为的案件可能“起作用”。实际案例仍然可能失败。另一个问题是,优化通常会导致计算方式的微小变化,因此,从符号上来说,结果应该相等,但在数值上,它们是不同的。理论上,上述例子在这种情况下可能会失败。有些编译器提供了一种以牺牲性能为代价生成更一致结果的选项。我建议“始终”避免浮点数相等。

物理测量和数字存储浮点数的相等性通常是没有意义的。因此,如果比较代码中的浮点值是否相等,则可能是做错了什么。您通常希望大于或小于该值或在公差范围内。通常可以重写代码,以避免这些类型的问题。

陈扬
2023-03-14

然而,我想知道,有没有任何情况下,使用==是完全可以的?

当然有。一类例子是不涉及计算的用法,例如只应执行更改的setters:

void setRange(float min, float max)
{
    if(min == m_fMin && max == m_fMax)
        return;

    m_fMin = min;
    m_fMax = max;

    // Do something with min and/or max
    emit rangeChanged(min, max);
}

请参见浮点==是否正常?浮点==是否正常?。

 类似资料:
  • 我编写了一个程序来演示Go中的浮点错误: 它打印: 这与用C编写的相同程序的行为相匹配(使用双代码类型) 但是,如果改用,程序就会陷入无限循环!如果将C程序修改为使用而不是,它将打印 为什么在使用时,Go程序的输出与C程序的输出不一样?

  • 问题内容: 我有这个问题,我必须将公里转换成英里。我是一个新手程序员,所以请耐心等待。 到目前为止,这是我的代码: 它给我一个错误,说: 问题答案: 您正在尝试将a 设置为变量 要修复,请更改此行 至

  • 问题内容: 众所周知,即使是十进制格式的小数点后有固定数字的浮点数也无法准确表示。因此,我有以下程序要测试: 输出如下: 我无法从上述结果中回答两个问题,我正在寻求以下方面的帮助: 为什么使用的双重表示形式,并且看起来很精确,而没有。 为什么返回true? 问题答案: 我怀疑在这里不能正常工作。写入0.1时,获取确切值的一种可靠方法是write 。 “为什么0.1f + 0.2f == 0.3f返

  • 众所周知,浮点数,即使是小数点后固定数字的十进制格式,也不能准确表示。所以我有以下程序要测试: 输出如下: 以上结果中有两个问题我无法回答,我正在寻求帮助: 为什么0.1、0.2和0.3的双重表示看起来很精确,而0.1、0.2却不精确。

  • 问题内容: 我有一段代码的行为会有所不同,具体取决于我是通过字典获取转换因子还是直接使用它们。 以下代码将打印 但是,如果你更换用,并用它将打印 首先让我说我很确定这里发生了什么。我以前在C中看到过它,但是在Python中却从未见过,但是自从Python在C中实现以来,我们已经看到了它。 我知道浮点数将更改从CPU寄存器到缓存以及返回的值。我知道比较两个相等的变量应该返回false,如果其中一个被

  • 3.2. 浮点数 Go语言提供了两种精度的浮点数,float32和float64。它们的算术规范由IEEE754浮点数国际标准定义,该浮点数规范被所有现代的CPU支持。 这些浮点数类型的取值范围可以从很微小到很巨大。浮点数的范围极限值可以在math包找到。常量math.MaxFloat32表示float32能表示的最大数值,大约是 3.4e38;对应的math.MaxFloat64常量大约是1.8