AtCoderの新ジャッジにおいて C言語のコンパイルオプション -lm
がついておらず、数学関数(libm) を使う事が出来ません。(引数が定数の場合最適化によって使えてしまう事もあります)そこでこれらのコードを実行するために x87 をアセンブリから使う方法やlibmを動的ロードする方法が有志によって見つけられました。
x87 使うよりも libm を自力でロードして、使う分シンボル抜く方がやりやすいような気も。というか dlopen(3) の man https://t.co/qGcwDhIud3 の例にも当にそういうのあるし
— angel (as ㌵㌤の猫) (@angel_p_57) 2023年8月9日
※まあでも -lm をコンパイル時のリンクオプションとしてつけてもらう、が妥当とは思う。流石に厳し過ぎる。 https://t.co/pHEztwIgkV
そこで、これのうち libm
の動的リロードをマクロにし、三行のスニペットにしました! LOAD_LIBM
マクロを追加すれば同様に追加することができます。
また、これらの引数/戻り値が double
以外の関数を扱いたい場合やエラー処理をきちんとしたい人はよしなに書き換えてもらえるとありがたいです。
良さげな改良がある場合は twitter などで教えてくれると喜びます(この記事からリンクを貼ったりしたいです)。
#include <dlfcn.h> #define LOAD_LIBM(FUNC) double (*FUNC)(double);__attribute__((constructor))static void loadlibm_##FUNC(){FUNC=dlsym(dlopen("libm.so.6",RTLD_LAZY),#FUNC);} LOAD_LIBM(sqrt); LOAD_LIBM(sin); LOAD_LIBM(cos);LOAD_LIBM(tan); LOAD_LIBM(atan2);
例
#include <dlfcn.h> #define LOAD_LIBM(FUNC) double (*FUNC)(double);__attribute__((constructor))static void loadlibm_##FUNC(){FUNC=dlsym(dlopen("libm.so.6",RTLD_LAZY),#FUNC);} LOAD_LIBM(sqrt); LOAD_LIBM(sin); LOAD_LIBM(cos);LOAD_LIBM(tan); LOAD_LIBM(atan2); #include<stdio.h> int main(){ double a; scanf("%lf",&a); printf("sqrt(%lf) = %lf\n", a, sqrt(a)); printf("sin(%lf) = %lf\n", a, sin(a)); printf("cos(%lf) = %lf\n", a, cos(a)); printf("atan2(%lf) = %lf\n", a, atan2(a)); }
sqrt(2.000000) = 1.414214 sin(2.000000) = 0.909297 cos(2.000000) = -0.416147 atan2(2.000000) = 1.365650