Integer division with / - request explanation of behavior

Today I discovered the difference in the meaning of the / (arithmetic
divide) method depending on whether it’s caller is an integer or a
float.

I discovered that I had to cast the calling number explicitly to a float
and it seemed totally non-intuitive to me.

A couple of points:

  1. I just discovered that this behavior exists in Java and C as well.
    Which, frankly, I didn’t even realize. Sigh.

  2. I’ve read some other posts and people have mentioned the notion of
    type purity with respect to this. I don’t see why that’s a problem.
    When one divides two integers, one isn’t asking for one of the integer
    arguments to be cast to a float, they’re asking for the result of
    dividing two integers. Since when is it not allowable for a method to
    take arguments of a given type and return a result of a different type?

A couple of questions:

  1. I see that there is a divmod operator in Ruby that returns the
    quotient and remainder of a division. Why is the overridden / operator
    necessary when there is already a way to get int1 DIV int2 by using
    int1.divmod(int2)[0]?

  2. Is the reason for the behavior of “/” is that this is the behavior of
    “/” in C? Why is it that way in C?

Thanks,
Wes

Wes G. wrote:

Today I discovered the difference in the meaning of the / (arithmetic
divide) method depending on whether it’s caller is an integer or a
float.

Not exactly. The result is expressed in the type of the more precise of
the
two operands.

I discovered that I had to cast the calling number explicitly to a float
and it seemed totally non-intuitive to me.

Well, that depends on what you want as a result. You aren’t required to
cast
one of the operands as a float. Maybe you want the default result for
the
provided values.

I often break a time value into hours, minutes and seconds. I do this by
successive application of integer modulo and divide. If the division
operator were to promote my values, the algorithm would fail.

A couple of points:

  1. I just discovered that this behavior exists in Java and C as well.
    Which, frankly, I didn’t even realize. Sigh.

There are excellent reasons for this behavior. When you divide two
numbers,
the result should have the precision of the more precise type of either
of
the two operands.

  1. I’ve read some other posts and people have mentioned the notion of
    type purity with respect to this. I don’t see why that’s a problem.

It’s not about type purity. That isn’t supported by the fact that the
less
precise type is discarded in favor of the more precise type.

When one divides two integers, one isn’t asking for one of the integer
arguments to be cast to a float, they’re asking for the result of
dividing two integers. Since when is it not allowable for a method to
take arguments of a given type and return a result of a different type?

It is not a question of allowed, it is a question of what one would
expect
without explicit adjustment.

A couple of questions:

  1. I see that there is a divmod operator in Ruby that returns the
    quotient and remainder of a division. Why is the overridden / operator
    necessary when there is already a way to get int1 DIV int2 by using
    int1.divmod(int2)[0]?

Because people expect to be able to use a division operator for
division.

  1. Is the reason for the behavior of “/” is that this is the behavior of
    “/” in C? Why is it that way in C?

This behavior is common to all modern languages. It exists because the
languages that support it are designed to act on a set of common-sense
assumptions.

BTW, the behavior goes beyond integers and floats. It also applies to
extended-precision numerical types.

x = BigDecimal.new(“3.0”,100)

puts 1/x

0.3333333333333333333333333333333333333333333333333333333333333333333
33333333333333333333333333333333333333333333333333333E0

One of the operands is an integer, but the result is cast to the
precision
of the more precise type. So much for type purity.

On 9/30/06, Paul L. [email protected] wrote:

Wes G. wrote:

I frankly think there are two reasons for it
(1) Matz thaught it is more useful that way
and/or
(2) Matz thaught it is POLS
these are good reasons of course ;).
I also believe that most users are quite happy with it, I am BTW ;),
there
are of course applications to be written in Ruby
where the behavior the other way round would be more useful…
… happy monkeypatching!

Cheers
Robert


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

Hello !

  1. Is the reason for the behavior of “/” is that this is the behavior of
    “/” in C? Why is it that way in C?

Here’s a couple of reasons:

  • C is mapping this operation directly to assembly. Old x86 processors
    used to be able to manipulate only integers, so the result of the
    division of one integer by another had to be an integer;

  • the architecture for processing floats is way more complex and less
    portable than the one from integers (integer arithmetic is the same on
    every single processor in the world; float arithmetic is a nightmare);

  • floats are much slower to manipulate, even in assembly code;

  • in ruby, integers are hard-coded as immediate values, whereas floats
    are embedded into a complex data structure. You wouldn’t want to invoke
    a really heavy machinery for an integer division…

Does that sound correct to you ?

Cheers,

Vince

Paul L. wrote:

  1. Is the reason for the behavior of “/” is that this is the behavior of
    “/” in C? Why is it that way in C?

This behavior is common to all modern languages. It exists because the
languages that support it are designed to act on a set of common-sense
assumptions.

