4

次のコードでは、奇妙な現象に遭遇しました。ライン上のランではif {$ma == $mb}両方ともmb同等maでし1.0たが、ifは取られませんでした。==toeqまたはを変更したときに[cequal $ma $mb]ifが取られました。

1.0また、 (if {$ma == 1.0}および)を使用して変数の1つを変更しようとするとif {$1.0 == $mb}、ifも使用されませんでしたが、式を使用したifif {1.0 ==1.0}が使用されます。

==に変更した後に私に起こったもう1つのことeqは、を使用する0.0-0.0等しくないeqが、を使用すると等しいということ==です。

それらの違いの原因は何ですか?

浮動小数点数の比較では、小さなイプシロンを使用して2つの数値が本当に近いかどうかを確認する方がよいことは承知していますが、この場合、ゼロによる除算を避けるために比較が行われます。

コード:

proc geometry_intersect_two_sections {xa1 ya1 xa2 ya2 xb1 yb1 xb2 yb2} {
    if {($xa1 == $xa2) && ($xb1 == $xb2)} {
        return {}
    }

    if {!($xa1 == $xa2)} {
        set ma [expr (($ya1-$ya2)*1.0)/($xa1-$xa2)]
        set na [expr $ya1 - ($ma * 1.0 * $xa1)]
    }

    if {!($xb1 == $xb2)} {
        set mb [expr (($yb1-$yb2)*1.0)/($xb1-$xb2)]
        set nb [expr $yb1 - ($mb * 1.0 * $xb1)]
    }

    if {$xa1 == $xa2} {
        set retx [expr $xa1 * 1.0]
        set rety [expr $retx * 1.0 * $mb + $nb]
        if {($rety <= [max $yb1 $yb2]) && ($rety >= [min $yb1 $yb2]) && ($rety <= [max $ya1 $ya2]) && ($rety >= [min $ya1 $ya2]) && \
            ($retx <= [max $xb1 $xb2]) && ($retx >= [min $xb1 $xb2]) && ($retx <= [max $xa1 $xa2]) && ($retx >= [min $xa1 $xa2])} {ety]
        } else {
            return {}
        }
    }

    if {$xb1 == $xb2} {
        set retx [expr $xb1 * 1.0]
        set rety [expr $retx * 1.0 * $ma + $na]
        if {($rety <= [max $yb1 $yb2]) && ($rety >= [min $yb1 $yb2]) && ($rety <= [max $ya1 $ya2]) && ($rety >= [min $ya1 $ya2]) && \
            ($retx <= [max $xb1 $xb2]) && ($retx >= [min $xb1 $xb2]) && ($retx <= [max $xa1 $xa2]) && ($retx >= [min $xa1 $xa2])} {
            return [list $retx $rety]
        } else {
            return {}
        }
    }

    if {$mb == $ma} {
        return {}
    }

    set retx [expr 1.0 * ($na - $nb)/($mb - $ma)]
    set rety [expr 1.0 * ($ma * $retx) + $na]
    if {($rety <= [max $yb1 $yb2]) && ($rety >= [min $yb1 $yb2]) && ($rety <= [max $ya1 $ya2]) && ($rety >= [min $ya1 $ya2]) && \
        ($retx <= [max $xb1 $xb2]) && ($retx >= [min $xb1 $xb2]) && ($retx <= [max $xa1 $xa2]) && ($retx >= [min $xa1 $xa2])} {
        return [list $retx $rety]
    } else {
        return {}
    }
4

3 に答える 3

6

IEEE 浮動小数点値 (CPU ハードウェアでサポートされているため、Tcl が内部的に使用する) は、値を正確に表していないという特徴を持つことがよく知られています。とにかく最初の概算に。それらは固定ビット数 ( doubleTcl が使用する 64) を持っているのとまったく同じように値を表しますが、それらが表す値は、あなたが思っているものとはわずかに異なる可能性があります (多くの値は、1/3が 10 進数でほぼ 0.333333333 であるが正確ではないのと同じように、2 進数の固定数;まったく同じ問題ですが、別の基数です)。

Tcl では、表示目的でこの問題を回避するためにいくつかの限定的な手順を実行しています。8.5 以降では、正確な値を再度取得するために必要な最小桁数で浮動小数点数をレンダリングし、8.4 以前では、数値を出力するときに単純に少ない桁数を使用します (17 桁ではなく最大 15 桁)。これは正確な表現を行うために必要です) マジックtcl_precision変数を介して制御できます。ただし、その変数を設定しないでください。値自体ではなく、値を文字列にレンダリングすることがすべてであるため、必要なことはしません。代わりに、別の (そして非常によく知られている) 戦略を使用する必要があります: equal-within-epsilon です。

# Magic value! This one is OK for values in the range of small integers
proc equal_float {a b {epsilon 1e-15}} {
    return [expr {abs($a - $b) < $epsilon}]
}

次に、次のように使用します。

# Instead of: if {$x == 42.3} { ... }
if {[equal_float $x 42.3]} { ... }

これの別の結果は、繰り返しの目的で浮動小数点数を使用してはならないことに注意してください。これにより、エラーが累積してイプシロンを超える可能性があります。0 から 25 まで 0.1 のステップで移動する代わりに、整数のステップで 0 から 250 まで移動してから、0.1 を掛けて float 値を導き出します。

于 2012-10-03T09:10:46.750 に答える
4

あなたの質問に直接答えるために==、2つの数が等しくないときはいつでも失敗します。

eqあなたの質問では、それはとは異なる結果をもたらしたとあなたは言います==。これはeq、比較が行われる前に値が文字列に変換されるためです。浮動小数点値を文字列に変換するときは、ある値に切り上げる必要があります。特定の範囲内にある2つの浮動小数点数の場合、それらは同じ値に丸められ、まったく同じ文字列が生成されます。

于 2012-10-02T16:42:40.997 に答える
2

==数値比較をeq強制し、文字列比較を強制します(そして、cequalメモリが私に役立つ場合、あなたはTclxから来て、本質的にと同じセマンティクスを持っていますeq)。

厳密な等式の浮動小数点値の比較に関する問題は、これらの型が正確な値を保持しないため(整数とは異なり)、設計上欠陥があります。2つのフロートを比較するための賢明なアプローチの1つは、特定の(非常に小さい)定数「イプシロン」を定義し、それらの差の絶対値がその「イプシロン」よりも小さいかどうかを確認することです。それより少ない場合は、2つのフロートが同じであり、それ以外は同じではないことを宣言します。グーグルですばやく検索すると、このエッセイこのページ、その他の便利なリンクが見つかります。

もう1つのアプローチは、整数に固執することです(アンダーフロー/オーバーフローしないように、適切な場合は適切なアップスケーリングとダウンスケーリングを使用します)。

于 2012-10-02T16:45:29.683 に答える