C++, Unreal Engine 4

[C++][UE4] std::string -> floatへの変換をtry / catch無しで行う

std::string文字列がfloatになり得るかどうかをチェックしつつfloatへの変換を行う場合、通常はstd::stofを用いて

のように行うかと思います。ただ、このままですと35行目にあるように小数の後に余計な文字列が連続しているような場合でも小数として生成されてしまいますので、
厳密にはstd::stofの第二引数 size_t* _Idxを用いてゴニョゴニョする必要があります。

約10万個のfloat文字列をfloat型に変換するのに560 msec程度で終わりました。約0.5秒ちょっとです。

ちなみにtry / catchを使わずに単にstd::stofを用いてしまうと

上記のように、floatに変換出来ない値を与えるとフリーズしてしまいます。

で、残念なことにUnreal Engineではtry / catchの使用は推奨されておらず、デフォルトでは無効になっています
一応、回避方法はあるのですが、

参考記事:
How can I enable unwind semantics for C++-style exceptions? – UE4 AnswerHub

バージョンごとに指定方法が異なるなど、やはり無理やりな方法である感は否めません。

となるとtry / catchを使わずにstd::string -> floatへの変換を安全に行いたいわけです。

まず思いつく方法は、正規表現を駆使する方法です。ここは素直に偉大なる先人たちの知恵を使いましょう。

参考記事:
Example: Matching Floating Point Numbers with a Regular Expression

みたいな関数を作ってやって確認すればOKです。ただ、少なくとも私の環境では、この方法は速度が非常に遅くなってしまいました。
上記と同じ約10万個のfloat文字列をfloat型に変換するのに4268 msecも要してしまいました。約4秒です。
しかも、正確にはfloatの範囲内に小数の値が収まっているかどうかなどのチェックもこの後追加で行う必要があります。

//——2018/5/15加筆——
上記正規表現を用いた方法に関して、以下のようなコメントを頂きました。

…た、確かに…!値が変わらない定数はstatic constで良いという大鉄則を完全に見落としておりました…。恥ずかしい…。

というわけで以下のように改良してみると…

上記と同じ約10万個のfloat文字列をfloat型に変換するのに2779 msecでした。約2.8秒になりました。
regexの生成って重いのですね…。しかしそれでも後述のstrtofのほうがやはりずっと速い結果にはなりました。
//————————

すると、

というお返事を頂きました!

なるほど確かにstd::stofの仕様にも

The function uses strtod (or wcstod) to perform the conversion (see strtod for more details on the process).

と書かれておりました。

参考記事:
stof – C++ Reference – cplusplus.com

今回はfloatにしたいので、strtodではなくstrtofを使えば良さそうです。
と、いうわけで、strtofの仕様を読みながら書けば終わりです。
strtof – C++ Reference – cplusplus.com

・第一引数const char* strに小数以外の文字列が含まれる場合、且つ、第二引数char** endptrにNULL以外の値を与えたとき、”A pointer to the rest of the string after the last valid character is stored in the object pointed by endptr.”とのことで、endptrにその文字列が入る。
・float値の範囲に収まらない場合は、-HUGE_VALF or HUGE_VALFを返す。

あたりを意識すれば書けそうです。

24~30行目がポイントで、小数のあとに空白のみが続いている場合は49, 50行目にあるようにきちんと小数とみなされ、小数のあとに小数でない文字列が続くときは、最初のstd::stof with try / catchでは小数とされてしまったものが、51行目にあるように小数でないとみなされるようになりました。
ちなみに上記と同じ約10万個のfloat文字列をfloat型に変換するのに505 msec程度で終わりました。約0.5秒です。
std::stofよりも若干速くなったのは、throwが無くなったから?なのでしょうか??

これでUnreal Engineでも高速且つ安全にstd::string -> floatへの変換処理が出来ますね!
ちなみに自分が調べた限りでは、Unreal EngineでFString -> floatに「安全に」変換する関数は無かったようでした
近いものでbool FString::IsNumeric()という関数はあるのですが、これは整数値のみに有効です。

参考記事:
FString::IsNumeric | Unreal Engine

コメントを残す

メールアドレスが公開されることはありません。