C言語でユークリッド距離を計算するhypot/hypotf/hypotl関数の使い方と自作関数を教えて!
こういった悩みにお答えします.
本記事の信頼性
- リアルタイムシステムの研究歴12年.
- 東大教員の時に,英語でOS(Linuxカーネル)の授業.
- 2012年9月~2013年8月にアメリカのノースカロライナ大学チャペルヒル校(UNC)コンピュータサイエンス学部で客員研究員として勤務.C言語でリアルタイムLinuxの研究開発.
- プログラミング歴15年以上,習得している言語: C/C++,Python,Solidity/Vyper,Java,Ruby,Go,Rust,D,HTML/CSS/JS/PHP,MATLAB,Assembler (x64,ARM).
- 東大教員の時に,C++言語で開発した「LLVMコンパイラの拡張」,C言語で開発した独自のリアルタイムOS「Mcube Kernel」をGitHubにオープンソースとして公開.
- 2020年1月~現在はアメリカのノースカロライナ州チャペルヒルにあるGuarantee Happiness LLCのCTOとしてECサイト開発やWeb/SNSマーケティングの業務.2022年6月~現在はアメリカのノースカロライナ州チャペルヒルにあるJapanese Tar Heel, Inc.のCEO兼CTO.
- 最近は自然言語処理AIとイーサリアムに関する有益な情報発信に従事.
- (AI全般を含む)自然言語処理AIの論文の日本語訳や,AIチャットボット(ChatGPT,Auto-GPT,Gemini(旧Bard)など)の記事を50本以上執筆.アメリカのサンフランシスコ(広義のシリコンバレー)の会社でプロンプトエンジニア・マネージャー・Quality Assurance(QA)の業務委託の経験あり.
- (スマートコントラクトのプログラミングを含む)イーサリアムや仮想通貨全般の記事を200本以上執筆.イギリスのロンドンの会社で仮想通貨の英語の記事を日本語に翻訳する業務委託の経験あり.
こういった私から学べます.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!
本記事は,以下の記事を理解していることを前提とします.
目次
ユークリッド距離を計算するhypot/hypotf/hypotl関数
1 2 3 |
double hypot(double x, double y); float hypotf(float x, float y); long double hypotl(long double x, long double y); |
hypot/hypotf/hypotl関数は,原点と点\((x, y)\)のユークリッド距離\(\sqrt{x^2 + y^2}\)を返します.
hypot/hypotf/hypotl関数の使い方
hypot/hypotf/hypotl関数の使い方は以下になります.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #include <math.h> #include <float.h> int main(void) { long double ld; double d; float f; d = 100.0; printf(" hypot(%15le, %15le) = %15le\n", d, d, hypot(d, d)); d = DBL_MAX / sqrt(2.0); printf(" hypot(%15le, %15le) = %15le\n", d, d, hypot(d, d)); d = DBL_MAX / sqrt(1.9); printf(" hypot(%15le, %15le) = %15le\n", d, d, hypot(d, d)); f = 100.0; printf("hypotf(%15e, %15e) = %15e\n", f, f, hypotf(f, f)); f = FLT_MAX / sqrtf(2.0); printf("hypotf(%15e, %15e) = %15e\n", f, f, hypotf(f, f)); f = FLT_MAX / sqrtf(1.9); printf("hypotf(%15e, %15e) = %15e\n", f, f, hypotf(f, f)); ld = 100.0; printf("hypotl(%15Le, %15Le) = %15Le\n", ld, ld, hypotl(ld, ld)); ld = LDBL_MAX / sqrtl(2.0); printf("hypotl(%15Le, %15Le) = %15Le\n", ld, ld, hypotl(ld, ld)); ld = LDBL_MAX / sqrtl(1.9); printf("hypotl(%15Le, %15Le) = %15Le\n", ld, ld, hypotl(ld, ld)); return 0; } |
実行結果は以下になります.
7行目のfloat型のhypotf関数の計算結果(xとyが両方とも\({\rm FLT\_MAX} / \sqrt{2.0}\))が無限大(inf)になっていることがわかります.
これに対して,4,10行目のdouble/long double型のhypot/hypotl関数の計算結果(xとyが両方とも \({\rm DBL\_MAX} / \sqrt{2.0}\),xとyが両方とも \({\rm LDBL\_MAX} / \sqrt{2.0}\))が無限大(inf)になっていません.
1 2 3 4 5 6 7 8 9 10 11 |
$ gcc hypot.c -lm $ a.out hypot( 1.000000e+02, 1.000000e+02) = 1.414214e+02 hypot( 1.271161e+308, 1.271161e+308) = 1.797693e+308 hypot( 1.304184e+308, 1.304184e+308) = inf hypotf( 1.000000e+02, 1.000000e+02) = 1.414214e+02 hypotf( 2.406160e+38, 2.406160e+38) = inf hypotf( 2.468668e+38, 2.468668e+38) = inf hypotl( 1.000000e+02, 1.000000e+02) = 1.414214e+02 hypotl( 8.412672e+4931, 8.412672e+4931) = 1.189731e+4932 hypotl( 8.631219e+4931, 8.631219e+4931) = inf |
hypot/hypotf/hypotl関数の自作関数
hypot/hypotf/hypotl関数の自作関数は,主に以下の3つの方法があります.
- ピタゴラスの定理(三平方の定理)
- オーバーフローをできる限り発生させないように工夫
- Moler-Morrison法
hypot/hypotf/hypotl関数の自作関数は以下のように実装します.
- myhypot/myhypotf/myhypotl関数:ピタゴラスの定理(三平方の定理)
- myhypot2/myhypotf2/myhypotl2関数:オーバーフローをできる限り発生させないように工夫
- myhypot3/myhypotf3/myhypotl3関数:Moler-Morrison法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 |
/* * Author: Hiroyuki Chishiro * License: 2-Clause BSD */ #include <stdio.h> #include <math.h> #include <float.h> #define DBL_LOOP_NUM 3 #define FLT_LOOP_NUM 2 #define LDBL_LOOP_NUM 4 double myhypot(double x, double y) { return sqrt(x * x + y * y); } float myhypotf(float x, float y) { return sqrtf(x * x + y * y); } long double myhypotl(long double x, long double y) { return sqrtl(x * x + y * y); } double myhypot2(double x, double y) { double t; if (fabs(x) <= DBL_EPSILON) { return fabs(y); } if (fabs(y) <= DBL_EPSILON) { return fabs(x); } if (fabs(y) > fabs(x)) { t = x / y; return fabs(y) * sqrt(1 + t * t); } else { t = y / x; return fabs(x) * sqrt(1 + t * t); } } float myhypotf2(float x, float y) { float t; if (fabsf(x) <= FLT_EPSILON) { return fabsf(y); } if (fabsf(y) <= FLT_EPSILON) { return fabsf(x); } if (fabsf(y) > fabsf(x)) { t = x / y; return fabsf(y) * sqrtf(1 + t * t); } else { t = y / x; return fabsf(x) * sqrtf(1 + t * t); } } long double myhypotl2(long double x, long double y) { long double t; if (fabsl(x) <= LDBL_EPSILON) { return fabsl(y); } if (fabsl(y) <= LDBL_EPSILON) { return fabsl(x); } if (fabsl(y) > fabsl(x)) { t = x / y; return fabsl(y) * sqrtl(1 + t * t); } else { t = y / x; return fabsl(x) * sqrtl(1 + t * t); } } double myhypot3(double x, double y) { int i; double t; x = fabs(x); y = fabs(y); if (x < y) { t = x; x = y; y = t; } if (y <= DBL_EPSILON) { return x; } for (i = 0; i < DBL_LOOP_NUM; i++) { t = y / x; t *= t; t /= 4 + t; x += 2 * x * t; y *= t; } return x; } float myhypotf3(float x, float y) { int i; float t; x = fabsf(x); y = fabsf(y); if (x < y) { t = x; x = y; y = t; } if (y <= FLT_EPSILON) { return x; } for (i = 0; i < FLT_LOOP_NUM; i++) { t = y / x; t *= t; t /= 4 + t; x += 2 * x * t; y *= t; } return x; } long double myhypotl3(long double x, long double y) { int i; long double t; x = fabsl(x); y = fabsl(y); if (x < y) { t = x; x = y; y = t; } if (y <= LDBL_EPSILON) { return x; } for (i = 0; i < LDBL_LOOP_NUM; i++) { t = y / x; t *= t; t /= 4 + t; x += 2 * x * t; y *= t; } return x; } int main(void) { long double ld; double d; float f; d = 100.0; printf(" myhypot(%15le, %15le) = %15le\n", d, d, myhypot(d, d)); printf(" myhypot2(%15le, %15le) = %15le\n", d, d, myhypot2(d, d)); printf(" myhypot3(%15le, %15le) = %15le\n", d, d, myhypot3(d, d)); d = DBL_MAX / sqrt(2.0); printf(" myhypot(%15le, %15le) = %15le\n", d, d, myhypot(d, d)); printf(" myhypot2(%15le, %15le) = %15le\n", d, d, myhypot2(d, d)); printf(" myhypot3(%15le, %15le) = %15le\n", d, d, myhypot3(d, d)); d = DBL_MAX / sqrt(1.9); printf(" myhypot(%15le, %15le) = %15le\n", d, d, myhypot(d, d)); printf(" myhypot2(%15le, %15le) = %15le\n", d, d, myhypot2(d, d)); printf(" myhypot3(%15le, %15le) = %15le\n", d, d, myhypot3(d, d)); f = 100.0; printf(" myhypotf(%15e, %15e) = %15e\n", f, f, myhypotf(f, f)); printf("myhypotf2(%15e, %15e) = %15e\n", f, f, myhypotf2(f, f)); printf("myhypotf3(%15e, %15e) = %15e\n", f, f, myhypotf3(f, f)); f = FLT_MAX / sqrtf(2.0); printf(" myhypotf(%15e, %15e) = %15e\n", f, f, myhypotf(f, f)); printf("myhypotf2(%15e, %15e) = %15e\n", f, f, myhypotf2(f, f)); printf("myhypotf3(%15e, %15e) = %15e\n", f, f, myhypotf3(f, f)); f = FLT_MAX / sqrtf(1.9); printf(" myhypotf(%15e, %15e) = %15e\n", f, f, myhypotf(f, f)); printf("myhypotf2(%15e, %15e) = %15e\n", f, f, myhypotf2(f, f)); printf("myhypotf3(%15e, %15e) = %15e\n", f, f, myhypotf3(f, f)); ld = 100.0; printf(" myhypotl(%15Le, %15Le) = %15Le\n", ld, ld, myhypotl(ld, ld)); printf("myhypotl2(%15Le, %15Le) = %15Le\n", ld, ld, myhypotl2(ld, ld)); printf("myhypotl3(%15Le, %15Le) = %15Le\n", ld, ld, myhypotl3(ld, ld)); ld = LDBL_MAX / sqrtl(2.0); printf(" myhypotl(%15Le, %15Le) = %15Le\n", ld, ld, myhypotl(ld, ld)); printf("myhypotl2(%15Le, %15Le) = %15Le\n", ld, ld, myhypotl2(ld, ld)); printf("myhypotl3(%15Le, %15Le) = %15Le\n", ld, ld, myhypotl3(ld, ld)); ld = LDBL_MAX / sqrtl(1.9); printf(" myhypotl(%15Le, %15Le) = %15Le\n", ld, ld, myhypotl(ld, ld)); printf("myhypotl2(%15Le, %15Le) = %15Le\n", ld, ld, myhypotl2(ld, ld)); printf("myhypotl3(%15Le, %15Le) = %15Le\n", ld, ld, myhypotl3(ld, ld)); return 0; } |
実行結果は以下になります.
全体的にオーバーフローをできる限り発生させないように工夫する方法のmyhypot2/myhypotf2/myhypotl2関数の精度が一番高いです.
Moler-Morrison法はオーバーフローするギリギリの値で計算すると結果が負の非数(-nan)になってしまい,正常に計算できていません.
16行目のmyhypotf2関数の計算結果(xとyが両方とも\({\rm FLT\_MAX} / \sqrt{2.0}\))は,hypotf関数の計算結果の無限大(inf)とは異なり,3.402823e+38と正常に計算できています.
この理由は,float型はdouble型に暗黙的に型変換して計算しているからだと考えられます.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
$ gcc myhypot.c -lm $ a.out myhypot( 1.000000e+02, 1.000000e+02) = 1.414214e+02 myhypot2( 1.000000e+02, 1.000000e+02) = 1.414214e+02 myhypot3( 1.000000e+02, 1.000000e+02) = 1.414214e+02 myhypot( 1.271161e+308, 1.271161e+308) = inf myhypot2( 1.271161e+308, 1.271161e+308) = 1.797693e+308 myhypot3( 1.271161e+308, 1.271161e+308) = -nan myhypot( 1.304184e+308, 1.304184e+308) = inf myhypot2( 1.304184e+308, 1.304184e+308) = inf myhypot3( 1.304184e+308, 1.304184e+308) = -nan myhypotf( 1.000000e+02, 1.000000e+02) = 1.414214e+02 myhypotf2( 1.000000e+02, 1.000000e+02) = 1.414214e+02 myhypotf3( 1.000000e+02, 1.000000e+02) = 1.414213e+02 myhypotf( 2.406160e+38, 2.406160e+38) = inf myhypotf2( 2.406160e+38, 2.406160e+38) = 3.402823e+38 myhypotf3( 2.406160e+38, 2.406160e+38) = -nan myhypotf( 2.468668e+38, 2.468668e+38) = inf myhypotf2( 2.468668e+38, 2.468668e+38) = inf myhypotf3( 2.468668e+38, 2.468668e+38) = -nan myhypotl( 1.000000e+02, 1.000000e+02) = 1.414214e+02 myhypotl2( 1.000000e+02, 1.000000e+02) = 1.414214e+02 myhypotl3( 1.000000e+02, 1.000000e+02) = 1.414214e+02 myhypotl( 8.412672e+4931, 8.412672e+4931) = inf myhypotl2( 8.412672e+4931, 8.412672e+4931) = 1.189731e+4932 myhypotl3( 8.412672e+4931, 8.412672e+4931) = -nan myhypotl( 8.631219e+4931, 8.631219e+4931) = inf myhypotl2( 8.631219e+4931, 8.631219e+4931) = inf myhypotl3( 8.631219e+4931, 8.631219e+4931) = -nan |
まとめ
C言語でユークリッド距離を計算するhypot/hypotf/hypotl関数の使い方と自作関数を紹介しました.
hypot/hypotf/hypotl関数の自作関数は,3つの方法で実装した結果,オーバーフローをできる限り発生させないように工夫する方法が一番精度が高いことがわかりました.
C言語を独学で習得することは難しいです.
私にC言語の無料相談をしたいあなたは,公式LINE「ChishiroのC言語」の友だち追加をお願い致します.
私のキャパシティもあり,一定数に達したら終了しますので,今すぐ追加しましょう!
独学が難しいあなたは,元東大教員がおすすめするC言語を学べるオンラインプログラミングスクール5社で自分に合うスクールを見つけましょう.後悔はさせません!