🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

2回答

695閲覧

交差判定をするプログラムで座標を表示されると誤差がでる時と出ない時がる理由が知りたいfloat型で

退会済みユーザー

退会済みユーザー

総合スコア0

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

0クリップ

投稿2019/12/25 07:58

提示コード /線分の交差判定 平行はfalse/部のprintf()で座標を表示されているのですが何故か誤差が出てしまいます。
A(2,4),B(8,4),C(5,5),D(5,2), Ab線分 CD 線分でこのコードでは5,4と表示され小数点以下はなしです。.0000
ですがこの線分を逆にしてAB をCD 線分にして CD をAB線分にすると誤差が出てしまいます。結果は5,3.2000という結果になってしまいます。
これはなぜでしょうか?float型でやっているのでその以前で細かい値が捨てられてるということは考えにくいです。
また交差しているので同じ座標がかえてっくるはずです。方眼紙で線分交差座標を作っているのでこの線分そもそもがおかしいというのは考えられないです。

#include <iostream> #include "stdio.h" #include "conio.h" #include <optional> //using namespace std; class Position { public: float x; float y; Position(float xx = 0, float yy = 0) { x = xx; y = yy; } bool operator==(const Position& t) { if(x == t.x && y == t.y) { return true; }else{ return false; } } }; /*交差判定と座標を表示*/ void cross_pos(Position a,Position b,Position c,Position d) { //tc = (x1 - x2) * (y3 - y1) + (y1 - y2)* (x1 - x3); float tc = (a.x - b.x) * (c.y - a.y) + (a.y - b.y) * (a.x - c.x); //td = (x1 - x2) * (y4 - y1) + (y1 - y2)* (x1 - x4); float td = (a.x - b.x) * (d.y - a.y) + (a.y - b.y) * (a.x - d.x); //ta = (x3 - x4) * (y1 - y3) + (y3 - y4) * (x3 - x1) float ta = (c.x - d.x) * (a.y - c.y) + (c.y - d.y) * (c.x - a.x); //tb = (x3 - x4) * (y2 - y3) + (y3 - y4) * (x3 - x2) float tb = (c.x - d.x) * (b.y - c.y) + (c.y - d.y) * (c.x - b.x); Position ab, ac, bc, bd, ad, cd, cb, db; /*線分を取得*/ ab = Position(b.x - a.x, b.y - a.y); ad = Position(d.x - a.x, d.y - a.y); ac = Position(c.x - a.x, c.y - a.y); bc = Position(c.x - b.x, c.y - b.y); cb = Position(b.x - c.x, b.y - c.y); bd = Position(d.x - b.x, d.y - b.y); cd = Position(d.x - c.x, d.y - c.y); db = Position(b.x - d.x, b.y - d.y); /*外積を使ってスカラーを取得*/ float BD_AB = (bd.x * ab.y) - (ab.x * bd.y); float CB_DB = (cb.x * bd.y) - (bd.x * cb.y); float BD_BC = (bd.x * bc.y) - (bc.x * bd.y); float AD_CD = (ad.x * cd.y) - (cd.x * ab.y); float BC_BD = (bd.x * bc.y) - (bc.x * bd.y); float BD_AD = (ad.x * bd.y) - (bd.x * ad.y); BD_AB /= 2; BD_BC /= 2; AD_CD /= 2; BC_BD /= 2; BD_AD /= 2; CB_DB /= 2; /*線分の交差判定 平行はfalse*/ if (tc * td < 0 && ta * tb < 0) { printf("交差している。\n"); /*比率*/ float k = AD_CD / (AD_CD + CB_DB); float x = a.x + k * (b.x - a.x); float y = a.y + k * (b.y - a.y); printf("x: %f\n",x); printf("y: %f\n",y); } } int main() { Position a, b, c, d; int t = 0; printf("a.x: "); t = scanf_s("%f", &a.x); printf("a.y: "); t = scanf_s("%f", &a.y); printf("\n"); printf("b.x: "); t = scanf_s("%f", &b.x); printf("b.y: "); t = scanf_s("%f", &b.y); printf("\n"); printf("c.x: "); t = scanf_s("%f", &c.x); printf("c.y: "); t = scanf_s("%f", &c.y); printf("\n"); printf("d.x: "); t = scanf_s("%f", &d.x); printf("d.y: "); t = scanf_s("%f", &d.y); printf("\n"); cross_pos(a, b, c, d); int _ch = _getch(); return 0; }

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

C++

1float AD_CD = (ad.x * cd.y) - (cd.x * ab.y); // ab?

投稿2019/12/25 08:08

ozwk

総合スコア13551

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ozwk

2019/12/25 08:13

他にも float ①_② = (①.x * ②.y) - (②.x * ①.y); という規則性に沿ってない部分が何箇所かあるので怪しいです。
ozwk

2019/12/25 08:18

ちなみに今回直接の原因ではありませんでしたが 「float型でやっているのでその以前で細かい値が捨てられてるということは考えにくい」 というのは誤りで、細かいの想定によりますが、思いっきり捨てられます。
fana

2019/12/25 08:36

とりあえずPosition型同士の加減算やスカラー倍,外積計算あたりを,operatorなり関数なりにすることを最初に(この「交差判定」の実装に取り掛かるよりも前に)行うべき.こういうミスが抑制されるように.
fana

2019/12/25 08:52

(なんか前にも同じようなことを書いた記憶があるけど)比率kを求める箇所に危うげな雰囲気を感じる.コレ,どんな入力が来ても正負が問題ない演算にうまいことなっているのだろうか?
退会済みユーザー

退会済みユーザー

2019/12/25 14:29

一応、方眼紙に適当な交差線などを作っていろいろ試しましたが何事もなく動いているのですが自分は初心者なので細かい演算どうこうの話は全く分からないのですが 何を変更すべきか教えて欲しいです。
fana

2019/12/26 01:22

float k = AD_CD / (AD_CD + CB_DB); のところで,AD_CDとCB_DBの符号が異なってしまうような入力パターンというのが存在しないのかどうか…? という話(杞憂かもしれない). (コメントされているようにAD_CDとCB_DBで変数の名付けルールが一貫していないように見えるけども,CB_DBの算出にdbを用いるように変更すると,符号が逆転してうまく結果が出なくなる.このことを把握した上でbdを使っている?)
guest

0

float型でやっているのでその以前で細かい値が捨てられてるということは考えにくいです。

いえ、(一般的なパソコンで使われるIEEE 754の2進表現の場合)float型は0.1すら正確に表現できません

投稿2019/12/25 08:02

maisumakun

総合スコア145975

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/12/25 08:03

では一体なぜ結果が変わるのでしょうか?
maisumakun

2019/12/25 08:04

1の位以下も0.5、0.25…というように2進法で表現されますが、0.1は無限小数となってしまうので、正確に表現できません。
退会済みユーザー

退会済みユーザー

2019/12/25 08:06

いえ、提示コードの内容なのですが、これは最初の回答より誤差がでてしまうのでどうにもならないのでしょうか?
maisumakun

2019/12/25 08:07

> では一体なぜ結果が変わるのでしょうか? 計算結果がfloatで表せない範囲に入ると、そのたびに下位の桁での丸めが発生します。計算順序が変われば、どこで丸めが行われるかも変化するので、結果は微妙に変化します。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.36%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問