Python 3.13 から「兆」が 10 の 6 乗になっている

この記事は 2024 TSG Advent Calendar 3日目の記事です。昨日の記事は @__dAi00 さんの記事 AivisSpeechを使ったDiscordボットの作成 ①AivisSpeechをGoogle Cloud Runにデプロイする でした。12/5 公開予定の続編も楽しみです。

今回は、初日に公開した以下の記事の副産物です。

import unicodedata

# Python 3.12 まで 1000000000000.0
# Python 3.13 から 1000000.0
print(unicodedata.numeric("兆"))

大変だ。Python 3.13 から「5000 兆円」が 50 億円になってしまう(?)

unicodedata.numeric メソッドと Unicode

例によって Unicode が関係してきます。前編でも触れたとおり、Unicode はそれぞれの文字がどんな性質を持つのかを「Unicode 文字データベース (UCD)」1 に記録しています。データベースには「絵文字かどうか (Emoji)」や「大文字かどうか (Uppercase)」といった属性が文字ごとに記録されています。

今回の記事で鍵になる情報は Numeric_Value プロパティ2 です。このプロパティは、文字がどういう数を表しているかを示したものです。Pythonunicodedata.numeric メソッドは、文字データベースに Numeric_Value プロパティの登録があった場合にその値を返します3

漢字の Numeric_Value を決めるのは、Unicode の CJK 統合漢字の性質を管理する Unihan データベースの kPrimaryNumeric プロパティです。ひとつの漢字に複数の kPrimaryNumeric が登録されている場合、Numeric_Value には最初の値が採用されます4

kPrimaryNumeric

... If an ideograph has more than one numeric value, the first one is to be considered the most common one, and that first value is used for the Numeric_Value property of the ideograph.

なぜ変わったのか?

Python 3.13 から unicodedata.numeric("兆") の結果が変わったのは、Unicode 15.1 で「兆」(U+5146) の kPrimaryNumeric が変更されたためです。参考までに、Unicode 15.05Unicode 15.16 での Unihan データベースで「兆」の扱いを掲載しておきます。

バージョン kPrimaryNumeric Numeric_Value
15.0 1000000000000 1000000000000
15.1 1000000 1000000000000 1000000

106 が新しく追加されたのは、「兆」を 106 として扱う中国やベトナムなどのでの慣例がもとになっています7。なお、中国では現在でも SI 接頭語の「メガ」を表す漢字として使われているようです8

これは日本における 1012 を表す「兆」の用例とは衝突しますが、こういった場合は中国本土における中国語の用例が優先される傾向があるようです9。おそらく中国語での用例が優先された結果、「兆」の Numeric_Value が 106 に上書きされたと考えられます。