2011-07-14 12 views
2

私のiOSアプリケーションでは、CGPointsで構築されたシェイプクラスがあります。私はencodeCGPoint:forKeyを使用してファイルに保存します。私はそれをすべて読んで読んだ。NSCoderでCGPointを完全精度で符号化する

しかし、私が読み込んだCGPoint値は、私が保存した値と正確には同じではありません。 CGFloat値の下位ビットは安定していません。したがって、CGPointEqualToPointはNOを返します。つまり、isEqualメソッドはNOを返します。これは私にトラブルと痛みを引き起こします。

明らかに、浮動小数点数を正確にシリアライズすることは、時間の始めから面倒です。しかし、この状況では、最善のアプローチは何ですか?私はいくつか考えることができます:

  • encodeFloatを使用して、xとyの値を書き出す代わりにencodeCGPoint(全くその役立つだろう?)
  • (彼らはしているそれらを保存する前に256.0で、私のxとyの値を乗算します
  • encodeCGPointの代わりにencodeDoubleを使用してxとyの値を書き出します(最下位ビットを間違って丸めてもかまいません)。
  • NSUIntegerにキャストします。 encodeInt32を使ってそれらを書き出してください(うまくいくでしょうが)。
  • 精度の、そして内-イプシロンの比較ではなく、CGPointEqualToPoint(ため息)

EDIT-ADDを使用するために私のisEqualメソッドを実装します。だから私は簡単にするために除外された問題、の後半は、ありますこれらのシェイプオブジェクトのハッシュメソッドを実装する必要があります。

ハッシング浮動小数点はでも恐ろしい痛み(「Good way to hash a float vector?」参照)、それは多かれ少なかれ私の質問を無効にします。ツールキットのencodeCGPointメソッドは、浮動小数点値を迷惑な方法で丸めます。文字通り%g形式の文字列にそれらを出力します。したがって、私はそれを使用してもハッシュを信頼できる方法ではありません。

したがって、独自のencodePoint関数を作成する必要があります。私がそれをしている限り、値を正確に符号化するものを書くかもしれません。 (2つの32ビット浮動小数点を64ビット整数フィールドにコピーしてください。いいえ、ポータブルではありませんが、これはiOS専用であり、そのトレードオフです)

CGPointsを正確に正確に格納すると、正確な比較と私が望む古いハッシュ関数に戻ります。許容範囲は私のために何もしないので、私はこのアプリケーションに使用していません。

私は寛容の比較をハッシュたい場合、私はN 有効数字、ない一定の距離イプシロンの許容範囲内の値を比較することと思います。 (すなわち、私は0.123456を0.123457の近くで比較したいと思いますが、1234.56を1234.57の近くで比較したいと思っています。)これは、大きな値と小さい値の両方で浮動小数点数学エラーに対して安定します。私はそれのためのサンプルコードを持っていないが、frexpf()関数から始まり、あまりにも難しくはありません。

答えて

1

通常、浮動小数点数を直接比較するのは適切なゲームプランではありません。 many other optionsのいずれかを試してください。あなたの問題に対する最善の解決策はおそらくあなたの最後の提案です。私はなぜそこに "一息"があるのか​​分かりません。倍精度浮動小数点数は、精度が約16桁の十桁です。プログラムが実際には実際には実際には実際に使用されない可能性は非常に高いです。には、それほど高い精度が必要です。

+0

をカール、CGFloat()はiOSで倍精度ではありません。しかし、私は、CGPointEqualToPointが座標のビット単位の比較を行うことはあまり有用ではないことに同意する必要があります。独自のisEqualをロールして、CGPointEqualToPointから明確にしてください。これは、座標を直接コピーしている場合にのみ有効です。ありがとうSteven; –

+0

私は実際に実際のタイプを調べなかった。同じ種類の推論が立っている。単精度浮動小数点数でも7桁の小数精度がありますが、OPの目的には十分に*得られています。 –

+0

ええと、7桁の数字は、あなたが表示したい画面のピクセルの近くにUIView *を信じられないほど近づけます;-) –

1

floatとdoubleの間の暗黙的な変換があるときはいつでも「CGFloat値の低ビットが安定していません」という問題があるため、εメソッドを使用します(フレームワークコードの場合があります)。tgmath.hは、コード)

Iは、以下の機能(すなわちCGGeometryための一般的な場合に有用だから公差は0.5にデフォルト設定)を使用します。

BOOL OTValueNearToValueWithTolerance(CGFloat v1, CGFloat v2, CGFloat tolerance) 
{ 
    return (fabs(v1 - v2) <= tolerance); 
} 

BOOL OTPointNearToPointWithTolerance(CGPoint p1, CGPoint p2, CGFloat tolerance) 
{ 
    return (OTValueNearToValueWithTolerance(p1.x, p2.x, tolerance) && OTValueNearToValueWithTolerance(p1.y, p2.y, tolerance)); 
} 

BOOL OTSizeNearToSizeWithTolerance(CGSize s1, CGSize s2, CGFloat tolerance) 
{ 
    return (OTValueNearToValueWithTolerance(s1.width, s2.width, tolerance) && OTValueNearToValueWithTolerance(s1.height, s2.height, tolerance)); 
} 

BOOL OTRectNearToRectWithTolerance(CGRect r1, CGRect r2, CGFloat tolerance) 
{ 
    return (OTPointNearToPointWithTolerance(r1.origin, r2.origin, tolerance) && OTSizeNearToSizeWithTolerance(r1.size, r2.size, tolerance)); 
} 

BOOL OTValueNearToValue(CGFloat v1, CGFloat v2) 
{ 
    return OTValueNearToValueWithTolerance(v1, v2, 0.5); 
} 

BOOL OTPointNearToPoint(CGPoint p1, CGPoint p2) 
{ 
    return OTPointNearToPointWithTolerance(p1, p2, 0.5); 
} 

BOOL OTSizeNearToSize(CGSize s1, CGSize s2) 
{ 
    return OTSizeNearToSizeWithTolerance(s1, s2, 0.5); 
} 

BOOL OTRectNearToRect(CGRect r1, CGRect r2) 
{ 
    return OTRectNearToRectWithTolerance(r1, r2, 0.5); 
} 

BOOL OTPointNearToEdgeOfRect(CGPoint point, CGRect rect, CGFloat amount, CGRectEdge edge) 
{ 
    CGRect nearRect, otherRect; 
    CGRectDivide(rect, &nearRect, &otherRect, amount, edge); 
    return CGRectContainsPoint(nearRect, point); 
} 
関連する問題