優先順序就是全部問題
讀懂 2 + 3 * 4 需要知道乘法比加法結合得更緊,因此答案是 14 而不是 20。每個運算子都帶有優先順序和結合性。乘法、除法和取餘高於加減;冪運算高於以上全部,並且是右結合的,所以 2^3^2 意為 2^(3^2),結果是 512 而不是 64。
一元負號是微妙的情形。數學慣例把它排在冪運算之下,這就是 -3^2 等於 -9 的原因:指數作用於 3,取負作用於結果。電子表格軟體以持不同意見著稱,它會返回 9;如果你曾見過兩個工具對同一公式給出不同答案,通常正是這個原因。本求值器遵循數學慣例。
括號可以覆蓋上述一切,而且值得放心大膽地使用。一個需要查優先順序表才能讀懂的表示式,遲早會被人讀錯。
簡述排程場演算法
表示式首先被拆分為記號——數字、名稱、運算子和標點——然後由 Dijkstra 的排程場演算法重排為字尾形式,即運算子跟在運算元之後。2 + 3 * 4 會變成 2 3 4 * +。字尾形式不需要括號也不需要優先順序規則,因為記號的順序已經編碼了結構,一臺簡單的棧式機器一趟就能求值。
該演算法維護兩個結構:一個輸出列表和一個運算子棧。數字直接進入輸出;運算子在自己入棧之前,先彈出棧中所有結合力不低於它的運算子。括號在棧上充當標記,右括號會一路彈出直到遇見它的配對。函式呼叫則作為帶引數計數的運算子一同參與,每遇到一個逗號計數加一。
電子表格引擎和查詢規劃器內部用的正是同一套技術。它以線性時間執行,無需遞迴,並且能自然地報出格式錯誤:如果最後棧沒有乾淨地清空,說明輸入本身不平衡。
關於浮點數,說實話
結果以 IEEE 754 雙精度計算,這與所有瀏覽器和多數程式語言使用的算術相同。它以二進位制儲存數字,而 0.1 這樣的十進位制小數沒有精確的二進位制表示,這正是 0.1 + 0.2 眾所周知會得到 0.30000000000000004 而不是 0.3 的原因。
把顯示結果舍入到設定的有效數字位數,可以在不改變底層值的前提下隱藏這些噪聲,這正是這裡精度控制元件的作用。設為 12 位既能保持可讀,又距離雙精度真正能區分的 15 到 17 位留有充足餘量。
有兩個限制值得記住。整數只在 2^53 以內保持精確,因此非常大的階乘和乘積會產生漂移;超過 170! 會直接溢位為無窮大。另外,兩個幾乎相等的數相減會摧毀精度——前導數字相互抵消,只剩下帶噪聲的尾部數字——因此重新整理公式以避開這類減法,往往會比直觀寫法精確得多。