9

Is there ever a case where a comparison (equals()) between two floating point values would return false if you compare them as DOUBLE but return true if you compare them as FLOAT?

I'm writing some procedure, as part of my group project, to compare two numeric values of any given types. There're 4 types I'd have to deal with altogether : double, float, int and long. So I'd like to group double and float into one function, that is, I'd just cast any float to double and do the comparison.

Would this lead to any incorrect results?

Thanks.

paxdiablo
889k243 gold badges1.6k silver badges2k bronze badges
asked Apr 10, 2012 at 4:16
2
  • So the question is explicitly about whether expansion from single to double precision can affect equality? Because the knee-jerk response to the opening sentence would be to talk about aliasing when losing precision. Commented Apr 10, 2012 at 4:20
  • Sorry, I guess my question was a bit confusing. I've rephrased it for clarity. Commented Apr 10, 2012 at 4:27

7 Answers 7

15

If you're converting doubles to floats and the difference between them is beyond the precision of the float type, you can run into trouble.

For example, say you have the two double values:

9.876543210
9.876543211

and that the precision of a float was only six decimal digits. That would mean that both float values would be 9.87654, hence equal, even though the double values themselves are not equal.

However, if you're talking about floats being cast to doubles, then identical floats should give you identical doubles. If the floats are different, the extra precision will ensure the doubles are distinct as well.

answered Apr 10, 2012 at 4:22
Sign up to request clarification or add additional context in comments.

6 Comments

Yes, I'm talking about a widening conversion from float to double. So you're saying I shouldn't get any data-correctness issue when doing this, right? Is there any reason why one would want to do the comparisons in float rather than in double? I remember we used to get some complaints about doing everything in double(but I forgot why. This got nothing to do with performance or memory, by the way). Thanks
I tend to use doubles for everything nowadays - gone are the days when I cared about saving a few bytes here and there :-) Of course, if you're maintaining a billion-element array, you may want to consider using the smaller data type.
@paxdiablo: Agreed, a billion elements is about the time when I start thinking about using float.
@user1064918: You may have trouble if you want to compare doubles to floats - see my answer below. I agree with paxdiablo- if you can just use doubles everywhere, you should :)
"Yes" is incorrect. If you have two floats and you widen them to double then the comparison result of the doubles will always be the same as the comparison result of the floats. The "Yes" answer appears to be for a separate question about converting doubles to floats - it's a correct answer to a different question, which is confusing.
|
9

As long as you are not mixing promoted floats and natively calculated doubles in your comparison you should be ok, but take care:

Comparing floats (or doubles) for equality is difficult - see this lengthy but excellent discussion.

Here are some highlights:

  1. You can't use ==, because of problems with the limited precision of floating point formats

  2. float(0.1) and double(0.1) are different values (0.100000001490116119384765625 and 0.1000000000000000055511151231257827021181583404541015625) respectively. In your case, this means that comparing two floats (by converting to double) will probably be ok, but be careful if you want to compare a float with a double.

  3. It's common to use an epsilon or small value to make a relative comparison with (floats a and b are considered equal if a - b < epsilon). In C, float.h defines FLT_EPSILON for exactly this purpose. However, this type of comparison doesn't work where a and b are both very small, or both very large.

  4. You can address this by using a scaled-relative-to-the-sizes-of-a-and-b epsilon, but this breaks down in some cases (like comparisons to zero).

  5. You can compare the integer representations of the floating point numbers to find out how many representable floats there are between them. This is what Java's Float.equals() does. This is called the ULP difference, for "Units in Last Place" difference. It's generally good, but also breaks down when comparing against zero.

The article concludes:

Know what you’re doing

There is no silver bullet. You have to choose wisely.

  • If you are comparing against zero, then relative epsilons and ULPs based comparisons are usually meaningless. You’ll need to use an absolute epsilon, whose value might be some small multiple of FLT_EPSILON and the inputs to your calculation. Maybe.
  • If you are comparing against a non-zero number then relative epsilons or ULPs based comparisons are probably what you want. You’ll probably want some small multiple of FLT_EPSILON for your relative epsilon, or some small number of ULPs. An absolute epsilon could be used if you knew exactly what number you were comparing against.
  • If you are comparing two arbitrary numbers that could be zero or non-zero then you need the kitchen sink. Good luck and God speed.

So, to answer your question:

  • If you are downgrading doubles to floats, then you might lose precision, and incorrectly report two different doubles as equal (as paxdiablo points out.)
  • If you are upgrading identical floats to double, then the added precision won't be a problem unless you are comparing a float with a double (Say you'd got 1.234 in float, and you only had 4 decimal digits of accuracy, then the double 1.2345 MIGHT represent the same value as the float. In this case you'd probably be better to do the comparison at the precision of the float, or more generally, at the error level of the most inaccurate representation in the comparison).
  • If you know the number you'll be comparing with, you can follow the advice quoted above.
  • If you're comparing arbitrary numbers (which could be zero or non-zero), there's no way to compare them correctly in all cases - pick one comparison and know its limitations.

