比较两个相同的“文字”浮点数是否相等呢?

问题描述

这个问题与语言无关,但是代码是用Java编写的。

我们都听说过比较浮点数是否相等通常是错误的。但是,如果我想比较两个完全相同的文字浮点值(或表示完全相同的文字值转换为浮点的字符串)怎么办?

我非常确定这些数字将完全相等(好吧,因为它们必须在二进制中相等-完全相同的东西怎么会导致两个不同的二进制数字?!),但是我想确定。

情况1:

void test1() {
    float f1 = 4.7;
    float f2 = 4.7;
    print(f1 == f2);
}

情况2:

class Movie {
    String rating; // for some reason the type is String
}
void test2() {
    movie1.rating = "4.7";
    movie2.rating = "4.7";

    float f1 = Float.parse(movie1.rating);
    float f2 = Float.parse(movie2.rating);

    print(f1 == f2);
}

在两种情况下,表达式f1 == f2的结果应为true。我对吗?如果rating具有相同的文字浮点数或字符串值,是否可以安全地比较它们是否相等?

解决方法

是的。始终对相同的编译时间常数进行求值。

考虑一下,它们必须相同,因为只有一个编译器,它将确定性地将文字转换为它们的浮点表示形式。

,

有一条经验法则应适用于所有编程经验法则(经验法则?):

它们过于简单,如果推得太远,将导致笨拙的决策。 如果您没有完全理解拇指法则背后的意图,您将陷入困境。经验法则也许仍然是一种净积极的态度(不加思索地应用它会使事情变得比恶化更糟),但它会造成损害,在任何情况下都不能用作辩论的理由。

因此,考虑到这一点,显然,问这个问题没有意义:

“考虑到存在“不使用==比较浮点数”的经验法则,总是不好吗?”

答案非常明显:Du,不。这并不总是坏的,因为经验法则从定义上讲几乎是正确的,即使不是常识,也永远不会适用。

那么让我们分解一下。

为什么有个经验法则,您不应该==比较浮点数?

您的问题表明您已经知道:这是因为对以IEEE754概念(例如Java的doublefloat表示的浮点进行任何数学运算都是不精确的(与Java的{{1} },这是确切的*)。

面对经验法则时,应该做所有应该做的事情,一旦发现经验法则为何存在并意识到它不适用于您的情况:完全忽略它。

也许您的问题可以归结为:我

思考我已经掌握了经验法则,但是也许我缺少了一些东西。除了“浮点数学运算会引入小的偏差使==比较变得混乱”(不适用于这种情况)之外,还有其他我不了解的经验法则的原因吗?

在这种情况下,我的答案是:据我所知,不。

*)但是BigDecimal有其自身的相等性问题,例如:两个BigDecimal对象是否精确地表示相同的数学数字,但被配置为以不同的比例“相等”呈现?这取决于您的观点是数字还是表示精确的小数点数字的对象以及一些元属性,包括如何呈现它以及如何在明确要求的情况下取整。出于价值考虑,BD的BigDecimal实现必须做出选择,并在2个相等的含义均等有效的解释之间进行选择,选择“我代表一个数字”,而不是“我代表一个数字”带有一堆元数据”。所有JPA / Hibernate堆栈中都存在相同的选择:JPA对象是否表示“数据库中的行”(因此,相等性仅由主键值定义,如果尚未保存,则两个对象不能相等,甚至不能相等)本身,除非具有相同的引用标识),否则它是否代表该行所代表的内容,例如一个学生,而不是“代表学生的数据库中的一行”,在这种情况下,unid是一个与身份无关紧要的字段,所有其他字段(姓名,出生日期,社会保险号等)都可以。平等很难。

答案 2 :(得分:0)

是的,您可以像这样比较浮点数。问题是,即使将4.7转换为浮点数时4.7不是4.7,它也会一致地转换为相同的值。

通常,比较这样的浮点数本身并不是错误的。但是对于更复杂的数学,您可能想要使用Math.round()或设置“相同”差异范围,以使两者应计为“相同”。

定点数也具有任意性。例如

1,000,001

大于

1.000,000

