はじめに
業務で数値比較をするべき場所が、文字列の比較を行っていました。その結果バグの原因になっていました。文字列の比較はどのような処理を行っているのか詳しく調べてみました。言語はPythonです。
数値比較と数値文字列比較の挙動の違い
print(100 > 2) # True
print("100" > "2") # False
数値の比較と数値文字列の比較では異なる結果が出力されました。
理由
リファレンスには下記のように記されていました。
文字列 (str のインスタンス) の比較は、文字の Unicode のコードポイントの数としての値 (組み込み関数 ord() の返り値) を使った辞書式順序で行われます。
https://docs.python.org/ja/3/reference/expressions.html#id12
概して、シーケンスオブジェクトは、同じシーケンス型の他のオブジェクトと比較できます。比較には 辞書的な (lexicographical) 順序が用いられます。まず、最初の二つの要素を比較し、その値が等しくなければその時点で比較結果が決まります。等しければ次の二つの要素を比較し、以降シーケンスの要素が尽きるまで続けます。比較しようとする二つの要素がいずれも同じシーケンス型であれば、そのシーケンス間での辞書比較を再帰的に行います。二つのシーケンスの全ての要素の比較結果が等しくなれば、シーケンスは等しいとみなされます。
https://docs.python.org/ja/3/tutorial/datastructures.html#comparing-sequences-and-other-types
すなわち、文字列の比較を行うときは2つの要素の先頭のUnicodeのコードポイントを比較し、等しければ次の要素のコードポイントを比較するということです。コードポイントとはUnicodeに記されている各文字に一意に定義付けされた数字のことです。
print(ord("1")) # 49
print(ord("2")) # 50
"1"のコードポイントは49、"2"のコードポイントは50となりました。すなわち、"1"のコードポイントより"2"のコードポイントが大きいため"100" > "2"はFalseになります。
コードポイントを16進数に変換してUnicodeを確認してみます。
print(hex(ord("1"))) # 0x31
print(hex(ord("2"))) # 0x32
Unicodeを取得できることが確認できました。