A couple of practical considerations (since this sounds like it's for an assignment):

  • The epsilon comparison mentioned by most is probably fine (but include a discussion of the limitations in the write up). If you're ever planning to compare doubles to floats, try to do it in float, but if not, try to do all comparisons in double. Even better, just use doubles everywhere.

  • If you want to totally ace the assignment, include a write-up of the issues when comparing floats and the rationale for why you chose any particular comparison method.

answered Apr 10, 2012 at 5:54

1 Comment

As the author of the linked article I must object to the statement "You can't use ==, because of problems with the limited precision of floating point formats". Yes, you can use ==. It is often not appropriate, and it must be used with care, but as I say in randomascii.wordpress.com/2014/01/27/…, "Sometimes there really is an answer that is correct, and in those cases anything less than perfection is just sloppy." Floating-point equality tests have their place.
2

I don't understand why you're doing this at all. The == operator already caters for all possible types on both sides, with extensive rules on type coercion and widening which are already specified in the relevant language standards. All you have to do is use it.

answered Apr 10, 2012 at 4:20

6 Comments

No, I was oversimplifying my problem. It's way more than merely a overriding the == operator task. But I'd just like to know if I would get incorrect results when trying to promote float to double and do a 64bit comparison as opposed to 32bit comparison
== can't be used to safely compare doubles in C - in fact, GCC will produce a warning if you try.
@user1064918 I didn't say anything about 'overriding'. You just have to use the operator. It already knows how to compare any primitive type to any other.
well, my question was a bit confusing. I've rephrased it.
@user1064918 I will only add that the language standards I referred to don't specify promotion from float to double for ==.
|
2

I'm perhaps not answering the OP's question but rather responding to some more or less fuzzy advice which require clarifications.

Comparing two floating point values for equality is absolutely possible and can be done. If the type is single or double precision is often of less importance.

Having said that the steps leading up to the comparison itself require great care and a thorough understanding of floating-point dos and don'ts, whys and why nots.

Consider the following C statements:

result = a * b / c;
result = (a * b) / c;
result = a * (b / c);

In most naive floating-point programming they are seen as "equivalent" i e producing the "same" result. In the real world of floating-point they may be. Or actually, the first two are equivalent (as the second follows C evaluation rules, i e operators of same priority left to right). The third may or may not be equivalent to the first twp.

Why is this?

"a * b / c" or "b / c * a" may cause the "inexact" exception i e an intermediate or the final result (or both) is (are) not exact(ly representable in floating point format). If this is the case the results will be more or less subtly different. This may or may not lead to the end results being amenable to an equality comparison. Being aware of this and single-stepping through operations one at a time - noting intermediate results - will allow the patient programmer to "beat the system" i e construct a quality floating-point comparison for practically any situation.

For everyone else, passing over the equality comparison for floating-poiny numbers is good, solid advice.

It's really a bit ironic because most programmers know that integer math results in predictable truncations in various situations. When it comes to floating-point almost everyone is more or less thunderstruck that results are not exact. Go figure.

answered Apr 13, 2012 at 20:19

Comments

1

You should be okay to make that cast as long as the equality test involves a delta.

For example: abs((double) floatVal1 - (double) floatVal2) < .000001 should work.

Edit in response to the question change

No you would not. The above still stands.

answered Apr 10, 2012 at 4:21

3 Comments

Shouldn't the delta be constructed from (negative) power of two components rather than expressed as a decimal value where the programmer can't be exacty sure what the compiler-generated equivalent will be? I mean it most certainly won't be ".000001". When you say "should work" you don't sound especially sure that it "will work". Have you tested the advice you've provided?
@OlofForshell That was a quick and dirty example to show that the comparison should be valid. I wasn't the only one to do so (e.g. @PetrAbdulin). You're right, ideally the delta would be a number generated to be on the order of the given floats. As for the fact that I said "should work", it's in my nature as a programmer to not speak in absolutes. But that comparison would work. You didn't sound too certain about your advice yourself: "Shouldn't the delta be constructed...". Have you tested that advice?
And by "That comparison would work", I meant that the casting part of it would work. The delta should created as you suggested.
1

For the comparison between float f and double d, you can calculate the difference of f and d. If abs(f-d) is less than some threshold, you can think of the equality holds. These threshold could be either absolute or relative as your application requirement. There are some good solutions Here. And I hope it helpful.

answered Apr 10, 2012 at 4:57

Comments

0

Would I ever get an incorrect result if I promote 2 floats to double and do a 64bit comparison rather than a 32bit comparison?

No.

If you start with two floats, which could be float variables (float x = foo();) or float constants (1.234234234f) then you can compare them directly, of course. If you convert them to double and then compare them then the results will be identical.

This works because double is a super-set of float. That is, every value that can be stored in a float can be stored in a double. The range of the exponent and mantissa are both increased. There are billions of values that can be stored in a double but not in a float, but there are zero values that can be stored in a float but not a double.

As discussed in my float comparison article it can be tricky to do a meaningful comparison between float or double values, because rounding errors may have crept in. But, converting both numbers from float to double doesn't not change this. All of the mentions of epsilons (which are often but not always needed) are completely orthogonal to the question.

On the other hand, comparing a float to a double is madness. 1.1 (a double) is not equal to 1.1f (a float) because 1.1 cannot be exactly represented in either.

answered Feb 19, 2016 at 17:00

Comments

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.