From: Vit Ondruch Date: 2011-09-13T21:04:18+09:00 Subject: [ruby-core:39517] [Ruby 1.9 - Bug #4576] Range#step miss the last value, if end-exclusive and has float number 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