AtCoderのC言語で標準数学関数が使えなくなるようなので、x87を使えるようにしておく

# 改悪される実行環境

2023年8月6日(日)、[AtCoder](https://atcoder.jp/)で新ジャッジのテストコンテストが開催された。

[新ジャッジテストコンテスト -Algorithm- - AtCoder](https://atcoder.jp/contests/newjudge-2308-algorithm)
[新ジャッジテストコンテスト -Heuristic- - AtCoder](https://atcoder.jp/contests/newjudge-2308-heuristic)

これのHeuristicで `sqrt` 関数を使おうとした所、コンパイルエラーになった。
コードテストで発見したのでこれの提出リンクは無いが、Algorithmでも再現した。

[提出 #44342804 - 新ジャッジテストコンテスト -Algorithm-](https://atcoder.jp/contests/newjudge-2308-algorithm/submissions/44342804)

```
/usr/bin/ld: /tmp/ccQQlh1z.o: in function `main':
Main.c:(.text.startup+0x68): undefined reference to `sqrt'
/usr/bin/ld: Main.c:(.text.startup+0x88): undefined reference to `sqrt'
collect2: error: ld returned 1 exit status
```

過去の問題では普通に `sqrt` 関数を使えているので、新仕様での改悪で間違い無さそうだ。

[提出 #33735452 - デンソークリエイトプログラミングコンテスト2022（AtCoder Beginner Contest 239）](https://atcoder.jp/contests/abc239/submissions/33735452)

質問を投げようかとも思ったが、よく見ると

> <font color="red">バージョンやライブラリなどに関する要望の受付は終了いたしました</font>ので、お答え出来ません。

と[トップ](https://atcoder.jp/contests/newjudge-2308-heuristic)の注意事項で明示されている。
残念だなあ。

# 後の祭り

2023年3月～7月にかけて、言語の要望が募集されていた。

[Language Test 202301 - AtCoder](https://atcoder.jp/contests/language-test-202301)


今回の件を受けて確認すると、ここでもコンパイルエラーが再現した。

[提出 #44347622 - Language Test 202301](https://atcoder.jp/contests/language-test-202301/submissions/44347622)

とくに言語を追加してほしいという希望はなかったのであまり気にとめていなかったが、今思えばちゃんと確認しておくべきだった。

# 対策

標準ライブラリの数学関数 (たとえば `sin`、`cos`、`sqrt`、`atan2` などは使いたくなる機会がありそう) は使えなくなってしまうようだが、多くのx86環境ではx87という浮動小数点計算機能がある。
これを使うことで、標準ライブラリに頼らず、かつ自前での実装もせずに数学関数を使うことができる。

たとえば以下のサイトが役立ちそうである。

* [x87 instruction setを使う - Qiita](https://qiita.com/tobira-code/items/be9982c9a084b57182db)
* [Mirror of: Into the Void: x86 Instruction Set Reference](https://c9x.me/x86/)
* [gccのx86インラインアセンブリに関して](http://sci10.org/on_gcc_asm.html)

基本的には、以下の流れで数学関数を用いることができる。

1. [FLD命令](https://c9x.me/x86/html/file_module_x86_id_100.html)で引数をメモリからx87のスタックにコピーする
2. x87のスタック上で数学関数の計算を行う
3. [FSTP命令](https://c9x.me/x86/html/file_module_x86_id_117.html)で計算結果をx87のスタックからメモリに転送する

数学関数の計算は、以下の命令で行うことができる。

* `sin` : [FSIN命令](https://c9x.me/x86/html/file_module_x86_id_114.html)
* `cos` : [FCOS命令](https://c9x.me/x86/html/file_module_x86_id_89.html)
* `sin` と `cos` : [FSINCOS命令](https://c9x.me/x86/html/file_module_x86_id_115.html)
* `sqrt` : [FSQRT命令](https://c9x.me/x86/html/file_module_x86_id_116.html)
* `atan2` : [FPATAN命令](https://c9x.me/x86/html/file_module_x86_id_106.html)

実際に組み込んでみた。

```c
double x87_sin(double angle) {
	double res;
	__asm__ __volatile__(
		"fldl %1\n\t"
		"fsin\n\t"
		"fstpl %0\n\t"
	: "=m"(res) : "m"(angle));
	return res;
}

double x87_cos(double angle) {
	double res;
	__asm__ __volatile__(
		"fldl %1\n\t"
		"fcos\n\t"
		"fstpl %0\n\t"
	: "=m"(res) : "m"(angle));
	return res;
}

void x87_sincos(double* sin_res, double* cos_res, double angle) {
	__asm__ __volatile__(
		"fldl %2\n\t"
		"fsincos\n\t"
		"fstpl %1\n\t"
		"fstpl %0\n\t"
	: "=m"(*sin_res), "=m"(*cos_res) : "m"(angle));
}

double x87_sqrt(double value) {
	double res;
	__asm__ __volatile__(
		"fldl %1\n\t"
		"fsqrt\n\t"
		"fstpl %0\n\t"
	: "=m"(res) : "m"(value));
	return res;
}

double x87_atan2(double y, double x) {
	double res;
	__asm__ __volatile__(
		"fldl %1\n\t"
		"fldl %2\n\t"
		"fpatan\n\t"
		"fstpl %0\n\t"
	: "=m"(res) : "m"(y), "m"(x));
	return res;
}
```

この `x87_sqrt` 関数は、新環境でも使えるようである。

[提出 #44348412 - 新ジャッジテストコンテスト -Algorithm-](https://atcoder.jp/contests/newjudge-2308-algorithm/submissions/44348412)

また、旧環境ではあるが、`sin`、`cos`、`sqrt`、`atan2` 関数を用いている以前の提出

[提出 #24617122 - 競プロ典型 90 問](https://atcoder.jp/contests/typical90/submissions/24617122)

の数学関数を今回用意した関数に置き換えても、正解になった。

[提出 #44348463 - 競プロ典型 90 問](https://atcoder.jp/contests/typical90/submissions/44348463)

# 免責

最低限の動作確認は行ったが、今回用意した関数が正しく動作する保証はできない。
さらに、AtCoderのルールや[Language Test](https://atcoder.jp/contests/language-test-202301)のスプレッドシートを確認したが、CPUにx86が使われるという保証は発見できなかった。
x86でないCPUが使われた場合、今回用意した関数は利用できなくなる可能性が高いと思われる。
<font color="lightgray">(エミュレーションなどが行われる可能性もあるため、利用できないと断言はできない)</font>
今回用意した関数を使用した結果、コンテストにおけるペナルティやタイムロスなどの不利益、またはその他の損害が発生しても、筆者は責任を負わない。
使用する場合は自己責任でお願いする。

# 教訓

次に言語のアップデートがあるときは、ちゃんと確認しよう。
今回、今までgccとclangの2種類あったC言語がgccだけに減らされているし、放置すると次は0種類にされるかもしれない…