我知道 Python//向負無窮大舍入,而在 C /中正在截斷,向 0 舍入。
到目前為止,這是我所知道的:
-12 / 10 = -1 - 2 // c
-12 // 10 = -2 8 # python
12 / -10 = -1 2 // c
12 // -10 = -2 - 8 # python
12 / 10 = 1 2 //both
12 // 10 = 1 2
-12 / -10 = 1 - 2 //both
= 2 8
C :
1. m%(-n) == m%n
2. -m%n == -(m%n)
3. (m/n)*n m%n == m
python:
1. m%(-n) == -8 == -(-m%n)
2. (m//n)*n m%n == m
但是為什么 Python//選擇向負無窮大取整呢?我沒有找到任何資源解釋這一點,但只是找到并聽到人們含糊地說:“出于數學原因”。
例如,在為什么 -1/2 在 C 中計算為 0,但在 Python 中計算為 -1?:
抽象地處理這些事情的人傾向于認為向負無窮大方向走更有意義(這意味著它與數學中定義的模函式兼容,而不是 % 有一些有趣的含義)。
但我沒有看到 C /與模函式不兼容。在 C 中,(m/n)*n m%n == m也適用。
那么 Python 選擇舍入到負無窮大的(數學)原因是什么?
uj5u.com熱心網友回復:
但是為什么 Python
//選擇向負無窮大取整呢?
我不確定最初做出這個選擇的原因是否記錄在任何地方(盡管據我所知,它可以在某處的某些 PIP 中詳細解釋),但我們當然可以提出各種原因感覺。
一個原因很簡單,向負數(或正數!)無窮大舍入意味著所有數字都以相同的方式四舍五入,而向零四舍五入使零變得特殊。數學上的說法是,向下舍入到 -∞ 是平移不變的,即它滿足方程:
round_down(x k) == round_down(x) k
對于所有實數x和所有整數k。向零舍入不會,因為例如:
round_to_zero(0.5 - 1) != round_to_zero(0.5) - 1
當然,也存在其他引數,例如您參考的引數基于與(我們希望如何)%運算子(行為)的兼容性 - 更多內容如下。
事實上,我想說這里真正的問題是為什么 Python 的int()函式沒有定義為將浮點引數舍入到負無窮大,所以m // n等于int(m / n). (我懷疑“歷史原因”。)再說一次,這沒什么大不了的,因為 Python 至少math.floor()確實滿足m // n == math.floor(m / n).
但我沒有看到 C
/與模函式不兼容。在 C 中,(m/n)*n m%n == m也適用。
/沒錯,但是在向零舍入的同時保留該身份需要%以一種尷尬的方式定義負數。特別是,我們失去了 Python 的以下兩個有用的數學屬性%:
0 <= m % n < abs(n)對于所有m和n; 和(m k * n) % n == m % n對于所有整數m和。nk
這些屬性很有用,因為 的主要用途之一%是將數字“環繞”m到有限的長度范圍內n。
例如,假設我們正在嘗試計算方向:假設heading我們當前的指南針航向以度數為單位(從正北順時針計數,使用0 <= heading < 360)并且我們想要在轉動angle度數后計算新的航向(angle > 0如果我們順時針轉動,或者angle < 0如果我們逆時針轉動)。使用 Python 的%運算子,我們可以簡單地計算我們的新標題:
heading = (heading angle) % 360
這將在所有情況下都有效。
然而,如果我們嘗試在 C 中使用這個公式,它具有不同的舍入規則和相應的不同%運算子,我們會發現環繞并不總是按預期作業!例如,如果我們開始面向西北 ( heading = 315) 并順時針旋轉 90° ( angle = 90),我們最終將面向東北 ( heading = 45)。但是,如果嘗試逆時針轉回 90° ( angle = -90),使用 C 的%運算子,我們不會heading = 315按預期回傳,而是回傳heading = -45!
為了使用 C 運算子獲得正確的環繞行為%,我們需要將公式撰寫為:
heading = (heading angle) % 360;
if (heading < 0) heading = 360;
或作為:
heading = ((heading angle) % 360) 360) % 360;
(更簡單的公式heading = (heading angle 360) % 360只有在我們總能保證的情況下才有效heading angle >= -360。)
這是您為除法的非平移不變舍入規則以及非平移不變%運算子所付出的代價。
uj5u.com熱心網友回復:
盡管我無法提供關于為什么/如何選擇舍入模式的正式定義,但是當你認為這在 C 中并不完全相同%時,你所包含的關于與運算子兼容性的參考確實是有道理的和 Python。%
在 C 中,它是余數運算子,而在 Python 中,它是模數運算子——而且,當兩個運算元有不同的符號時,它們不一定是相同的東西。在以下答案中對這些運算子之間的區別有一些很好的解釋:“mod”和“remainder”有什么區別?
現在,考慮到這種差異,整數除法的舍入(截斷)模式必須與兩種語言中的一樣,以確保您參考的關系(m/n)*n m%n == m, 保持有效。
這里有兩個簡短的程式演示了這一點(請原諒我有點幼稚的 Python 代碼——我是該語言的初學者):
C :
#include <iostream>
int main()
{
int dividend, divisor, quotient, remainder, check;
std::cout << "Enter Dividend: "; // -27
std::cin >> dividend;
std::cout << "Enter Divisor: "; // 4
std::cin >> divisor;
quotient = dividend / divisor;
std::cout << "Quotient = " << quotient << std::endl; // -6
remainder = dividend % divisor;
std::cout << "Remainder = " << remainder << std::endl; // -3
check = quotient * divisor remainder;
std::cout << "Check = " << check << std::endl; // 27
return 0;
}
Python:
print("Enter Dividend: ") # -27
dividend = int(input())
print("Enter Divisor: ") # 4
divisor = int(input())
quotient = dividend // divisor;
print("Quotient = " str(quotient)) # -7
modulus = dividend % divisor;
print("Remainder = " str(modulus)) # 1
check = quotient * divisor modulus; # -27
print("Check = " str(check))
請注意,對于不同符號(-27 和 4)的給定輸入,商和余數/模在語言之間是不同的,但在兩種情況下恢復的check值都是正確的。
uj5u.com熱心網友回復:
“出于數學原因”
考慮一個問題(在視頻游戲中很常見),您的 X 坐標可能為負數,并希望將其轉換為平鋪坐標(假設 16x16 平鋪)和平鋪內的偏移
Python%直接為您提供偏移量,并直接/為您提供平鋪:
>>> -35 // 16 # If we move 35 pixels left of the origin...
-3
>>> -35 % 16 # then we are 13 pixels from the left edge of a tile in row -3.
13
(并divmod同時給你兩個。)
uj5u.com熱心網友回復:
整數和實數算術都定義了它們的除法運算子,以便以下兩個等式適用于所有 n 和 d 值。
(n d)/d = (n/d) 1
(-n)/d = -(n/d)
不幸的是,整數算術不能以兩者都成立的方式定義。出于許多目的,第一個等價比第二個更有用,但在代碼將兩個值相除的大多數情況下,以下之一將適用:
這兩個值都是正的,在這種情況下,第二個等價是無關緊要的。
被除數是除數的精確整數倍,在這種情況下,兩個等式可以同時成立。
從歷史上看,處理涉及負數的除法的最簡單方法是觀察是否恰好一個運算元為負,洗掉符號,執行加法,然后如果恰好一個運算元為負,則將結果設為負。這將在兩種常見情況下都按要求運行,并且比使用在所有情況下都支持第一個等價的方法便宜,而不是僅當除數是股息的精確倍數時。
Python 不應該被視為使用劣等語意,而是決定在重要的情況下通常會更好的語意即使在精確的語意無關緊要的情況下也值得稍微減慢除法。
uj5u.com熱心網友回復:
從 python 檔案(https://docs.python.org/2/reference/expressions.html):
/(除法)和 //(下除法)運算子產生其引數的商。數字引數首先轉換為通用型別。普通或長整數除法產生相同型別的整數;結果是數學除法的結果,“地板”函式應用于結果。
注意在最后一行它說該floor函式應用于結果。
那么讓我們看看floor函式在 python 中的行為(https://docs.python.org/3/library/math.html):
math.floor(x) 回傳 x 的下限,即小于或等于 x 的最大整數。如果 x 不是浮點數,則委托給 x。floor (),它應該回傳一個 Integral 值。
這里math.floor(x)python中函式的邏輯是get the largest integer less than or equal to x,它與floor()c 中的函式邏輯相同(https://www.cplusplus.com/reference/cmath/floor/)。
但是因為 c 中的除法運算子取決于運算元的資料型別,所以它只是截斷小數部分并只產生除法商的整數部分,以防兩個運算元是整數(https://www.informit.com/articles /article.aspx?p=352857&seqNum=4 ):
除法轉移:您還沒有看到關于除法運算子 (/) 的其余故事。此運算子的行為取決于運算元的型別。如果兩個運算元都是整數,則 C 執行整數除法。這意味著答案的任何小數部分都將被丟棄,使結果成為整數。如果一個或兩個運算元都是浮點值,則保留小數部分,使結果為浮點數。
因此,在這兩種語言中,地板的邏輯是相同的,并且是向負無窮大舍入,但是對于 python,地板除法實際上呼叫了地板函式,這與處理兩個整數時的 c 除法行為不同。
為什么floor要向負無窮舍入是因為它是獲得小于或等于所應用數的最大整數的最合理方法。
以數字為例,-6.8并對其應用 floor 函式,結果-7現在將-7是小于或等于的最大整數,-6.8因為我們向負無窮大取整,但是如果我們只是截斷數字的小數部分呢?
它-6實際上比我們原來的數字要大-6.8,所以我認為這取決于你是要截斷小數部分還是要得到除法商的底數。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/412637.html
標籤:
