-4

Imagine that an inexperienced programmer tries to compare 2 floats for equivalence:

a = 0.01
b = 0.1 ** 2
print(a == b)

Is there any language that would throw an exception on such foolishness? Or a way to override the default behaviour in python and/or javascript?

If not, what is the reason behind language design decision to allow the operation?


EDIT: Looks like I've been accused of suggesting to make comparisons of floats impossible. I have nothing against a < b or float_eq(a, b). It's just that most people have very different intuition about what ==, !=, >= and <= operators are supposed to do from what they actually do with float numbers. There are various precedents in language design to prevent some of the dumber mistakes people tend to make, like disallowing if(a=b) in python or use strict in perl.
Community
  • 1
  • 1
Aprillion
  • 21,510
  • 5
  • 55
  • 89
  • 1
    It's a meaningful operation. You shouldn't just disallow it because it might not do what you expect any more than you should disallow `0.1 + 0.2`. – user2357112 Jun 28 '14 at 22:07
  • 1
    You can safely do it for some values, zero being an interesting one. – Quentin Jun 28 '14 at 22:07
  • 2
    Why could I not compare `0.1 ** 2` and `0.01` if I wanted to? It is possible to misuse addition between integers, should that throw an exception too? – Pascal Cuoq Jun 28 '14 at 22:08
  • @PascalCuoq because some people don't expect that `0.1**2 == 0.01` will be false – Aprillion Jun 28 '14 at 22:20
  • 2
    Being able to compare two `float`s for equality is very useful. Floating-point math is very, very different from real-number math, and the sooner programmers realise this the fewer surprises they will face. Trying to hide the difference because they could be confused is kind of a daft idea, and that's why no languages do it. – tmyklebu Jun 28 '14 at 22:25
  • I'd argue that, ignoring NaNs, people's intuition works perfectly for `==` and `!=` and `>=` and `<=`. – tmyklebu Jun 28 '14 at 22:41
  • @tmyklebu without googling/coding, what does your intuition tell you about `0.1*10` - is it less or more than 1? or `0.1**2` - is it less or more than 0.01? is the answer same in single, double and quad precision? and given the number of SO questions about rounding errors as evidence to the contrary, why do you think most other people would share your intuition? – Aprillion Jun 28 '14 at 22:46
  • @deathApril: Why are you blaming floating-point **comparison** for `0.1` not being exactly representable as a `double`? – tmyklebu Jun 28 '14 at 22:54
  • @tmyklebu the intuition about equality comes from belief that 0.1 in computers is the same thing as the real number 0.1 - I never said comparison is reason of the imprecision, I believe the imprecision makes the comparison unintuitive – Aprillion Jun 28 '14 at 23:02
  • @deathApril: Well, no. The intuition about equality is correct. The intuition about what the objects are that you're dealing with is incorrect. You can come up with formulae that give arbitrarily surprising results when implemented in floating-point arithmetic, and this has nothing to do with comparison. – tmyklebu Jun 28 '14 at 23:08
  • @tmyklebu: Being able to compare floating-point quantities for equality is useful, though if I were designing a language I'm not sure I'd allow use of the primary equality operator between two "normal" NaN-able IEEE floats. I'd rather have the language forbid any use of `==` with any combination of operands which couldn't be guaranteed to implement an equivalence relation. Although many non-IEEE types implement an equivalence relation, IEEE-754 forbids floating-point variables from doing so, and thus use of such things with `==` is dodgy [equality comparison would be okay if at least... – supercat Jul 07 '14 at 16:18
  • ...one operand were known not to be `NaN`, since such a comparison could behave as an equivalence relation for any combination of operands that would compile]. – supercat Jul 07 '14 at 16:19
  • @supercat: Yeah. That choice was made so that it's as easy as possible to write numerical code. I agree with the decision because the volume of numerical code I write is greater than the volume of code I write where I care about NaNs and I really want `<` to be a total order. (In which case I'd just sort or whatever by bit-pattern.) – tmyklebu Jul 07 '14 at 16:23
  • @tmyklebu: In what cases is having `Nan!=NaN` return `true` actually useful? I could see usefulness to having `NaN==NaN` and `NaN!=NaN` both return false (in which case the latter would implement an equivalence relation even if the former didn't) but as implemented I see no clean way to do things like cache function results, which would seem an essential aspect of a lot of numeric code. – supercat Jul 07 '14 at 16:31
  • @supercat: If a NaN gets introduced at some point in a computation, all comparisons involving that NaN fail, and that NaN propagates whenever you do arithmetic using it. Lots of numerical code loops until either convergence or an iteration limit is hit. A convergence test of the form `if (error < tol) return;` will thus always fail if a NaN shows up, meaning the iteration limit will get hit, meaning the user will be notified that something's wrong. – tmyklebu Jul 07 '14 at 16:36
  • @supercat: The usage model for a lot of numerical code is different from the usage model for, say, a sorting algorithm. Numerical code often allows itself to report failure and users are expected to handle it how they see fit. This is because recovery from failure can be very, very expensive. If a sorting algorithm fails, it's a crap sorting algorithm. – tmyklebu Jul 07 '14 at 16:39
  • @tmyklebu: Many kinds of numerical processing entail sorting, or require skipping calculations whose result has already been determined. If e.g. NaN sorts below -inf, then one may find the spread between 10th and 90th percentiles in a set of values using a simple sort; if the bottom element is NaN, one may either flag the overall result as invalid or count the number of NaN values and--if no more than 10% of the array are NaN, adjust the spread appropriately. Are things like percentile calculations not common in numerical processing? – supercat Jul 07 '14 at 16:48
  • @supercat: In one philosophy, if a NaN shows up in your calculation, something went south. Say you sort a list containing a NaN and then fish out a couple elements. You *don't care* what the 10th and 90th percentiles are because the calculation went south. All you have to do is detect that before returning to the user. – tmyklebu Jul 07 '14 at 16:52
  • @tmyklebu: If you don't care what the percentiles are, but merely that one succeeded, sorting NaN below -Inf will allow one to easily test whether any results yielded NaN (just look at the minimum element). Is there any "nice" way to e.g. obtain the median of a data set without having to explicitly test every element for NaN, that will guarantee that e.g. in a nine-item set with one NaN, it will return something that ranks third through sixth? – supercat Jul 07 '14 at 18:28
  • @supercat: Sorting NaN below -inf would make `if (norm < tol)` succeed if `norm` is NaN. – tmyklebu Jul 07 '14 at 18:36
  • @tmyklebu: In most cases, that would be just fine (the computation would generally finish with a NaN result without wasting time on iterations which would have no useful purpose); in those cases where it isn't, one could test -norm against -tol. – supercat Jul 07 '14 at 19:05
  • @tmyklebu: I guess my main point was that--especially if a language provides non-NaN-able types as well as NaN-able ones--I would think it useful to have an "equivalence" operator which will promise to implement an equivalence relation for all combinations of operands it accepts. For comparisons like `long` to `double`, either behave as though both arguments were widened to a wider type capable of accommodating both or else refuse compilation. Since "normal" IEEE types don't implement an equivalence relation even with themselves, only allow == if at least one operand can't be NaN. – supercat Jul 08 '14 at 14:55

3 Answers3

3

Why throw an exception for a construct, float == float, where everything is static? Unless you were planning to accept if (false && float == float) …, you might as well reject this construct statically.

In fact, why have float == float in the language at all? There are perfectly good languages without floating-point. Programmers that are so inexperienced as to foolishly try to use floating-point computations would be better off using a programming language that does not have them. I would personally recommend Ook.

I should point out that it is possible to use floating-point intelligently, just like most other constructs found in programming languages can be used intelligently. The CRlibm library contains several beautiful patterns of subtle floating-point computations. It helps when the programming language gives the programmer strong hypotheses on the way source code will be translated to the IEEE 754 hardware instructions that nearly all processors have had for years. The CRlibm library is written for a C99 compiler that implements IEEE 754 without excess precision. You could implement the same library in Java, but not conveniently in C#, for instance.

In conclusion, floating-point equality is as useful as other floating-point operations, and it is mathematically exact, unlike most of them. You might as well not use floating-point at all, which you can do in any language, and in particular in C#. If you are not sure you can refrain from using floating-point computation if it is available in the programming language, program in Ook.

Pascal Cuoq
  • 79,187
  • 7
  • 161
  • 281
  • I dunno about the "inexperienced programmers should never use floating-point" thing. Answering "0.1 + 0.2 != 0.3?????" for the umpteenth time certainly isn't fun, but having an abstraction that sorta works like real numbers is pretty useful when you're starting out. – tmyklebu Jun 28 '14 at 22:27
  • @tmyklebu But that is not the subject of the question! This said, many languages have Rational, and many have Decimal, and even Decimal is apparently good enough at hiding its flaws that the sort of person who ask or answer questions such as http://stackoverflow.com/questions/588004/is-floating-point-math-broken think that it is exact. – Pascal Cuoq Jun 28 '14 at 22:33
  • making fixed point numbers with 10 decimal places the default format when someone types `var amount = 2.3;` would avoid a lot of sleepless nights,, I will have a look at `Ook`, thanks! – Aprillion Jun 28 '14 at 22:41
  • @deathApril The mention of Ook was a joke, sorry. But any modern high-level language should have Decimal or Rational to supplement float, so just use what is available in your favourite language. – Pascal Cuoq Jun 28 '14 at 22:43
2

0.01 == 0.1 ** 2 may or may not work (I haven't checked), but 0.25 == 0.5 ** 2 is absolutely guaranteed to work, and so is 0.1 ** 2 == 0.1 ** 2. It's important to understand why you can't necessarily count on floating point numbers to be equal where the equations say they should be equal, but in certain circumstances it is perfectly reasonable, and quite useful, to count on floating point equality. A language which disallowed the operation would be less powerful than one which did not.

Sneftel
  • 40,271
  • 12
  • 71
  • 104
  • `float_eq(a, b)` would be quite enough for "certain circumstances", there is no need for language to be less powerful.. there is a precedence in python not allowing variable assignment in `if (a = b)` so people are less likely to shoot themselves into their legs – Aprillion Jun 28 '14 at 22:23
  • 1
    You might as well make the program refuse to compile unless `# I promise to check my argument types` is written at the top of each function. Forcing programmers to jump through hoops to perform well-defined operations may occasionally be a good idea, but not generally, and it's decidedly un-Pythonic. – Sneftel Jun 28 '14 at 22:26
1

I don't know of a mainstream language that treats the use of '==' on floating point as an error, or that allows you to overload '==' for primitive types.

But I don't think it would be a good idea either, because '==' can be used safely on floating point in limited situations ... if you know what you are doing.

A better approach (if you want to protect programmers against naive mistakes with '==') would be to implement this as a rule in your language's preferred static code checker; e.g. the analogue to Lint (for C) and PMD or FindBugs (for Java).

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216