Re: [Pythonideas] What math.floor(inf) should return? Was: Make `float('inf') //1 == float('inf')`
On Thu, Sep 18, 2014 at 6:57 PM, Case Van Horsen
I don't think there is any dispute over what math.floor(inf) should
return.
POSIX, C99, IEEE and probably many other standards agree that inf should be returned as long as the resulting type can represent it.
I dispute that there is no dispute over what math.floor(inf) should return. ;)
I am changing the subject so that we don't mix making new decisions with a critique and defense of the decisions that were made in the past. I wrote: "inf should be returned as long as the resulting type can represent it". This is the part that I still believe is not disputed. No one has suggested that math.floor(inf) should return nan, for example.
All the standards specify a result type can represent +Inf and +0. A standards compliant version should return +Inf and +0. lrint() and llrint() are defined to return long or long long, respectively. It would be fine if they raised an exception. The current math.floor() actually behaves more like llrint() than floor().
POSIX does not preclude raising an exception: "If the correct value would cause overflow, a range error shall occur" ( http://pubs.opengroup.org/onlinepubs/009695399/functions/floor.html).
I accept that having math.floor() return an integer (and raise an
exception
for +Inf) may be useful in many cases but it is different from the standard. Other floatingpoint libraries still return a floatingpoint value.
The standards are influenced by the limitation inherent in many languages where ints have finite range and cannot represent floor() of many finite floating point values. Python does not have this limitation. (Granted  PEP 3141 could do a better job explaining why floor, ceil, round, //, etc. should return Integer rather than Real.)
On Thu, Sep 18, 2014 at 7:16 PM, Alexander Belopolsky
On Thu, Sep 18, 2014 at 6:57 PM, Case Van Horsen
wrote: I don't think there is any dispute over what math.floor(inf) should return. POSIX, C99, IEEE and probably many other standards agree that inf should be returned as long as the resulting type can represent it.
I dispute that there is no dispute over what math.floor(inf) should return. ;)
I am changing the subject so that we don't mix making new decisions with a critique and defense of the decisions that were made in the past.
I wrote: "inf should be returned as long as the resulting type can represent it". This is the part that I still believe is not disputed. No one has suggested that math.floor(inf) should return nan, for example.
All the standards specify a result type can represent +Inf and +0. A standards compliant version should return +Inf and +0. lrint() and llrint() are defined to return long or long long, respectively. It would be fine if they raised an exception. The current math.floor() actually behaves more like llrint() than floor().
POSIX does not preclude raising an exception: "If the correct value would cause overflow, a range error shall occur" (http://pubs.opengroup.org/onlinepubs/009695399/functions/floor.html).
Under the section RETURN VALUE, it also states: If x = +0 or +Inf, then x shall be returned. And under APPLICATION USAGE, it states: The floor() function can only overflow when the floatingpoint representation has DBL_MANT_DIG > DBL_MAX_EXP.
I accept that having math.floor() return an integer (and raise an exception for +Inf) may be useful in many cases but it is different from the standard. Other floatingpoint libraries still return a floatingpoint value.
The standards are influenced by the limitation inherent in many languages where ints have finite range and cannot represent floor() of many finite floating point values. Python does not have this limitation. (Granted  PEP 3141 could do a better job explaining why floor, ceil, round, //, etc. should return Integer rather than Real.)
For ceil, trunc, and floor, I can't think of any situation where returning an integer is more "precise" than returning a float. Assuming standard IEEE 64bit format, only the first 53 bits will be significant. The remaining bits will just be 0. (If there is a counterexample, please let me know. I know enough about floatingpoint to know I don't very much about floatingpoint. ;) ) I appreciate that returning an Integral value can make using the result of floor(), etc., easier by eliminating the need for int(floor()) but I think it adds a false sense of precision and changes the behavior of math.floor() for +0 and +Inf. Even though I don't think it is worth changing, I can think of two options. 1) Add trunc, ceil, floor as reserved works (just like round) and let them call the methods defined by PEP3141. Then math.floor() can revert back to returning a float. 2) Add an alternate math library, say stdmath, (or ieeemath or...) that can follow a different set of rules than the math module. In addition to the functions provided by the math module, it could define additional functions such as stdmath.div such that stdmath.div(2.0, 0) return Inf instead of raising an exception.
On Thu, Sep 18, 2014 at 11:16 PM, Case Van Horsen
2) Add an alternate math library, say stdmath, (or ieeemath or...)
.. or numpy :)
that can follow a different set of rules than the math module. In addition to the functions provided by the math module, it could define additional functions such as stdmath.div such that stdmath.div(2.0, 0) return Inf instead of raising an exception.
Alexander Belopolsky
I accept that having math.floor() return an integer (and raise an exception for +Inf) may be useful in many cases but it is different from the standard. Other floatingpoint libraries still return a floatingpoint value.
The standards are influenced by the limitation inherent in many languages where ints have finite range and cannot represent floor() of many finite floating point values. Python does not have this limitation. (Granted  PEP 3141 could do a better job explaining why floor, ceil, round, //, etc. should return Integer rather than Real.)
Scheme (which IIRC infuenced PEP3141) has arbitrary precision ints, but guile at least returns floats: scheme@(guileuser)> (floor 2e308) $3 = +inf.0 [I'm mentioning Decimal now since the standard is *very* close to IEEE 7542008.] Decimal's divide_int function returns "integers" (Decimals with exponent 0), but only if they don't overflow the context precision: c = getcontext()
c.prec 28 c.divide_int(Decimal("333e25"), 1) Decimal('3330000000000000000000000000')
c.divide_int(Decimal("333e250"), 1) decimal.InvalidOperation: quotient too large in //, % or divmod
Despite this fact, Decimal still returns inf in the disputed case:
c.divide_int(Decimal("inf"), 1) Decimal('Infinity')
Also, even when the Overflow trap is set, Decimal only raises Overflow when an operation actually overflows:
c.traps[Overflow] = True Decimal("1e999999999") * 10 decimal.Overflow: above Emax
Decimal("inf") * 10 Decimal('Infinity')
c.divide_int(Decimal("inf"), 1) Decimal('Infinity')
Stefan Krah
On Fri, Sep 19, 2014 at 3:16 AM, Alexander Belopolsky < alexander.belopolsky@gmail.com> wrote:
The standards are influenced by the limitation inherent in many languages where ints have finite range and cannot represent floor() of many finite floating point values. Python does not have this limitation. (Granted  PEP 3141 could do a better job explaining why floor, ceil, round, //, etc. should return Integer rather than Real.)
Indeed. FWIW, I think it was a mistake to change the return type of math.floor and math.ceil in Python 3. There's no longer any way to spell the simple, fast, float>float floor operation. It would be nice to have those basic floatingpoint math operations accessible again.  Mark
On Fri, Sep 19, 2014 at 5:44 AM, Mark Dickinson
On Fri, Sep 19, 2014 at 3:16 AM, Alexander Belopolsky
wrote: The standards are influenced by the limitation inherent in many languages where ints have finite range and cannot represent floor() of many finite floating point values. Python does not have this limitation. (Granted  PEP 3141 could do a better job explaining why floor, ceil, round, //, etc. should return Integer rather than Real.)
Indeed. FWIW, I think it was a mistake to change the return type of math.floor and math.ceil in Python 3. There's no longer any way to spell the simple, fast, float>float floor operation. It would be nice to have those basic floatingpoint math operations accessible again.
It looks like the Scheme standards committee agree with you. PEP3141 references: http://groups.csail.mit.edu/mac/ftpdir/schemereports/r5rshtml/r5rs_8.html#... There is a later version that specifically discusses the behavior for +Inf and Nan. http://www.r6rs.org/final/html/r6rs/r6rsZH14.html#node_sec_11.7.2 Here is the pertinent section: Although infinities and NaNs are not integer objects, these procedures return an infinity when given an infinity as an argument, and a NaN when given a NaN. (floor 4.3) ⇒ 5.0 (ceiling 4.3) ⇒ 4.0 (truncate 4.3) ⇒ 4.0 (round 4.3) ⇒ 4.0 (floor 3.5) ⇒ 3.0 (ceiling 3.5) ⇒ 4.0 (truncate 3.5) ⇒ 3.0 (round 3.5) ⇒ 4.0 (round 7/2) ⇒ 4 (round 7) ⇒ 7 (floor +inf.0) ⇒ +inf.0 (ceiling inf.0) ⇒ inf.0 (round +nan.0) ⇒ +nan.0 Also note that a floating point representation is used for the results when the argument is floating point number. Scheme does not use the internal representation (IEEE754 or long int or....) to determine if a number is an integer or real but rather its value. Here is an example from a Scheme session: (integer? 2) ;Value: #t (integer? 2.0) ;Value: #t (integer? 2.1) ;Value: #f (#t and #f are equivalent to True and False.) I think PEP3141 incorrectly interpreted the meaning of "integer" to imply the use of an "integer representation" while I think the Scheme standard implies an "integer value"; i.e. x is an "integer" iff xround(x) == 0.
 Mark
participants (4)

Alexander Belopolsky

Case Van Horsen

Mark Dickinson

Stefan Krah