Float divmod python ошибка

Задача: Напишите простой калькулятор, который считывает с пользовательского ввода три строки: первое число, второе число и операцию, после чего применяет операцию к введённым числам («первое число» «операция» «второе число») и выводит результат на экран.

Поддерживаемые операции: +, -, /, *, mod, pow, div, где
mod — это взятие остатка от деления,
pow — возведение в степень,
div — целочисленное деление.

Проблема: При операциях с нулем возвращает:
Traceback (most recent call last):
File «jailed_code», line 9, in
print(n1 % n2)
ZeroDivisionError: float modulo

Мой код:

n1 = float(input())
n2 = float(input()
o = str(input())

if o == 'mod':
    if (n1 or n2) == 0.0:
        print("Деление на 0!")
    else:
        print(n1%n2)
elif o == 'div':
    if (n1 or n2) == 0.0:
        print("Деление на 0!")
    else:
        print(n1//n2)  
elif o == '/':
    if (n1 or n2) == 0.0:
        print("Деление на 0!")
    else:
        print(n1/n2)        
elif o == '*':
    print(n1 * n2)
elif o == '+':
    print(n1 + n2)
elif o == '-':
    print(n1 - n2)
elif o == 'pow':
    print(n1 ** n2)
else:
    print('Something was wrong')

Задача: Напишите простой калькулятор, который считывает с пользовательского ввода три строки: первое число, второе число и операцию, после чего применяет операцию к введённым числам («первое число» «операция» «второе число») и выводит результат на экран.

Поддерживаемые операции: +, -, /, *, mod, pow, div, где
mod — это взятие остатка от деления,
pow — возведение в степень,
div — целочисленное деление.

Проблема: При операциях с нулем возвращает:
Traceback (most recent call last):
File «jailed_code», line 9, in
print(n1 % n2)
ZeroDivisionError: float modulo

Мой код:

n1 = float(input())
n2 = float(input()
o = str(input())

if o == 'mod':
    if (n1 or n2) == 0.0:
        print("Деление на 0!")
    else:
        print(n1%n2)
elif o == 'div':
    if (n1 or n2) == 0.0:
        print("Деление на 0!")
    else:
        print(n1//n2)  
elif o == '/':
    if (n1 or n2) == 0.0:
        print("Деление на 0!")
    else:
        print(n1/n2)        
elif o == '*':
    print(n1 * n2)
elif o == '+':
    print(n1 + n2)
elif o == '-':
    print(n1 - n2)
elif o == 'pow':
    print(n1 ** n2)
else:
    print('Something was wrong')

The divmod() method in python takes two numbers and returns a pair of numbers consisting of their quotient and remainder. 

Syntax : 

divmod(x, y)
x and y : x is numerator and y is denominator
x and y must be non complex

Examples:  

Input : x = 9, y = 3
Output :(3, 0)

Input : x = 8, y = 3
Output :(2, 2)

Explanation: The divmod() method takes two parameters x and y, where x is treated as numerator and y is treated as the denominator. The method calculates both x // y and x % y and returns both the values. 

  • If x and y are integers, the return value is
(x // y, x % y)
  • If x or y is a float, the result is
(q, x % y), where q is the whole part of the quotient.

Python3

print('(5, 4) = ', divmod(5, 4))

print('(10, 16) = ', divmod(10, 16))

print('(11, 11) = ', divmod(11, 11))

print('(15, 13) = ', divmod(15, 13))

print('(8.0, 3) = ', divmod(8.0, 3))

print('(3, 8.0) = ', divmod(3, 8.0))

print('(7.5, 2.5) = ', divmod(7.5, 2.5))

print('(2.6, 10.7) = ', divmod(2.6, 0.5))

Output: 

(5, 4) =  (1, 1)
(10, 16) =  (0, 10)
(11, 11) =  (1, 0)
(15, 13) =  (1, 2)
(6.0, 5) =  (2.0, 2.0)
(3, 9.0) =  (0.0, 3.0)
(13.5, 6.2) =  (3.0, 0.0)
(1.6, 10.7) =  (5.0, 0.10000000000000009)

Errors And Exceptions 

  1. If either of the arguments (say x and y), is a float, the result is (q, x%y). Here, q is the whole part of the quotient.
  2. If the second argument is 0, it returns Zero Division Error
  3. If the first argument is 0, it returns (0, 0)

Practical Application: Check if a number is prime or not using divmod() function.  

Examples: 

Input : n = 7
Output :Prime

Input : n = 15
Output :Not Prime

Algorithm 

  1. Initialise a new variable, say x with the given integer and a variable counter to 0
  2. Run a loop till the given integer becomes 0 and keep decrementing it.
  3. Save the value returned by divmod(n, x) in two variables, say p and q
  4. Check if q is 0, this will imply that n is perfectly divisible by x, and hence increment the counter value
  5. Check if the counter value is greater than 2, if yes, the number is not prime, else it is prime

PYTHON3

n = 15

x = n

count = 0

while x != 0:

    p, q = divmod(n, x)

    x -= 1

    if q == 0:

        count += 1

if count > 2:

    print('Not Prime')

else:

    print('Prime')

Output: 

Not Prime

More Applications: 

Example 1: 

Python3

num = 86

sums = 0

while num != 0:

    use = divmod(num, 10)

    dig = use[1]

    sums = sums + dig

    num = use[0]

print(sums)

Output: 

14

Example 2:

Python3

num = 132

pal = 0

while num != 0:

    use = divmod(num, 10)

    dig = use[1]

    pal = pal*10+dig

    num = use[0]

print(pal)

Output: 

231 

Syntax

object.__divmod__(self, other)

The Python __divmod__() method implements the built-in divmod operation. So, when you call divmod(a, b), Python attempts to call x.__divmod__(y). If the method is not implemented, Python first attempts to call __rdivmod__ on the right operand and if this isn’t implemented either, it raises a TypeError.

We call this a “Dunder Method” for Double Underscore Method” (also called “magic method”). To get a list of all dunder methods with explanation, check out our dunder cheat sheet article on this blog.

Background Default divmod()

Python’s built-in divmod(a, b) function takes two integer or float numbers a and b as input arguments and returns a tuple (a // b, a % b). The first tuple value is the result of the integer division a//b. The second tuple is the result of the remainder, also called modulo operation a % b. In case of float inputs, divmod() still returns the division without remainder by rounding down to the next round number.

To understand this operation in detail, feel free to read over our tutorial or watch the following video:

Python divmod() — A Simple Guide

Example Custom divmod()

In the following example, you create a custom class Data and overwrite the __divmod__() method so that it returns a dummy string when trying to calculate the modulo of two numbers.

class Data:
        
    def __divmod__(self, other):
        return '... my result of divmod...'


a = Data()
b = Data()
c = divmod(a, b)

print(c)
# ... my result of divmod...

If you hadn’t defined the __divmod__() method, Python would’ve raised a TypeError.

How to Resolve TypeError: unsupported operand type(s) for divmod()

Consider the following code snippet where you try to divide two custom objects without defining the dunder method __truediv__():

class Data:
    pass


a = Data()
b = Data()
c = divmod(a, b)

print(c)

Running this leads to the following error message on my computer:

Traceback (most recent call last):
  File "C:UsersxcentDesktopcode.py", line 7, in <module>
    c = divmod(a, b)
TypeError: unsupported operand type(s) for divmod(): 'Data' and 'Data'

The reason for this error is that the __divmod__() method has never been defined—and it is not defined for a custom object by default. So, to resolve the TypeError: unsupported operand type(s) for divmod(), you need to provide the __divmod__(self, other) method in your class definition as shown previously:

class Data:
        
    def __divmod__(self, other):
        return '... my result of divmod...'

Of course, you’d use another return value in practice as explained in the “Background divmod()” section.

Python __divmod__ vs __rdivmod__

Say, you want to calculate the divmod of two custom objects x and y:

print(divmod(x, y))

Python first tries to call the left object’s __divmod__() method x.__divmod__(y). But this may fail for two reasons:

  1. The method x.__divmod__() is not implemented in the first place, or
  2. The method x.__divmod__() is implemented but returns a NotImplemented value indicating that the data types are incompatible.

If this fails, Python tries to fix it by calling the y.__rdivmod__() for reverse divmod on the right operand y.

If this method is implemented, Python knows that it doesn’t run into a potential problem of a non-commutative operation. If it would just execute y.__divmod__(x) instead of x.__divmod__(y), the result would be wrong because the divmod operation is non-commutative (neither the integer division, nor the modulo operation is commutative). That’s why y.__rdivmod__(x) is needed.

So, the difference between x.__divmod__(y) and x.__rdivmod__(y) is that the former calculates (x // y, x % y) whereas the latter calculates (y // x, y % x) — both calling the respective divmod method defined on object x.

You can see this in effect here where we attempt to call the divmod operation on the left operand x—but as it’s not implemented, Python simply calls the reverse divmod operation on the right operand y.

class Data_1:
    pass

class Data_2:
    def __rdivmod__(self, other):
        return 'called divmod'


x = Data_1()
y = Data_2()

print(divmod(x, y))
# called divmod

References:

  • https://docs.python.org/3/reference/datamodel.html

Explainer Video Modulo

You can also check out my explainer video where I’ll give you a deep dive on the built-in modulo operation and how to use them for various data types. Click to watch:

Python Modulo — A Simple Illustrated Guide

Where to Go From Here?

Enough theory. Let’s get some practice!

Coders get paid six figures and more because they can solve problems more effectively using machine intelligence and automation.

To become more successful in coding, solve more real problems for real people. That’s how you polish the skills you really need in practice. After all, what’s the use of learning theory that nobody ever needs?

You build high-value coding skills by working on practical coding projects!

Do you want to stop learning with toy projects and focus on practical code projects that earn you money and solve real problems for people?

🚀 If your answer is YES!, consider becoming a Python freelance developer! It’s the best way of approaching the task of improving your Python skills—even if you are a complete beginner.

If you just want to learn about the freelancing opportunity, feel free to watch my free webinar “How to Build Your High-Income Skill Python” and learn how I grew my coding business online and how you can, too—from the comfort of your own home.

Join the free webinar now!

While working as a researcher in distributed systems, Dr. Christian Mayer found his love for teaching computer science students.

To help students reach higher levels of Python success, he founded the programming education website Finxter.com. He’s author of the popular programming book Python One-Liners (NoStarch 2020), coauthor of the Coffee Break Python series of self-published books, computer science enthusiast, freelancer, and owner of one of the top 10 largest Python blogs worldwide.

His passions are writing, reading, and coding. But his greatest passion is to serve aspiring coders through Finxter and help them to boost their skills. You can join his free email academy here.

krenddel

0 / 0 / 0

Регистрация: 25.04.2020

Сообщений: 14

1

26.04.2020, 12:21. Показов 11977. Ответов 2

Метки нет (Все метки)


В коде:

Python
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#calculator
 
a=float(input())
b=float(input())
c=input()
 
'''
Поддерживаемые операции: +, -, /, *, mod, pow, div, где
mod — это взятие остатка от деления,
pow — возведение в степень,
div — целочисленное деление.
'''
 
if c=='+':
    print(a+b)
if c=='-':
    print(a-b)
if c=='/':
    if b=='0':
        print('Деление на 0!')
    else:
        print(a/b)
elif c=='*':
    print(a*b)
elif c=='mod':
    if (b=='0') or (b=='0.0'):
        print('Деление на 0!')
    else:
        print(a%b)
elif c=='pow':
    print(a**b)
elif c=='div':
    print(a//b)
else:
    print('Ты что-то ввёл неправильно!  :3')

############################
Выдает ошибку:

Traceback (most recent call last):
File «kaka.py», line 29, in <module>
print(a%b)
ZeroDivisionError: float modulo

Хотя,там написано, что если b равно 0, то вывести «деление на ноль»
Вообще не понимаю в чем ошибка

__________________
Помощь в написании контрольных, курсовых и дипломных работ, диссертаций здесь

0

Programming

Эксперт

94731 / 64177 / 26122

Регистрация: 12.04.2006

Сообщений: 116,782

26.04.2020, 12:21

2

25 / 9 / 0

Регистрация: 26.11.2018

Сообщений: 82

26.04.2020, 12:31

2

На 26 строке вы ставите в условие строку, а надо число. И на 19 замените, где проводится операция деления

0

Viktorrus

1726 / 966 / 198

Регистрация: 22.02.2018

Сообщений: 2,694

Записей в блоге: 6

26.04.2020, 12:38

3

Цитата
Сообщение от krenddel
Посмотреть сообщение

b==’0′

Вы число сравниваете со строкой. Если b будет равно нулю, у Вас условие все равно будет False, то есть у Вас не определяется, когда b равно нулю. Исправте в условии на

Python
1
if b==0:
Python
1
2
3
4
5
>>> b = float('0')
>>> b
0.0
>>> b == 0
True

0

IT_Exp

Эксперт

87844 / 49110 / 22898

Регистрация: 17.06.2006

Сообщений: 92,604

26.04.2020, 12:38

3

 a = input()
f = float(a.replace(',','.'))
b = float(input())
c = input()
if b == 0 and (c == 'div' or 'mod' or '/'):
    print('Деление на 0!')
elif c == '+':
    print(f + b)
elif c == '-':
    print(f - b)
elif c == '*':
    print(f * b)
elif c == '/':
    print(f / b)
elif c == 'mod':
    print(f % b)
elif c == 'div':
    print(f // b)
elif c == 'pow':
    print(f ** b)

При вводных данных:
5
0
— или +

Выводит “Деление на 0!”
При

 if b == 0 and (c == 'div' or 'mod' or '/') and not(c == '-' or '+')

Вот такая штука с mod

 Test input:
5.0
0.0
mod 
Correct output:
Деление на 0!
Your code output:
Error:
Traceback (most recent call last):
  File "jailed_code", line 16, in <module>
    print(f % b)
ZeroDivisionError: float modulo

И вот такая с div

 Traceback (most recent call last):
  File "jailed_code", line 18, in <module>
    print(f // b)
ZeroDivisionError: float divmod()

Само задание для написание калькулятора:
Напишите простой калькулятор, который считывает с пользовательского ввода три строки: первое число, второе число и операцию, после чего применяет операцию к введённым числам (“первое число” “операция” “второе число”) и выводит результат на экран.

Поддерживаемые операции: +, -, /, *, mod, pow, div, где
mod — это взятие остатка от деления,
pow — возведение в степень,
div — целочисленное деление.

Если выполняется деление и второе число равно 0, необходимо выводить строку “Деление на 0!”.

Обратите внимание, что на вход программе приходят вещественные числа.

October 22, 2019
Floor Division in Python

  • The Problem
    • Avoiding this Error
  • Definition of Floor Division
  • Python Arithmetic
  • float_floor_div
  • float_divmod
    • Division by Zero
    • Quotient and Dividend
    • Remainder Sign
    • Remainder is Zero
    • Calling Floor
    • Numerator is Zero
    • Returning the Tuple
  • Design Decisions
    • PEP 328
    • PEP 3141
    • Further Debate
  • Conclusions
    • Breaking Changes
    • Simplicity
    • Speed
    • Polymorphism

The Problem

In solving data analytics problems, occasionally we must bin data into intervals or categories based on some arbitrary stratification. A common example outside of the world of computer science is letter grades, a concept that can be abstracted1 to:

def grade(score):
    grades = ['F', 'D', 'C', 'B', 'A']
    if score >= 100:
        return 'A'
    if score <= 50:
        return 'F'
    return grades[(score - 50) // 10]

Thus, for:

scores = [88, 72, 61, 39, 97]
results = [grade(score) for score in scores]

We get:

['B', 'C', 'D', 'F', 'A']

However, if any of our score values2 are not integers we will encounter a TypeError:

>>> grade(61.2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
TypeError: list indices must be integers or slices, not float

This is caused by the following type coercion, as lists cannot be indexed by floats:

>>> (61.2 - 50) // 2
1.0

Avoiding this Error

Of course, we can cast to an int:

def grade(score):
    grades = ['F', 'D', 'C', 'B', 'A']
    if score > 99:
        return 'A'
    if score <= 50:
        return 'F'
    return grades[(int(score) - 50) // 10]

However, for large datasets this is not an efficient operation:

chris% python -m timeit 'int(102.3 // 2)'
5000000 loops, best of 5: 99.6 nsec per loop
chris% python -m timeit '102.3 // 2'     
50000000 loops, best of 5: 7.05 nsec per loop

In my real-world example, the score equivalent is always a float, so we can map the float integer values to their respective categories3, similar to:

>>> grades
{0.0: 'F', 1.0: 'D', ...}
>>> grades[0]
'F'
>>> grades[0.]
'F'
>>> grades[1]
'D'
>>> grades[1.]
'D'

However, I was still curious why floor division was returning a float and not an integer.

Definition of Floor Division

According to Concrete Mathematics4:

⌊x⌋ = the greatest integer less than or equal to x

⌈x⌉ = the least integer greater than or equal to x

Note the key word integer; while this may be the official mathematical definition, Python does not seem to follow this to the letter and returns a type-coerced value.

Python Arithmetic

It is documented when types are coerced. From the Python docs:

  • If either argument is a complex number, the other is converted to complex;
  • otherwise, if either argument is a floating point number, the other is converted to floating point;
  • otherwise, both must be integers and no conversion is necessary.

The same is true for binary operators:

The / (division) and // (floor division) operators yield the quotient of their arguments. The numeric arguments are first converted to a common type. Division of integers yields a float, while floor division of integers results in an integer; the result is that of mathematical division with the ‘floor’ function applied to the result.

float_floor_div

The float divmod work occurs in float_floor_div, which comes from the cpython source. All this function does is call float_divmod and dereferences the quotient:

static PyObject *
float_floor_div(PyObject *v, PyObject *w)
{
    PyObject *t, *r;

    t = float_divmod(v, w);
    if (t == NULL || t == Py_NotImplemented)
        return t;
    assert(PyTuple_CheckExact(t));
    r = PyTuple_GET_ITEM(t, 0);
    Py_INCREF(r);
    Py_DECREF(t);
    return r;
}

The bulk of the work occurs inside of the called function, float_divmod.

float_divmod

The source for float_divmod lives here. The first step of this function handles the type cast:

float_divmod(PyObject *v, PyObject *w)
{
    double vx, wx;
    double div, mod, floordiv;
    CONVERT_TO_DOUBLE(v, vx);
    CONVERT_TO_DOUBLE(w, wx);
    ...

Where vx is the dividend, and wx is the divisor. The rest of this function handles the various cases that can occur:

Division by Zero

If the divisor is zero, we raise an error:

...
if (wx == 0.0) {
    PyErr_SetString(PyExc_ZeroDivisionError, "float divmod()");
    return NULL;
}
...

Quotient and Dividend

Before we can do any work, we must calculate the quotient and dividend:

mod = fmod(vx, wx);
div = (vx - mod) / wx;

We get the modulus using the fmod function from the C Standard Library, and the quotient using float division, also leveraging the C standard library.

Remainder Sign

Once we have these values, we check to ensure the sign is correct.

...
if (mod) {
    if ((wx < 0) != (mod < 0)) {
        mod += wx;
        div -= 1.0;
    }
}
...

Remainder is Zero

If the remainder (i.e., the modulus) is zero, we copy the sign to the divisor with copysign(0.0, wx)5.

Calling Floor

If the quotient is not zero, we call the floor function:

...
if (div) {
    floordiv = floor(div);
    if (div - floordiv > 0.5)
        floordiv += 1.0;
}
...

Numerator is Zero

If the dividend is zero, we copy the sign of the quotient onto zero with copysign(0.0, vx / wx).

Returning the Tuple

Once all of this has completed, we return a tuple like (floor_qotient, modulus).

Design Decisions

As Python is a mature language, the changes that affected these behaviors underwent much debate.

PEP 328

Since the proposal of the Python 3 change to the division operator in PEP 238, it was decided that the result of floor division with floats would be a float, following the above arithmetic conversions:

In particular, if a and b are both ints or longs, the result has the same type and value as for classic division on these types (including the case of mixed input types; int // long and long // int will both return a long).

For floating point inputs, the result is a float. For example: 3.5 // 2.0 == 1.0

PEP 3141

This PEP defined the changes to the numerical stack in Python and explicitly noted the following:

__floor__(self), called from math.floor(x), which returns the greatest Integral <= x.

Thus, it would follow that the floor call in float_divmod should return an integer and not a float value. However, this is not the case.

Further Debate

Thirteen years later, Alexander Belopolsky reported Issue 22444: Floor divide should return int:

PEP 3141 defines floor division as floor(x/y) and specifies that floor() should return int type. Builtin float type has been made part of the PEP 3141 numerical tower, but floor division of two floats still results in a float.

In this thread, my exact issue accessing a list index above was raised:

This is one of the common uses of floor division — to find an index of a cell in a regular grid: (x - start) // step. In this situation, it is convenient to have the result ready to be used as an index without a cast.

However, the decision to not make this change ended with Raymond Hettinger’s reply:

  1. The current behavior has been around for a long time and is implemented in several modules, including decimal and fraction. As core devs, we need to keep focused on a priority of making the language stable (not making changes that truly necessary and invalidating all previously published material) and more importantly not adding yet more obstacles to converting from Python 2 to Python 3 (which Guido has called «death by a thousand cuts»).

  2. The current behavior can be useful it that it allows floor division operations without unexpected type conversions occurring in the middle of an expression. We really don’t want to break those use cases.

Conclusions

Python’s philosophy means the language provides high-level access to data structures. Here, the coercion avoids dealing with the nuances of numeric types that one would have to wrangle with when writing C. In C, given the different numerical types, you have to return a double, because the range of a double is so much larger than that of an integer.

Breaking Changes

These decisions were made during the early days of the Python 3 transition, where the core developers wanted to minimize the friction of the upgrade from 2 to 3. As a result, to not break code that depended on floor division being type preserving6, it was decided that floor division should return a value equal to an integer7 but agnostic of type.

In a post-Python 2 era, these concerns do not hold the same weight. This is an example of some technical debt that Python has accrued, as other parts of Python do not behave this way:

>>> type(timedelta(2.2) // timedelta(3))
<class 'int'>
>>> type(Fraction(2.3) // Fraction(1))
<class 'int'>

Simplicity

Python makes sacrifices to retain its beauty and simplicity, this being one of them. As a result, the programmer, in this instance, is unable to choose the functionality that best addresses a specific problem. A possible solution would be to provide both floor() and float_floor() for accessing these values in both types. Also possible would be to make lists accessible by the float counterparts of their integer indices, since 1 == 1.0.

Speed

Further, the language is far more performant as a result of this decision: float // any == float is a computationally simple and fast operation; float // any == int is significantly more complicated and expensive, especially for large inputs due to the inefficient cast from float back to int.

chris% python -m timeit 'int(102.3 // 2)'
5000000 loops, best of 5: 99.6 nsec per loop
chris% python -m timeit '102.3 // 2'     
50000000 loops, best of 5: 7.05 nsec per loop

This performance hit does not stem from the float conversion itself 8 but rather from the fact that integer and float values are stored in different registers: the CPU must first store the float from the FPU register in memory, then read that memory address into the int register. This is a classic example of a load-hit-store stall.

Polymorphism

Since we can combine types in this way in Python, we need to be cognizant of the pitfalls that we can run into when writing polymorphic functions. Most of the time, the type of number will not matter to the Python interpreter. When type does matter, understanding how these data are handled internally leads to a smoother development process.


Discussion: Hacker News, Reddit

 a = input()
f = float(a.replace(',','.'))
b = float(input())
c = input()
if b == 0 and (c == 'div' or 'mod' or '/'):
    print('Деление на 0!')
elif c == '+':
    print(f + b)
elif c == '-':
    print(f - b)
elif c == '*':
    print(f * b)
elif c == '/':
    print(f / b)
elif c == 'mod':
    print(f % b)
elif c == 'div':
    print(f // b)
elif c == 'pow':
    print(f ** b)

При вводных данных:
5
0
— или +

Выводит “Деление на 0!”
При

 if b == 0 and (c == 'div' or 'mod' or '/') and not(c == '-' or '+')

Вот такая штука с mod

 Test input:
5.0
0.0
mod 
Correct output:
Деление на 0!
Your code output:
Error:
Traceback (most recent call last):
  File "jailed_code", line 16, in <module>
    print(f % b)
ZeroDivisionError: float modulo

И вот такая с div

 Traceback (most recent call last):
  File "jailed_code", line 18, in <module>
    print(f // b)
ZeroDivisionError: float divmod()

Само задание для написание калькулятора:
Напишите простой калькулятор, который считывает с пользовательского ввода три строки: первое число, второе число и операцию, после чего применяет операцию к введённым числам (“первое число” “операция” “второе число”) и выводит результат на экран.

Поддерживаемые операции: +, -, /, *, mod, pow, div, где
mod — это взятие остатка от деления,
pow — возведение в степень,
div — целочисленное деление.

Если выполняется деление и второе число равно 0, необходимо выводить строку “Деление на 0!”.

Обратите внимание, что на вход программе приходят вещественные числа.


Floor Division in Python

  • The Problem
    • Avoiding this Error
  • Definition of Floor Division
  • Python Arithmetic
  • float_floor_div
  • float_divmod
    • Division by Zero
    • Quotient and Dividend
    • Remainder Sign
    • Remainder is Zero
    • Calling Floor
    • Numerator is Zero
    • Returning the Tuple
  • Design Decisions
    • PEP 328
    • PEP 3141
    • Further Debate
  • Conclusions
    • Breaking Changes
    • Simplicity
    • Speed
    • Polymorphism

The Problem

In solving data analytics problems, occasionally we must bin data into intervals or categories based on some arbitrary stratification. A common example outside of the world of computer science is letter grades, a concept that can be abstracted1 to:

def grade(score):
    grades = ['F', 'D', 'C', 'B', 'A']
    if score >= 100:
        return 'A'
    if score <= 50:
        return 'F'
    return grades[(score - 50) // 10]

Thus, for:

scores = [88, 72, 61, 39, 97]
results = [grade(score) for score in scores]

We get:

['B', 'C', 'D', 'F', 'A']

However, if any of our score values2 are not integers we will encounter a TypeError:

>>> grade(61.2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 1, in <lambda>
TypeError: list indices must be integers or slices, not float

This is caused by the following type coercion, as lists cannot be indexed by floats:

>>> (61.2 - 50) // 2
1.0

Avoiding this Error

Of course, we can cast to an int:

def grade(score):
    grades = ['F', 'D', 'C', 'B', 'A']
    if score > 99:
        return 'A'
    if score <= 50:
        return 'F'
    return grades[(int(score) - 50) // 10]

However, for large datasets this is not an efficient operation:

chris% python -m timeit 'int(102.3 // 2)'
5000000 loops, best of 5: 99.6 nsec per loop
chris% python -m timeit '102.3 // 2'     
50000000 loops, best of 5: 7.05 nsec per loop

In my real-world example, the score equivalent is always a float, so we can map the float integer values to their respective categories3, similar to:

>>> grades
{0.0: 'F', 1.0: 'D', ...}
>>> grades[0]
'F'
>>> grades[0.]
'F'
>>> grades[1]
'D'
>>> grades[1.]
'D'

However, I was still curious why floor division was returning a float and not an integer.

Definition of Floor Division

According to Concrete Mathematics4:

⌊x⌋ = the greatest integer less than or equal to x

⌈x⌉ = the least integer greater than or equal to x

Note the key word integer; while this may be the official mathematical definition, Python does not seem to follow this to the letter and returns a type-coerced value.

Python Arithmetic

It is documented when types are coerced. From the Python docs:

  • If either argument is a complex number, the other is converted to complex;
  • otherwise, if either argument is a floating point number, the other is converted to floating point;
  • otherwise, both must be integers and no conversion is necessary.

The same is true for binary operators:

The / (division) and // (floor division) operators yield the quotient of their arguments. The numeric arguments are first converted to a common type. Division of integers yields a float, while floor division of integers results in an integer; the result is that of mathematical division with the ‘floor’ function applied to the result.

float_floor_div

The float divmod work occurs in float_floor_div, which comes from the cpython source. All this function does is call float_divmod and dereferences the quotient:

static PyObject *
float_floor_div(PyObject *v, PyObject *w)
{
    PyObject *t, *r;

    t = float_divmod(v, w);
    if (t == NULL || t == Py_NotImplemented)
        return t;
    assert(PyTuple_CheckExact(t));
    r = PyTuple_GET_ITEM(t, 0);
    Py_INCREF(r);
    Py_DECREF(t);
    return r;
}

The bulk of the work occurs inside of the called function, float_divmod.

float_divmod

The source for float_divmod lives here. The first step of this function handles the type cast:

float_divmod(PyObject *v, PyObject *w)
{
    double vx, wx;
    double div, mod, floordiv;
    CONVERT_TO_DOUBLE(v, vx);
    CONVERT_TO_DOUBLE(w, wx);
    ...

Where vx is the dividend, and wx is the divisor. The rest of this function handles the various cases that can occur:

Division by Zero

If the divisor is zero, we raise an error:

...
if (wx == 0.0) {
    PyErr_SetString(PyExc_ZeroDivisionError, "float divmod()");
    return NULL;
}
...

Quotient and Dividend

Before we can do any work, we must calculate the quotient and dividend:

mod = fmod(vx, wx);
div = (vx - mod) / wx;

We get the modulus using the fmod function from the C Standard Library, and the quotient using float division, also leveraging the C standard library.

Remainder Sign

Once we have these values, we check to ensure the sign is correct.

...
if (mod) {
    if ((wx < 0) != (mod < 0)) {
        mod += wx;
        div -= 1.0;
    }
}
...

Remainder is Zero

If the remainder (i.e., the modulus) is zero, we copy the sign to the divisor with copysign(0.0, wx)5.

Calling Floor

If the quotient is not zero, we call the floor function:

...
if (div) {
    floordiv = floor(div);
    if (div - floordiv > 0.5)
        floordiv += 1.0;
}
...

Numerator is Zero

If the dividend is zero, we copy the sign of the quotient onto zero with copysign(0.0, vx / wx).

Returning the Tuple

Once all of this has completed, we return a tuple like (floor_qotient, modulus).

Design Decisions

As Python is a mature language, the changes that affected these behaviors underwent much debate.

PEP 328

Since the proposal of the Python 3 change to the division operator in PEP 238, it was decided that the result of floor division with floats would be a float, following the above arithmetic conversions:

In particular, if a and b are both ints or longs, the result has the same type and value as for classic division on these types (including the case of mixed input types; int // long and long // int will both return a long).

For floating point inputs, the result is a float. For example: 3.5 // 2.0 == 1.0

PEP 3141

This PEP defined the changes to the numerical stack in Python and explicitly noted the following:

__floor__(self), called from math.floor(x), which returns the greatest Integral <= x.

Thus, it would follow that the floor call in float_divmod should return an integer and not a float value. However, this is not the case.

Further Debate

Thirteen years later, Alexander Belopolsky reported Issue 22444: Floor divide should return int:

PEP 3141 defines floor division as floor(x/y) and specifies that floor() should return int type. Builtin float type has been made part of the PEP 3141 numerical tower, but floor division of two floats still results in a float.

In this thread, my exact issue accessing a list index above was raised:

This is one of the common uses of floor division — to find an index of a cell in a regular grid: (x - start) // step. In this situation, it is convenient to have the result ready to be used as an index without a cast.

However, the decision to not make this change ended with Raymond Hettinger’s reply:

  1. The current behavior has been around for a long time and is implemented in several modules, including decimal and fraction. As core devs, we need to keep focused on a priority of making the language stable (not making changes that truly necessary and invalidating all previously published material) and more importantly not adding yet more obstacles to converting from Python 2 to Python 3 (which Guido has called «death by a thousand cuts»).

  2. The current behavior can be useful it that it allows floor division operations without unexpected type conversions occurring in the middle of an expression. We really don’t want to break those use cases.

Conclusions

Python’s philosophy means the language provides high-level access to data structures. Here, the coercion avoids dealing with the nuances of numeric types that one would have to wrangle with when writing C. In C, given the different numerical types, you have to return a double, because the range of a double is so much larger than that of an integer.

Breaking Changes

These decisions were made during the early days of the Python 3 transition, where the core developers wanted to minimize the friction of the upgrade from 2 to 3. As a result, to not break code that depended on floor division being type preserving6, it was decided that floor division should return a value equal to an integer7 but agnostic of type.

In a post-Python 2 era, these concerns do not hold the same weight. This is an example of some technical debt that Python has accrued, as other parts of Python do not behave this way:

>>> type(timedelta(2.2) // timedelta(3))
<class 'int'>
>>> type(Fraction(2.3) // Fraction(1))
<class 'int'>

Simplicity

Python makes sacrifices to retain its beauty and simplicity, this being one of them. As a result, the programmer, in this instance, is unable to choose the functionality that best addresses a specific problem. A possible solution would be to provide both floor() and float_floor() for accessing these values in both types. Also possible would be to make lists accessible by the float counterparts of their integer indices, since 1 == 1.0.

Speed

Further, the language is far more performant as a result of this decision: float // any == float is a computationally simple and fast operation; float // any == int is significantly more complicated and expensive, especially for large inputs due to the inefficient cast from float back to int.

chris% python -m timeit 'int(102.3 // 2)'
5000000 loops, best of 5: 99.6 nsec per loop
chris% python -m timeit '102.3 // 2'     
50000000 loops, best of 5: 7.05 nsec per loop

This performance hit does not stem from the float conversion itself 8 but rather from the fact that integer and float values are stored in different registers: the CPU must first store the float from the FPU register in memory, then read that memory address into the int register. This is a classic example of a load-hit-store stall.

Polymorphism

Since we can combine types in this way in Python, we need to be cognizant of the pitfalls that we can run into when writing polymorphic functions. Most of the time, the type of number will not matter to the Python interpreter. When type does matter, understanding how these data are handled internally leads to a smoother development process.


Discussion: Hacker News, Reddit

Понравилась статья? Поделить с друзьями:
  • Flip battery ошибка пежо
  • Flibusta opds ошибка
  • Flengine dll ошибка
  • Flawless widescreen ошибка
  • Flatout ultimate carnage ошибка при запуске