BTW, the behavior goes beyond integers and floats. It also applies to
extended-precision numerical types.

And as far as I know it’s even an IEEE standard. Programming systems
better comply with it in order to make results comparable and
interchangeable.

Kind regards

robert

On 2006.09.30 18:25, Paul L. wrote:

Wes G. wrote:

Today I discovered the difference in the meaning of the / (arithmetic
divide) method depending on whether it’s caller is an integer or a
float.

Not exactly. The result is expressed in the type of the more precise of the
two operands.

Actually, integers are more precise than IEEE floating points
since 2.2 - 1.2 != 1.0 :slight_smile: s/precise/accurate !

Eero S. wrote:

Actually, integers are more precise than IEEE floating points
since 2.2 - 1.2 != 1.0 :slight_smile: s/precise/accurate !

Yes, I used the wrong term. I should have said “longer” rather than more
precise. Even that is less true than it used to be, as what is generally
accepted to be an int changes over time.

Float and double math is at least reliable in its unreliability. :slight_smile:

Robert K. wrote:

/ …

BTW, the behavior goes beyond integers and floats. It also applies to
extended-precision numerical types.

And as far as I know it’s even an IEEE standard.

I think that’s true also, but certainly type promotion is what people
expect
to happen.

Programming systems
better comply with it in order to make results comparable and
interchangeable.

There’s a can of worms. It turns out the behavior of the different
languages
WRT conversion from and to base ten leads to some borderline failures if
the textual forms are transferred between environments.

As it turns out Perl is one of the worst as far as being able to
recreate
its own internal binary representation based on its self-generated
textual
representation. I became comfortable (perhaps too comfortable) with
being
able to convert doubles back and forth between string and binary form,
reliably and repeatably, in most languages, but I couldn’t make this
happen
in Perl (version 4.0 IIRC).

Without realizing it, I had come to assume that a language’s base ten
textual representation for a double or a float would be readable by that
same language and when reconverted (base ten -> base two) produce an
exact
copy of the original binary form, and this is true in most languages.
Then
a Perl programmer challenged me on this, saying the base ten
representation
almost never recreated the original binary number, and I tried to show
that
wasn’t true. I wrote test suites in Java, C and C++ (the latter not
using
printf and scanf) and Perl, and the results were perfect in every
language
except Perl.

At this point, that observation may be more history than fact, because
of
the increasing importance of data transfers between environments.

Wes G. wrote:

Today I discovered the difference in the meaning of the / (arithmetic
divide) method depending on whether it’s caller is an integer or a
float.

I discovered that I had to cast the calling number explicitly to a float
and it seemed totally non-intuitive to me.

The mathn namespace-clobbering module makes maths work like expected.

The magic lies in aliasing Fixnum#/ to Fixnum#quo (same for Bignum).

You could use that method explicitly, or just clobber the classes if
you’re sure nothing else expects the C meaning to work.

I frankly think there are two reasons for it
(1) Matz thaught it is more useful that way
and/or
(2) Matz thaught it is POLS
these are good reasons of course ;).

POLS?

Wes G. wrote:

I frankly think there are two reasons for it
(1) Matz thaught it is more useful that way
and/or
(2) Matz thaught it is POLS
these are good reasons of course ;).

POLS?

Principle Of Least Surprise.

Paul L. wrote:

At this point, that observation may be more history than fact, because of
the increasing importance of data transfers between environments.

Long ago in a galaxy far away, there were many more choices for transfer
of data from one machine to another. And someone coined the rule “The
receiver makes it right”.

Paul L. wrote:

Robert K. wrote:

/ …

BTW, the behavior goes beyond integers and floats. It also applies to
extended-precision numerical types.
And as far as I know it’s even an IEEE standard.

I think that’s true also, but certainly type promotion is what people expect
to happen.

In this case I lean on the standard’s side: numerics are just too basic
and fundamental to let implementers of languages do what they like. I
know a case where a legacy system doing static calculus was replaced by
a new system on a totally different OS platform. Data was migrated and
afterwards people complained that the new system was doing the match
wrong. It turned out that it was the old system that actually had the
math wrong. They were lucky that in years no building crashed because
of this…

Programming systems
better comply with it in order to make results comparable and
interchangeable.

There’s a can of worms. It turns out the behavior of the different languages
WRT conversion from and to base ten leads to some borderline failures if
the textual forms are transferred between environments.

I am sure, conversion is yet another standard. :slight_smile: Or maybe not:

copy of the original binary form, and this is true in most languages. Then
a Perl programmer challenged me on this, saying the base ten representation
almost never recreated the original binary number, and I tried to show that
wasn’t true. I wrote test suites in Java, C and C++ (the latter not using
printf and scanf) and Perl, and the results were perfect in every language
except Perl.