这两个数字是否不同?这取决于您需要的精度。但是对于大多数目的,这些数字在功能上是相同的

答案 3 :(得分:0)

这个问题与语言无关……

实际上,这里没有浮点问题,答案完全取决于语言。

没有浮点问题,因为IEEE-754很清楚:当且仅当两个浮点数据对应于相同的实数时,两个浮点数据(有限数,无穷大和/或NaN)才相等。 >

存在语言问题,因为文字如何映射到浮点数以及源文本如何映射到操作因语言而异。例如,C 2018 6.4.4.2 5说:

所有具有相同源格式 77)的浮点常量应转换为具有相同值的相同内部格式。

脚注77说:

1.231.230123e-2123e-021.23L都是不同的源格式,因此无需转换为相同的内部格式和值

因此,C标准允许1.23 == 1.230评估为false。 (由于历史原因,允许这样做,将其留为实现质量问题。)如果用“相同”的文字浮点值表示完全相同的源文本,则在C中不会发生此问题;在特定的C实现中,每次相同的源文本都必须产生相同的浮点值。但是,这个例子告诉我们要谨慎。

C还允许实现方式在执行浮点运算方面具有灵活性:它允许实现方式在计算表达式时使用超出标称精度的值,并且允许在同一表达式的不同部分使用不同的精度。因此1./3. == 1./3.可以评估为false。 某些语言(例如Python)没有良好的正式规范,并且对如何执行浮点运算基本没有任何意见。可以想象,Python实现可以使用处理器寄存器中可用的超高精度将源文本1.3转换为long double或类似类型,然后将其保存为double,然后转换为源文本1.3long double,然后检索double并将其与仍在寄存器中的long double进行比较,并得到指示不平等的结果。

我所知道的实现中不会发生这种问题,但是,当问这样的问题时,问一个规则是否始终成立,无论使用哪种语言,都会为可能的异常敞开大门。

,

是的,您可以像这样比较浮点数。问题是,即使将4.7转换为浮点数时4.7不是4.7,它也会一致地转换为相同的值。

通常,比较这样的浮点数本身并不是错误的。但是对于更复杂的数学,您可能想要使用Math.round()或设置“相同”差异范围,以使两者应计为“相同”。

定点数也具有任意性。例如

1,000

这两个数字是否不同?这取决于您需要的精度。但是对于大多数目的,这些数字在功能上是相同的

,

这个问题与语言无关……

实际上,这里没有浮点问题,答案完全取决于语言。

没有浮点问题,因为IEEE-754很清楚:当且仅当两个浮点数据对应于相同的实数时,两个浮点数据(有限数,无穷大和/或NaN)才相等。 >

存在语言问题,因为文字如何映射到浮点数以及源文本如何映射到操作因语言而异。例如,C 2018 6.4.4.2 5说:

所有具有相同源格式 77)的浮点常量应转换为具有相同值的相同内部格式。

脚注77说:

1.231.230123e-2123e-021.23L都是不同的源格式,因此无需转换为相同的内部格式和值

因此,C标准允许1.23 == 1.230评估为false。 (由于历史原因,允许这样做,将其留为实现质量问题。)如果用“相同”的文字浮点值表示完全相同的源文本,则在C中不会发生此问题;在特定的C实现中,每次相同的源文本都必须产生相同的浮点值。但是,这个例子告诉我们要谨慎。

C还允许实现方式在执行浮点运算方面具有灵活性:它允许实现方式在计算表达式时使用超出标称精度的值,并且允许在同一表达式的不同部分使用不同的精度。因此1./3. == 1./3.可以评估为false。 某些语言(例如Python)没有良好的正式规范,并且对如何执行浮点运算基本没有任何意见。可以想象,Python实现可以使用处理器寄存器中可用的超高精度将源文本1.3转换为long double或类似类型,然后将其保存为double,然后转换为源文本1.3long double,然后检索double并将其与仍在寄存器中的long double进行比较,并得到指示不平等的结果。

我所知道的实现中不会发生这种问题,但是,当问这样的问题时,问一个规则是否始终成立,无论使用哪种语言,都会为可能的异常敞开大门。