0

I find the result very strange. Why not 0.3? Can somebody tell me why this result? Is it possible to fix this.

 ?- X is 5.3-5.
    X = 0.2999999999999998.

    ?- 

My second question is how would I transform from 'hour' notation '13.45' ---->'15.30' into numbers of hours ? For example the period above calculated 15.30-13.45 would be 1.85. But I need to operate on parts of the hour and not the remainders of the numbers. Like 15 1/2 - 13 /4, this way is better. I try

?- X is (5.3-5)*100/60.
X = 0.4999999999999997.

?- X is (5.3-5)*100//60.
ERROR: ///2: Type error: `integer' expected, found `29.999999999999982'

Any suggestions?

false
  • 10,264
  • 13
  • 101
  • 209
vincent
  • 101
  • 1
  • 9
  • 2
    Something very similar recently came up [on the SWI-Prolog mailing list](https://lists.iai.uni-bonn.de/pipermail/swi-prolog/2014/012496.html), where the problem turned out to be that Pascal is using a lot of compiler magic to obscure the complexity of floating point numbers, causing confusion when Prolog didn't work the same way. – Daniel Lyons Feb 25 '14 at 17:29

3 Answers3

4

The answer you get from SWI is correct.
In many programming languages, and practically all Prolog systems, floating point implementations are based on a binary (radix = 2) system. The number 5.3 cannot be represented precisely in this system, so some approximation is chosen. Subtracting is particularly well suited to expose such inaccuracies.

?- X is 5.3-5-0.3.
   X = -1.6653345369377348e-16.

As long as you are staying with numbers that are the sum of powers of 2 (including 2-1, 2-2, ...) you will get precise results as far as the precision of the floats behind permits it:

?- X is 5.000244140625-5-0.000244140625.
   X = 0.0.

What ISO conforming Prolog systems have to do is to ensure that when writing a float with write-option quoted(true), the float is written in such a manner that it can be read back precisely.

As to your second question: (//)/2 is defined on integers only. If you want to convert a float to an integer you have in ISO Prolog the usual LIA-1 functions:

floor/1, truncate/1, round/1, ceiling/1.

?- X is round(5.3).
   X = 5.

However, I'd rather recommend using (div)/2 in place of (//)/2. The meaning of (//)/2 is no longer supported by LIA-1:2012 (Standard ISO/IEC 10967-1:2012) — for good reason. See this and this answer for details.

false
  • 10,264
  • 13
  • 101
  • 209
2

This is due to floating point arithmetic, which is (almost*) always imperfect. Computers work in binary, but people mostly work in base 10. This introduces some imprecision here and there; how bad the imprecision is depends on how the hardware and (sometimes) software in question works. But the key is that you can't predict exactly what the errors will be, only that there will be errors.

The result is that you can't expect 5.3-5 to be exactly 0.3; you need to test whether it is really close (like within 0.00000000000001) instead. When you get results like in your second example, you need to explicitly convert to an integer before using the result. You can read more in this answer.

[footnote:] Per the comments below, I will add that arithmetic using floating point numbers that happen to be powers of 2 (that is, 2^i) or sums of powers of 2 (e.g., 7=4+2+1) can be calculated exactly by a computer. There's a philosophical argument as to whether this is actually "floating point arithmetic." But it explains why, for example, most languages will report that 2.0 + 1.0 is 3, but 0.2 + 0.1 is something like 0.30000000000000004.

Community
  • 1
  • 1
elixenide
  • 44,308
  • 16
  • 74
  • 100
  • 2
    "which is always imperfect" is incorrect. It is perfect for radix-2 based numbers within a certain range. – false Feb 25 '14 at 17:41
  • Math using radix-2 based numbers is not floating point arithmetic. Those are different concepts. And while a computer may produce a correct result in some scenarios even for floating point arithmetic, that doesn't mean there isn't imprecision in the calculation; it means that the imprecision was too minor to make it into the memory space allocated for the calculation's result. – elixenide Feb 25 '14 at 17:43
  • 1
    @EdCottell: Some numbers are represented *exactly*, no imprecision whatsoever for those numbers. Please consult LIA-1 if you want to know the very details. – false Feb 25 '14 at 17:46
  • @false "Some numbers" =/= "floating point arithmetic." You're missing the point of my answer entirely. – elixenide Feb 25 '14 at 17:48
  • Ed, there is a set of numbers which admit a floating point representation with no loss of precision whatsoever. Lua, for instance, only has doubles and no integers because there is no error representing integers below a certain limit as doubles. @false is taking issue with the generality of "always imperfect." – Daniel Lyons Feb 25 '14 at 19:34
  • I understand that some numbers can be represented in floating point without a loss of precision. But floating point *arithmetic* -- i.e., operating on 2 or more numbers -- is imprecise by nature. – elixenide Feb 25 '14 at 19:36
  • @EdCottrell: Addition/subtraction/multiplication of Σ 2i numbers will give you again such a number. It's all exact within some range. Think of 0.875+0.5. And for "computers work in binary" see [this](http://stackoverflow.com/questions/1447215/why-arent-floating-point-decimal-numbers-hardware-accelerated-like-floating-poi). – false Feb 25 '14 at 19:52
  • @false I don't think we're communicating here, because your comments are not relevant to my point. See [this question and answer](http://stackoverflow.com/a/588014/2057919), which are relevant to this question, as well: imprecision is inherent in the IEEE 754 standard. Just because `0.875+0.5` has an exact *correct* answer doesn't necessarily mean that a computer will get an exact answer. For example, `0.875+0.555` also has an exactly correct answer, but many languages will respond with something imprecise, like `1.4300000000000002`. – elixenide Feb 25 '14 at 19:59
  • @EdCottrell: The example you give is irrelevant because `0.555` is not of the form Σ 2^i. `0.555` can only be represented exactly with radix = 10, not radix = 2. – false Feb 25 '14 at 20:04
  • @false My example is relevant, because the question is not about numbers of the form Σ 2^i. It's about a floating point operation. I wrote that "floating point arithmetic, which is always imperfect." This is true. Arithmetic involving numbers *that aren't true floating-point numbers*, but are actually integer powers of 2, is not the same thing. Arithmetic that involves numbers which must be represented as floating point numbers is always imprecise when performed by a (binary) computer. – elixenide Feb 25 '14 at 20:06
  • @EdCottrell: "Always" includes the numbers I mentioned. – false Feb 25 '14 at 20:10
  • @false I would argue "Floating point arithmetic" does not include manipulation solely of numbers that can be represented exactly in binary. That said, I have edited my answer to clarify my use of "always." If you were the downvoter, I would appreciate your reconsideration. – elixenide Feb 25 '14 at 20:17
2

Regarding this line:

X is (5.3-5)*100//60.

The // predicate is specifically integer divide and you're operating on floats. You can either do this:

X is round((5.3-5)*100/60).

Or

X is round((5.3-5)*100)//60.

Of course, in both of these cases, you get 0 because it's about 0.3 if you were doing floats. So it's unclear if that's what you really intended.

Other interesting options:

?- X is (5.3-5)*100/ 60.
X = 0.4999999999999997.

?- X is float(round((5.3-5)*100) rdiv 60).
X = 0.5.
lurker
  • 56,987
  • 9
  • 69
  • 103
  • Thanks, I didn't know about the 'round'. Very concise and helpful. – vincent Feb 25 '14 at 17:02
  • @vincent you might want to browse SWI Prolog's arithmetic functions: http://www.swi-prolog.org/pldoc/man?section=functions – lurker Feb 25 '14 at 17:03