One more reason to not use Perl. :slight_smile: The question is whether one should
rely on the binary <-> text conversion. The best for decimal accuracy
is probably BCD anyway…

Kind regards

robert

On 9/30/06, Wes G. [email protected] wrote:

Posted via http://www.ruby-forum.com/.

Sorry, sorry, I wanted to seem intelligent and educated and informed so I
used an abbriviation,
no honestly it is kind of a standard in this ML, but I should have taken
the
time to spell it out of course.

Robert


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

On 9/30/06, M. Edward (Ed) Borasky [email protected] wrote:

Long ago in a galaxy far away, there were many more choices for transfer
of data from one machine to another. And someone coined the rule “The
receiver makes it right”.

I kinna start to love that guy :wink:


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

On 9/30/06, Paul L. [email protected] wrote:

math wrong. They were lucky that in years no building crashed because
produces, no more, no less, for a problem that produces an irrational
result. This is obviously for the benefit of the nonspecialist in the
audience, who would have been less impressed if the savant had offered
more digits than the calculator – those extra digits couldn’t possibly be
real, could they, otherwise the calculator would have provided them.

Would you mind if I suggest this Ruby Q.

compute all didgits of Pi, if you have less time, just compute the
last
digit of Pi.

Robert

/ …

/ …
that they can be reliably converted and compared to each other.


Paul L.
http://www.arachnoid.com


Deux choses sont infinies : l’univers et la bêtise humaine ; en ce qui
concerne l’univers, je n’en ai pas acquis la certitude absolue.

  • Albert Einstein

Robert K. wrote:

/ …

I
know a case where a legacy system doing static calculus was replaced by
a new system on a totally different OS platform. Data was migrated and
afterwards people complained that the new system was doing the match
wrong. It turned out that it was the old system that actually had the
math wrong. They were lucky that in years no building crashed because
of this…

That’s a great story. I hope its details are written up somewhere. You
may
be aware there is a lot of academic interest in examples where erroneous
computer results come to be accepted, even defended as accurate.

I am reminded of a scene in “Rain Man” in which the idiot savant played
by
Dustin Hoffman is asked to provide a difficult mathematical result,
while
the questioner simultaneously computes it on a calculator. The savant
replies with the exact number of decimal places that the calculator
produces, no more, no less, for a problem that produces an irrational
result. This is obviously for the benefit of the nonspecialist in the
audience, who would have been less impressed if the savant had offered
more digits than the calculator – those extra digits couldn’t possibly
be
real, could they, otherwise the calculator would have provided them.

/ …

There’s a can of worms. It turns out the behavior of the different
languages WRT conversion from and to base ten leads to some borderline
failures if the textual forms are transferred between environments.

I am sure, conversion is yet another standard. :slight_smile: Or maybe not:

I think this is part of a standard, and it’s usually followed to the
letter.

/ …

The question is whether one should
rely on the binary <-> text conversion. The best for decimal accuracy
is probably BCD anyway…

Yes. My point is that this business of converting between binary and
text/base-ten forms is generally very reliable, both within and between
environments, Perl apparently excepted.

Not to say that the numbers themselves represent precise quantities, but
that they can be reliably converted and compared to each other.

Robert D. wrote:

Would you mind if I suggest this Ruby Q.

compute all didgits of Pi, if you have less time, just compute the
last
digit of Pi.

Easy as pi … just use the Ruby language “call with continued fraction”
feature.

On 06-09-30, at 11:11, Eero S. wrote:

two operands.

Actually, integers are more precise than IEEE floating points
since 2.2 - 1.2 != 1.0 :slight_smile: s/precise/accurate !

Not on all platforms. For instance, in Ocaml, it’s true:

2. -. 1. = 1.;;

  • : bool = true

Jeremy T. wrote:

Actually, integers are more precise than IEEE floating points
since 2.2 - 1.2 != 1.0 :slight_smile: s/precise/accurate !

Not on all platforms. For instance, in Ocaml, it’s true:

2. -. 1. = 1.;;

  • : bool = true

Your example works because integers are rational IEEE floating point
numbers. Problems arise when you use a number that’s a rational decimal
number, but an irrational IEEE binary floating point one. Those aren’t
precise no matter what storage size you use.

2.2 -. 1.2;;

  • : float = 1.0000000000000002

(2.2 -. 1.2) != 1.0;;

  • : bool = true

For this example, Ruby’s rounding earlier loses the imprecision.

Ripping off a tutorial article on IEEE floats[1] produces an example
that manifests in Ruby (also in OCaml):

irb(main):001:0> a = 0.0
=> 0.0
irb(main):002:0> 10000.times {
irb(main):003:1* a += 0.0001
irb(main):004:1> }
=> 10000
irb(main):005:0> a
=> 0.999999999999906
irb(main):006:0>

David V.

[1] Microsoft Support