Issue #4576 has been updated by Vit Ondruch.


Shyouhei Urabe wrote:
> Vit Ondruch wrote:
> > Please first see the commit [1] and then tell me why the original test case should fail?
> 
> Because no one guarantees that it should pass.

It is a feature I guess, there is even test case for this unfortunately, so why should something pass on one platform and should not pass on another? That doesn't make sense.

> > Actually it fails on i386 and succeeds on x86_64 which is a bit suspicious.
> 
> It is a clear sign that you are "dancing with floats".
> 
> As Tomoyoki Chikanaga says in note #note-2 of this issue I believe this is a "learn floating point number" kind of thig.

No, it is not ... the main difference is if the float comparison is done in memory or in registers. Each have different precision and it popups on i386.

Here is long explanation copied from RH bugzilla:

Jaroslav karvada 2011-08-29 00:15:22 CEST

AFAIK by default on 32 bit machines GCC uses FPU instructions for better
compatibility with older machines, while on 64 bit machines it uses SSE
instructions for better performance. 

The SSE offers more registers and consistency - the value always retain 64 bit,
while FPU offers better precision - it uses 80 bit intermediate values when
possible. And that's the source of inconsistency.

The core from your stripped down reproducer:

...
  double beg = 1.0;
  double unit = 1.2;
  double end = 9.4;

  printf("%d\n", 7 * unit + beg < end);
...

On 32 bit it returns 1, while on 64 bit it returns 0. Why? Analysis:

FPU instructions (on 32 bit):
7 * 1.199999999999999956 = 8.399999999999999689
8.399999999999999689 + 1 = 9.399999999999999689
9.399999999999999689 < 9.400000000000000355

SSE instructions (on 64 bit):
7 * 1.2 = 8.4000000000000004
8.4000000000000004 + 1 =  9.4000000000000004
9.4000000000000004 == 9.4000000000000004

Please note that the intermediate number 9.400000000000000355 can be rounded to
9.4000000000000004, but the comparison is done on FPU stack with the full
precision. And that's the problem.

You can force usage of the FPU on 64 bit, by compiling with -mfpmath=387 and
then the results will be the same on both arches. But please note the floating
points are tricky and it shouldn't be relied on internal rounding as in the
reproducer above.
----------------------------------------
Bug #4576: Range#step miss the last value, if end-exclusive and has float number
http://redmine.ruby-lang.org/issues/4576

Author: Joey Zhou
Status: Closed
Priority: Normal
Assignee: 
Category: 
Target version: 
ruby -v: -


=begin
Hi, I find that:

* if: range.exclude_end? == true
* and: any one in [begin_obj, end_obj, step] is a true Float(f.to_i != f)
* and: unless begin_obj + step*int == end_obj
* then: the result will miss the last value.

for example:

 p (1...6.3).step.to_a # => [1.0, 2.0, 3.0, 4.0, 5.0], no 6.0
 p (1.1...6).step.to_a # => [1.1, 2.1, 3.1, 4.1], no 5.1
 p (1...6).step(1.1).to_a # => [1.0, 2.1, 3.2, 4.300000000000001], no 5.4

 p (1.0...6.6).step(1.9).to_a # => [1.0, 2.9], no 4.8
 p (1.0...6.7).step(1.9).to_a # => [1.0, 2.9, 4.8]
 p (1.0...6.8).step(1.9).to_a # => [1.0, 2.9, 4.8], no 6.7

Maybe the #step is ok on integers, but there's something wrong if the range is end-exclusive and contain float numbers.
=end



-- 
http://redmine.ruby-lang.org