>>> I don't think you can really ask for anything better.
>>
>> The above def suggests I can. I'm not saying it's a plug-in
>> replacement for %, but it shows that a better answer can be obtained
>> for the case in question.
>
> But what is better? I think we disagree on that.
When there are two methods for performing a computation, the better
one is the one that produces the more accurate result. When floats
are involved that means the method which produces the result closer
to what one would get with the real numbers they are meant to imitate.
OK, I agree with you. But you can only reasonably do that in every
step of the calculation. First, you need to represent 0.1 as a
floating point number and it ends up slightly larger. Then, you do the
% operation, but because the number turned out slightly larger
previously, it no longer fits 10 times and 1.0 % 0.1 returns something
slightly smaller than 0.1. The only way according to your rule above
that it can return 0.0 as you want it to is if the history of the
calculation is taken into account, but that is practically infeasible
for a general purpose language. You get errors along each step of the
calculation, and 1.0 % 0.1 is really two steps: approximate 0.1 by the
closest floating point number (done at parse time), and that is
0.10000000000000000555, and then you do 1.0 % 0.10000000000000000555
which is 1.0 - 9 * 0.10000000000000000555 = 0.09999999999999995004
because 1.0 - 10 * 0.10000000000000000555 (in real arithmetic) is
-5.55e-17 and negative. What Ruby does is as close to real number
arithmetic as you can get *in every step of the calculation*. The
whole calculation is the programmer's responsibility.
>>> In Ruby 0.1
>>> really is 0.10000000000000000555 as that is the floating point
>>> number closest to 0.1 ...
>>
>> That's true ...
>>
>>> ... and it doesn't fit 10 whole times in 1.0. This has nothing to do
>>> with the algorithm used.
>>
>> ... but Ruby appears to think otherwise
>>
>> sprintf("%.25f", 1.0/0.1) # => "10.0000000000000000000000000"
>>
>> causing me to conclude something is amiss in the % operator
>> algorithm.
>
> But that is a different question.
It's the question I'm focused on, and the question I believe the OP
was bringing up.
> It seems to me that you are looking for a way to make % and / obey a
> mathematical equation that is true for reals but that simply isn't for
> floats. You can change the definitions of % and / slightly to obey the
> rule you want it to obey (although I'm not entirely sure that your
> "fix" works in general,) but you can't change it to satisfy all
> mathematical equations that are true for reals, and any change
> probably makes other calculations more imprecise.
I'm only talking about % here. I don't think / is an issue. I think
issue revolves around the discontinuity of % and the inability of a
binary floating-point number system to represent numbers such as 1/10
exactly.
With all due respect, but when I explained why 1.0 % 0.1 turns out
different from 0.0, you had to admit I was correct but your counter
argument was that 1.0 / 0.1 is *exactly* 10.0 in Ruby.
> I can understand your feeling though. You expect 1.0 % 0.1 to be 0.0
> and you get 0.1, which is a big difference, a lot bigger than the one
> between 0.1 and 0.10000000000000000555. This is a consequence of the
> fact that % is a discontinuous function.
The discontinuity of % is certainly at the root of the problem, but ...
> Any discontinuous function is
> dangerous in floating point arithmetic, but only when used near its
> discontinuities. The only general solution to this is either not to
> use discontinuous functions near their discontinuities, or take into
> account the large imprecision. This is really a well-understood
> problem. It's not the algorithm used that is the problem, it's the
> question asked that is the problem.
...this is where we differ. I believe the % algorithm should take the
points of discontinuity into account and tread very carefully in
their neighborhoods.
OK, but it does. Ruby is right in saying that 1.0 % 0.1 is 0.1, with
the first 0.1 being slightly larger than the real 0.1, and the second
one being slightly smaller. And the fact that you can find an
algorithm that gives 0.0 as you expect doesn't make that the correct
result. I can give you an algorithm that calculates 1.0 but ends up
with 0.0 instead, and using only + and -, but that doesn't make 1.0
and 0.0 equal.
The question you ask Ruby is what 1.0 % 0.1 is. That's near a
discontinuity in %. You know that there is an error in representing
0.1, and it can go either side of the discontinuity. As it turns out,
it goes on the wrong side of the discontinuity as far as you are
concerned, but from the point of view of Ruby, that side has the
closest floating point number.
Can you accept that Ruby does % as an atomic operation as well as
possible? Your algorithm uses 4 atomic operations (Float#div is
apparently not an atomic operation, even though I found this in the
change log: "div" should return x.divmod(x)[0]) and the errors happen
to cancel each other out for the cases you've tested. That's good, and
if that's the behavior you want in your algorithms, then you should
use it, but for the sake of doing every atomic calculation as well as
possible, it's bad.
Peter
···
On 01/09/07, Morton Goldberg <m_goldberg@ameritech.net> wrote:
On Aug 31, 2007, at 8:57 PM, Calamitas wrote:
> On 31/08/2007, Morton Goldberg <m_goldberg@ameritech.net> wrote:
>> On Aug 30, 2007, at 5:56 PM, Calamitas wrote: