Is comparing two same "literal" float numbers for equality wrong?

Stack Overflow Asked by Mahozad on September 5, 2020

This question is kind of language-agnostic but the code is written in Java.

We have all heard that comparing floating-point numbers for equality is generally wrong. But what if I wanted to compare two exact same literal float values (or strings representing exact same literal values converted to floats)?

I’m quite sure that the numbers will be exactly equal (well, because they must be equal in binary—how can the exact same thing result in two different binary numbers?!) but I wanted to be sure.

Case 1:

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


Case 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);
}


In both situations, the expression f1 == f2 should result in true. Am I right? Can I safely compare ratings for equality if they have the same literal float or string values?

This question is kind of language-agnostic…

Actually, there is no floating-point issue here, and the answer depends entirely on the language.

There is no floating-point issue because IEEE-754 is clear: Two floating-point datums (finite numbers, infinities, and/or NaNs) compare as equal if and only if they correspond to the same real number.

There are language issues because how literals are mapped to floating-point numbers and how source text is mapped to operations differs from language to language. For example, C 2018 6.4.4.2 5 says:

All floating constants of the same source form77) shall convert to the same internal format with the same value.

And footnote 77 says:

1.23, 1.230, 123e-2, 123e-02, and 1.23L are all different source forms and thus need not convert to the same internal format and value.

Thus the C standard permits 1.23 == 1.230 to evaluate to false. (There are historical reasons this was permitted, leaving it as a quality-of-implementation issue.) If by “same” literal float value, you mean the exact same source text, then this problem does not occur in C; the exact same source text must produce the same floating-point value each time in a particular C implementation. However, this example teaches us to be cautious.

C also allows implementations flexibility in how floating-point operations are performed: It allows an implementation to use more than the nominal precision in evaluating expressions, and it allows using different precisions in different parts of the same expression. So 1./3. == 1./3. could evaluate to false. Some languages, like Python, do not have a good formal specification and are largely silent about how floating-point operations are performed. It is conceivable a Python implementation could use excess precision available in processor registers to convert the source text 1.3 to a long double or similar type, then save it somewhere as a double, then convert the source text 1.3 to a long double, then retrieve the double to compare it to the long double still in registers and get a result indicating inequality.

This sort of issue does not occur in implementations I am aware of, but, when asking a question like this, asking whether a rule always holds, regardless of language, leaves the door open for possible exceptions.

Answered by Eric Postpischil on September 5, 2020

Yes, you can compare floats like this. The thing is that even if 4.7 isn't 4.7 when converted to a float, it will be converted consistently to the same value.

In general it is not wrong per se to compare floats like this. But for more complex math, you might want to use Math.round() or set a "sameness" difference span that the two should be within to be counted as "the same".

There is also an arbitrariness to fixed point numbers. For instance

1,000,000,001


is bigger than

1.000,000,000


Are these two numbers different? It depends on the precision you need. But for most purposes, these numbers are functionally the same

Answered by ControlAltDel on September 5, 2020

There's a rule of thumb that you should apply to all programming rules of thumb (rule of thumbs?):

They are oversimplified, and will result in boneheaded decision making if pushed too far. IF you do not fully -grok- the intent behind the rule of thumb, you will mess up. Perhaps the rule of thumb remains a net positive (applying it without thought will improve things more than it will make them worse), but it will cause damage, and in any case it cannot be used as an argument in a debate.

So, with that in mind, clearly, there is no point in asking the question:

"Giving that the rule of thumb 'do not use == to compare floats' exists, is it ALWAYS bad?".

The answer is the extremely obvious: Duh, no. It's not ALWAYS bad, because rules of thumb pretty much by definition, if not by common sense, never ALWAYS apply.

So let's break it down then.

WHY is there a rule of thumb that you shouldn't == compare floats?

Your question suggests you already know this: It's because doing any math on floating points as represented by IEEE754 concepts such as java's double or float are inexact (vs. concepts like java's BigDecimal, which is exact *).

Do what you should always do when faced with a rule of thumb that, upon grokking why the rule of thumb exists and realizing it does not apply to your scenario: Completely ignore it.

Perhaps your question boils down to: I THINK I grok the rule of thumb, but perhaps I'm missing something; aside from the 'floating point math introduces small deviations which mess up == comparison', which does not apply to this case, are there any other reasons for this rule of thumb that I am not aware of?

In which case, my answer is: As far as I know, no.

*) But BigDecimal has its own equality problems, such as: Are two BigDecimal objects that represent the same mathematical number precisely, but which are configured to render at a different scale 'equal'? That depends on whether your viewpoint is that they are numbers or objects representing an exact decimal point number along with some meta properties including how to render it and how to round things if explicitly asked to do so. For what it is worth, the equals implementation of BD, which has to make a sophie's choice and choose between 2 equally valid interpretations of what equality means, chooses 'I represent a number', not 'I represent a number along with a bunch of metadata'. The same sophie's choice exists in all JPA/Hibernate stacks: Does a JPA object represent 'a row in the database' (thus equality being defined solely by the primary key value, and if not saved yet, two objects cannot be equal, not even to itself, unless the same reference identity), or does it represent the thing that the row represents, e.g. a student, and not 'a row in the DB that represents a student', in which case unid is the one field that does NOT matter for identity, and all the others (name, birthdate, social security number, etc) do. equality is hard.

Answered by rzwitserloot on September 5, 2020

Yes. Compile time constants that are the same are evaluated consistently.

If you think about it, they must be the same, because there’s only one compiler and it converts literals to their floating point representation deterministically.

Answered by Bohemian on September 5, 2020

Related Questions

Are these Threads synchronized?

3  Asked on November 12, 2020 by haoshoku

How to pause and resume a while loop in Python?

5  Asked on November 11, 2020 by mentalcombination

How does thrust determine arguments to pass to functor

1  Asked on November 11, 2020 by a_man

Error In WordPress On All Pages Just Below The Header (Using Theme elumine)

2  Asked on November 10, 2020 by pallav

How can I style specific symbols in an element?

6  Asked on November 10, 2020 by ankit-aggarwal

Debugging C++ program that reads external files in visual studio code

1  Asked on November 10, 2020 by arpit-bhadauria

Flutter crash after open apps

4  Asked on November 10, 2020 by zukijuki

Is there another way to enable taxes accountability in odoo?

0  Asked on November 9, 2020 by eric

Java alternative of product function of python form itertools

1  Asked on November 8, 2020 by vipul-tyagi

What is wrong with my cython implementation of erosion operation of mathematical morphology

1  Asked on November 8, 2020 by kaan-e

Kubernetes – How to run local image of jenkins

1  Asked on November 8, 2020 by jerome12

How to avoid ambiguous template instantiation?

2  Asked on November 8, 2020 by wintergreen_plaza

leetcode algorithm edgecase issue

3  Asked on November 7, 2020 by stephen1993

Arrows in API strings?

1  Asked on November 6, 2020 by vichofs

How to use SQL PARTITION BY GROUPS?

2  Asked on November 5, 2020 by radagast

react start cannot find files in public folder

2  Asked on November 5, 2020 by minh-triet

tf.keras.utils.to_categorical raises TypeError in graph mode

1  Asked on November 5, 2020 by borun-chowdhury

pushViewController & popViewController animation no longer functioning in iOS 14

1  Asked on November 5, 2020 by catas

What is the equivalent of Web Forms “Page_Load” in ASP.Net Core 5, so my code will run before any page loading?

1  Asked on November 5, 2020 by fiverbox-com

Azure IoT Hub MQTT failure(Without SDK)

1  Asked on November 5, 2020 by govtham