数値→文字列→数値したときのずれ
C#で、float値を、ToString()して、float.Parseすると若干のずれがあります。
どの程度ずれるのかについて書きました。
当然、これはずれる場合とずれない場合があるのですが、今回はUnityのRandam値で検証しました。
最初に結論
結論から言うと、利用する値の範囲によって変わります。
-9999~9999ぐらいの値の範囲であれば、±0.0009765625ぐらいのずれでした。
-999~999ぐらいの値の範囲だと、6.103516E-05ぐらいですね。
これらの値は、アバウトなので参考程度に。
※コメント欄にToString("G9")すれば、改善するという指摘がありました。
ありがとうございます。
恐ろしいことに、全くずれなくなりました。ずれが0です。
ということで、下記の記事は普通に引数無しのToString()した場合の記事です。
ToString("G9")にしたら、こんな余計なことする必要ないのかもしれません。
どうやって調べたか
かなり適当なんですが、コードです。
float値の2つの値の範囲って、無限にあるのでランダムでやってます。
毎フレーム1000回試してみて、最大誤差が現れた時にDebug.Logしています。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StringToNumber : MonoBehaviour
{
float _minDif = 0;
float _maxDif = 0;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
for (int i = 0; i < 1000; ++i)
{
float f = Random.Range(-9999f, 9999f);
string s = f.ToString();
float v = float.Parse(s);
float d = v - f;
if (d < 0f)
{
if (d < _minDif)
{
_minDif = d;
Debug.Log("MIN DIF:" + _minDif);
}
}
else
{
if (d > _maxDif)
{
_maxDif = d;
Debug.Log("MAX DIF:" + _maxDif);
}
}
}
}
}
誤差があると何が困るのか
実際に起きた問題として、オブジェクトの位置を保存する場合です。
このオブジェクトは、rangeMinからrangeMaxの間にしか置けないものでした。
範囲外だと問題がでている状態になって、灰色で表示されるようになります。
範囲ぎりぎりの地点は、位置をスナップできるようにしてあったとします。
仮に、rangeMaxの地点で、位置を文字列として保存しました。
で、ロードし直すと誤算によって問題がでている状態になっていることがあるわけです。
対策1
誤差を許容しましょう。
-9999~9999ぐらいの値の範囲であれば、±0.0009765625ぐらいのずれでした。
なので、0.001ぐらいのずれは許容するようなコードにする。
(私だったら怖いので、0.01ぐらいにしますが)
bool rangeCheck(float value)
{
float eps = 0.001;
if ((rangeMin-eps<value) && (value<rangeMax+eps)) return true;
return false;
}
対策2
バイナリで保存する。
結論
すごくどうでも良い話だったかもしれませんが、文字列化して数値に戻すと少しだけ数値が変わるので、少しだけ許容幅を設けよう。
その際の許容範囲ってどのぐらいにしたら良いのかって話でした。
文字列で数値を保存するのって便利だけど、問題がでることもあるよねって話です。
(というか実際に問題が出たので、記事に残しておこうと思うのでした)