1

I'm wondering if x/y when x and y are integers (but floating point type), is guaranteed to yield the same floating point value as kx/ky, where k is an integer.

So, for example, does 1.0/3, 2.0/6, 3.0/9, ... all yield the same exact floating point number (one that would compare equally with == operator)?

In case this is different per language/ platform, I am specifically interested in c++ on Linux.

Bryce Sandlund
  • 487
  • 2
  • 5
  • 17
  • Are `x` and `y` floating-point values that coincidentally happen to be integers? – templatetypedef Nov 21 '15 at 20:15
  • Yes, since otherwise integer division would occur. – Bryce Sandlund Nov 21 '15 at 20:16
  • In general floating point numbers should not be compared for equality, except with 0 which has an exact representation. – Shepard Nov 21 '15 at 20:56
  • Yes, but I needed to use it as a key in a c++ unordered map. If the result was guaranteed to be the same, it made the code much simpler. Though this may not be a great practice in software engineering, the code was competition code for a competitive programming problem. – Bryce Sandlund Nov 21 '15 at 21:18
  • 1
    @Shepard: I think that's more cautious than necessary. There are plenty of real numbers that can be represented exactly in floating-point, including 1.0, 2.0, 0.5, etc. – Keith Thompson Nov 21 '15 at 21:19
  • @KeithThompson Yes but you can't be sure that the result of a calculus will bring exactly that representation, even if it should. Personally I compare for equality only with 0, and only when there could be a multiplication with zero during the calculus. – Shepard Nov 21 '15 at 22:01
  • @Shepard: It depends on how the value was computed. `(1.0/3.0)*3.0` isn't guaranteed to yield `1.0` exactly, but given `double x = 1.0;`, I'd feel safe in assuming that `x == 1.0` is true (even if it's not absolutely guaranteed by the standard). And if the implementation supports IEEE floating-point, there are even more guaratees you can depend on. – Keith Thompson Nov 21 '15 at 22:05
  • @KeithThompson Yes sure, that's what I was talking about. As soon as you start using your number for calculus you cannot be sure that it will have the exact value that you think. And it is the case the 99% of the times, since I can't imagine when you simply declare a number and use it as it is. – Shepard Nov 21 '15 at 22:15

3 Answers3

2

As long as k*x and k*y operations are exact (the result fits in a floating point), then IEEE754 standard guarantees that you'll get the nearest floating point to the exact division result.

Obviously, since (k*x)/(k*y)=(x/y) in exact math, the nearest floating point will be the same for both.

If k*x or k*y does not fit into a float (the floating point operation is inexact), then you don't get any guaranty.

Concerning bare minimum guaranteed by C++, I don't know, but you can consider that most platforms do comply with these basic IEEE754 properties.

aka.nice
  • 9,100
  • 1
  • 28
  • 40
  • Most platforms are reasonable about this, but the usual semantics implemented for non-80-bit floating-point on (32-bit) x86 don't really provide you with any guarantees; not even that, `x == x` when `x` is not a NaN. – tmyklebu Nov 21 '15 at 21:05
  • @tmyklebu ah yes, for example evaluating the division with extended precision, then rounding to single/double precision with the risk of double rounding... One using a language with such a high cult to speed and such a low cult to correctness should prepare to learn about fp:strict and other complexifications. Fortunately, SSE is a bit more reasonnable. – aka.nice Nov 21 '15 at 21:36
0

If the calculations are done in the same precision, I think they'll end up the same. However, if that's not the case, both float->double and double->float conversions will create discrepancies. And that's not an impossible scenario (at least without fp:strict), since the compiler can mix FPU and SSE code (for instance, if it needs to call a function that's not implemented in SSE, or use it as an argument/return result for a cdecl function).

That said, you can also create a quotient (x/y) class and use it as the key. You can define all arithmetic for it, for instance

q0+q1 = (q0.x*q1.y+q1.x*q0.y)/(q0.y*q1.y)
q0<q1 = q0.x*q1.y*(q0.y*q1.y) < q1.x*q0.y*(q0.y*q1.y)

(in the latter case * (q0.y * q1.y) is added to account for the fact that we've multiplied the original expression, q0.x/q0.y < q1.x/q1.y by q0.y*q1.y, and if it's negative, < will change to >). You can also get rid of some divisions that way.

Anton Knyazyev
  • 464
  • 4
  • 7
-1

I don't know about guarantees, but compiling this

int main() {
    int i = 0;
    float
        x = 1E20,
        y = 3E20,
        f = 10;
    while ( ++i <=20 ) {
        printf(" %d) %f = %f / %f\n", i, x / y, x, y );
        x *= f;
        y *= f;
    }
}

with gcc -O0 (on Debian GNU/Linux on an Intel(R) Xeon(R) CPU E3-1246) produces

 1) 0.333333 = 1.000000 / 3.000000
 2) 0.333333 = 1000.000000 / 3000.000000
 3) 0.333333 = 1000000.000000 / 3000000.000000
 4) 0.333333 = 1000000000.000000 / 3000000000.000000
 5) 0.333333 = 999999995904.000000 / 3000000053248.000000
 6) 0.333333 = 999999986991104.000000 / 3000000028082176.000000
 7) 0.333333 = 999999984306749440.000000 / 3000000159078678528.000000
 8) 0.333333 = 999999949672133165056.000000 / 3000000271228864561152.000000
 9) 0.333333 = 999999941790833817157632.000000 / 3000000329775659716968448.000000
 10) 0.333333 = 999999914697178458896728064.000000 / 3000000186813393145719422976.000000
 11) 0.333333 = 999999939489602493962365435904.000000 / 3000000196258126111458713403392.000000
 12) 0.333333 = 999999917124474831091725703839744.000000 / 3000000060858434314620245836300288.000000
 13) 0.333333 = 999999882462153731101078006664265728.000000 / 3000000043527273764624921987712548864.000000
 14) -nan = inf / inf
Kenney
  • 9,003
  • 15
  • 21