// Copyright (C) 2026 Kiyotsugu Arai // SPDX-License-Identifier: LGPL-3.0-or-later // FloatMath.cpp // 多倍長浮動小数点数の数学関数ライブラリの実装 // Taylor級数 + 引数半減/二乗復元による任意精度実装 #include #include #include #include #include #include #include #include #include #include #include namespace calx { //============================================================================= // ユーティリティ関数 //============================================================================= // 入力の effective_bits_ に基づき、計算精度 (10進桁数) を決定する。 // 入力精度が要求精度より十分低い場合、入力精度 + guard で計算し高速化する。 static int effectiveComputePrecision(int input_eff, int requested_precision) { // 超越関数は入力が正確な値 (e.g. 1.5 = 2ビット) でも // 要求精度分の正確な結果を返す必要がある。 // 入力の有効ビットが要求精度の半分未満の場合、精度削減を無効化する。 constexpr int GUARD = 32; int req_bits = Float::precisionToBits(requested_precision); if (input_eff >= INT_MAX || input_eff + GUARD >= req_bits) { return requested_precision; } // 入力の有効ビットが少なくても、要求精度を下回らない // (超越関数の出力精度は入力精度に制限されない) return requested_precision; } // 結果の精度フィールドを設定し、入力の有効ビットを反映する。 // input_eff < requested ならば effective_bits_ を input_eff に設定。 static void finalizeResult(Float& result, int input_eff, int precision) { result.setResultPrecision(precision); int req_bits = Float::precisionToBits(precision); if (input_eff < INT_MAX && input_eff < req_bits) { result.setEffectiveBits(input_eff); } } //============================================================================= // Ziv の反復戦略 — 超越関数の correct rounding //============================================================================= // 計算結果 y の仮数部を target_bits で丸めたとき、丸め方向が確定するか判定。 // guard_bits 個のガードビットが丸め境界から十分離れていれば true。 static bool canRoundCorrectly(const Float& y, int target_bits, int guard_bits, RoundingMode mode) { if (y.isZero() || y.isNaN() || y.isInfinity()) return true; int bit_length = static_cast(y.mantissa().bitLength()); if (bit_length <= target_bits) return true; // 丸め不要 // 判定に使えるガードビット数 (計算誤差 < 1 ulp を考慮し margin=2) constexpr int MARGIN = 2; int usable = guard_bits - MARGIN; if (usable < 1) return false; // ガードビット領域: bit 位置 [shift - guard_bits, shift - 1] // shift = bit_length - target_bits int shift = bit_length - target_bits; const uint64_t* data = y.mantissa().data(); // usable 個のガードビットを読み取る // 位置: bit (shift - 1) が最上位ガードビット, bit (shift - usable) が最下位 // これらが全 0 または全 1 なら丸め境界に近すぎる int hi_pos = shift - 1; // 最上位ガードビット位置 int lo_pos = shift - usable; // 最下位ガードビット位置 if (lo_pos < 0) lo_pos = 0; // 全ビットが 0 かチェック bool all_zero = true; // 全ビットが 1 かチェック bool all_one = true; // ワード単位でスキャン size_t lo_word = static_cast(lo_pos) / 64; size_t hi_word = static_cast(hi_pos) / 64; for (size_t w = lo_word; w <= hi_word; ++w) { uint64_t word = data[w]; // このワードで調べるビット範囲のマスクを作成 unsigned lo_bit = (w == lo_word) ? static_cast(lo_pos % 64) : 0; unsigned hi_bit = (w == hi_word) ? static_cast(hi_pos % 64) : 63; unsigned width = hi_bit - lo_bit + 1; uint64_t mask; if (width >= 64) { mask = ~uint64_t(0); } else { mask = ((uint64_t(1) << width) - 1) << lo_bit; } uint64_t bits = word & mask; if (bits != 0) all_zero = false; if (bits != mask) all_one = false; // 両方 false なら早期終了 if (!all_zero && !all_one) return true; } // 全 0 or 全 1 → 丸め境界に近すぎて方向不明 return false; } // Ziv の反復戦略テンプレート。 // computeFunc(x, eff_x, precision) → Float を呼び、correct rounding を保証する。 // computeFunc の precision は10進桁数。内部で +10 guard digits を付加する前提。 template static Float zivRound(ComputeFunc&& computeFunc, const Float& x, int eff_x, int precision) { constexpr int MAX_ITER = 6; int target_bits = Float::precisionToBits(precision); RoundingMode mode = Float::roundingMode(); int extra_guard = 10; // 初回: 10 guard digits ≈ 33 bits for (int iter = 0; iter < MAX_ITER; ++iter) { int wp = precision + extra_guard; Float y = computeFunc(x, eff_x, wp); int actual_bits = static_cast(y.mantissa().bitLength()); int guard_bits = actual_bits - target_bits; if (guard_bits > 0 && canRoundCorrectly(y, target_bits, guard_bits, mode)) { finalizeResult(y, eff_x, precision); return y; } // ガードビットを増やしてリトライ extra_guard *= 2; } // フォールバック: faithful rounding (実用上ほぼ到達しない) Float y = computeFunc(x, eff_x, precision + extra_guard); finalizeResult(y, eff_x, precision); return y; } //============================================================================= // mpn 直接計算 — Taylor 級数ループのアロケーション排除 //============================================================================= // mpn 名前空間は calx::mpn (MpnOps.hpp で定義) // 軽量浮動小数点: arena 内のポインタ + ワード数 + 指数 struct RawFloat { uint64_t* d; // 仮数部 (arena 内, d[0]=LSB) size_t nw; // 実ワード数 (先頭ゼロ除去済み) int64_t exp; // 2進指数 (値 = mantissa * 2^exp) }; // 仮数の正規化: MSB を最上位ワードのビット 63 に配置 // rf_mul/rf_divmod_1 の後に呼び出して指数の一貫性を保つ static void rf_normalize(RawFloat& a) { if (a.nw == 0) return; // 先頭ゼロワードを除去 a.nw = mpn::normalized_size(a.d, a.nw); if (a.nw == 0) return; // ビットレベル: 最上位ワードの leading zeros 分だけ左シフト unsigned lz = static_cast(std::countl_zero(a.d[a.nw - 1])); if (lz > 0) { for (size_t i = a.nw - 1; i > 0; --i) { a.d[i] = (a.d[i] << lz) | (a.d[i - 1] >> (64 - lz)); } a.d[0] <<= lz; a.exp -= static_cast(lz); } } // 乗算 + 切り詰め: dst = a * b, 上位 target_nw ワードのみ保持 static void rf_mul(RawFloat& dst, const RawFloat& a, const RawFloat& b, uint64_t* prod_buf, uint64_t* scratch, size_t target_nw) { if (a.nw == 0 || b.nw == 0) { dst.nw = 0; dst.exp = 0; return; } int64_t prod_exp = a.exp + b.exp; // mulhigh_n 最適化: 両方 target_nw サイズなら上位のみ計算 (コスト ≈ 2/3) if (a.nw == target_nw && b.nw == target_nw && target_nw >= 8) { size_t mh_scratch = mpn::mulhigh_n_scratch_size(target_nw); // scratch が足りるか確認 (multiply_scratch_size >= mulhigh_scratch_size) mpn::mulhigh_n(prod_buf, a.d, b.d, target_nw, scratch); // mulhigh_n は rp[0..n-1] = 上位 n ワード (近似、O(1) 誤差) std::memcpy(dst.d, prod_buf, target_nw * sizeof(uint64_t)); dst.nw = mpn::normalized_size(dst.d, target_nw); // mulhigh の結果は a*b >> (target_nw * 64) の近似 dst.exp = prod_exp + static_cast(target_nw) * 64; rf_normalize(dst); return; } size_t prod_n = a.nw + b.nw; mpn::multiply(prod_buf, a.d, a.nw, b.d, b.nw, scratch); size_t actual = mpn::normalized_size(prod_buf, prod_n); if (actual > target_nw) { size_t drop = actual - target_nw; std::memcpy(dst.d, prod_buf + drop, target_nw * sizeof(uint64_t)); dst.nw = mpn::normalized_size(dst.d, target_nw); dst.exp = prod_exp + static_cast(drop) * 64; } else { std::memcpy(dst.d, prod_buf, actual * sizeof(uint64_t)); dst.nw = actual; dst.exp = prod_exp; } rf_normalize(dst); } // キャッシュ付き乗算: dst = a * b, b の forward NTT をキャッシュ // b が定数で繰り返し乗算する場合に forward NTT を 1 回省略できる static void rf_mul_cached(RawFloat& dst, const RawFloat& a, const RawFloat& b, uint64_t* prod_buf, size_t target_nw, prime_ntt::NttCache& cache) { if (a.nw == 0 || b.nw == 0) { dst.nw = 0; dst.exp = 0; return; } // NTT 閾値未満は通常乗算にフォールバック size_t min_nw = std::min(a.nw, b.nw); if (min_nw < 3000) { // scratch が必要 → thread_local で確保 thread_local std::vector scratch_buf; size_t scratch_need = mpn::multiply_scratch_size( std::max(a.nw, b.nw), min_nw); if (scratch_buf.size() < scratch_need) scratch_buf.resize(scratch_need); rf_mul(dst, a, b, prod_buf, scratch_buf.data(), target_nw); return; } size_t prod_n = a.nw + b.nw; prime_ntt::mul_prime_ntt_cached(prod_buf, a.d, a.nw, b.d, b.nw, cache); size_t actual = mpn::normalized_size(prod_buf, prod_n); int64_t prod_exp = a.exp + b.exp; if (actual > target_nw) { size_t drop = actual - target_nw; std::memcpy(dst.d, prod_buf + drop, target_nw * sizeof(uint64_t)); dst.nw = mpn::normalized_size(dst.d, target_nw); dst.exp = prod_exp + static_cast(drop) * 64; } else { std::memcpy(dst.d, prod_buf, actual * sizeof(uint64_t)); dst.nw = actual; dst.exp = prod_exp; } rf_normalize(dst); } // 自乗 + 切り詰め: dst = a^2, 上位 target_nw ワードのみ保持 // mpn::square は乗算より高速 (半対角の加算を省略) static void rf_sqr(RawFloat& dst, const RawFloat& a, uint64_t* prod_buf, uint64_t* scratch, size_t target_nw) { if (a.nw == 0) { dst.nw = 0; dst.exp = 0; return; } size_t prod_n = 2 * a.nw; mpn::square(prod_buf, a.d, a.nw, scratch); size_t actual = mpn::normalized_size(prod_buf, prod_n); int64_t prod_exp = a.exp + a.exp; if (actual > target_nw) { size_t drop = actual - target_nw; std::memcpy(dst.d, prod_buf + drop, target_nw * sizeof(uint64_t)); dst.nw = mpn::normalized_size(dst.d, target_nw); dst.exp = prod_exp + static_cast(drop) * 64; } else { std::memcpy(dst.d, prod_buf, actual * sizeof(uint64_t)); dst.nw = actual; dst.exp = prod_exp; } rf_normalize(dst); } // 単一リム除算 (インプレース): a /= divisor static void rf_divmod_1(RawFloat& a, uint64_t divisor) { if (a.nw == 0) return; mpn::divmod_1(a.d, a.d, a.nw, divisor); rf_normalize(a); } // 単一リム乗算 (インプレース): a *= multiplier static void rf_mul_1(RawFloat& a, uint64_t multiplier) { if (a.nw == 0 || multiplier == 0) { a.nw = 0; a.exp = 0; return; } if (multiplier == 1) return; uint64_t carry = mpn::mul_1(a.d, a.d, a.nw, multiplier); if (carry) { a.d[a.nw] = carry; a.nw++; } rf_normalize(a); } // src を shift_bits ビット右シフトして dst に加算 (インプレース) // dst[0..dst_nw-1] += (src >> shift_bits) のオーバーラップ部分 // 戻り値: dst_nw を超えるキャリー (0 or 1) static uint64_t shift_add(uint64_t* dst, size_t dst_nw, const uint64_t* src, size_t src_nw, size_t shift_bits) { size_t word_shift = shift_bits / 64; unsigned bit_shift = static_cast(shift_bits % 64); if (word_shift >= src_nw) return 0; size_t remaining = src_nw - word_shift; size_t overlap = std::min(remaining, dst_nw); if (overlap == 0) return 0; uint64_t carry = 0; if (bit_shift == 0) { carry = mpn::add(dst, dst, overlap, src + word_shift, overlap); } else { for (size_t i = 0; i < overlap; ++i) { uint64_t lo = src[word_shift + i] >> bit_shift; uint64_t hi = (word_shift + i + 1 < src_nw) ? (src[word_shift + i + 1] << (64 - bit_shift)) : 0; uint64_t shifted = lo | hi; uint64_t sum = dst[i] + shifted; uint64_t c = (sum < dst[i]) ? 1ULL : 0ULL; sum += carry; c += (sum < carry) ? 1ULL : 0ULL; dst[i] = sum; carry = c; } } // キャリー伝播 for (size_t i = overlap; carry && i < dst_nw; ++i) { dst[i] += carry; carry = (dst[i] < carry) ? 1ULL : 0ULL; } return carry; } // src を shift_bits ビット右シフトして dst から減算 (インプレース) // dst[0..dst_nw-1] -= (src >> shift_bits) のオーバーラップ部分 // 戻り値: ボロー (0 or 1) static uint64_t shift_sub(uint64_t* dst, size_t dst_nw, const uint64_t* src, size_t src_nw, size_t shift_bits) { size_t word_shift = shift_bits / 64; unsigned bit_shift = static_cast(shift_bits % 64); if (word_shift >= src_nw) return 0; size_t remaining = src_nw - word_shift; size_t overlap = std::min(remaining, dst_nw); if (overlap == 0) return 0; uint64_t borrow = 0; if (bit_shift == 0) { borrow = mpn::sub(dst, dst, overlap, src + word_shift, overlap); } else { for (size_t i = 0; i < overlap; ++i) { uint64_t lo = src[word_shift + i] >> bit_shift; uint64_t hi = (word_shift + i + 1 < src_nw) ? (src[word_shift + i + 1] << (64 - bit_shift)) : 0; uint64_t shifted = lo | hi; uint64_t diff = dst[i] - shifted; uint64_t b = (diff > dst[i]) ? 1ULL : 0ULL; uint64_t diff2 = diff - borrow; b += (diff2 > diff) ? 1ULL : 0ULL; dst[i] = diff2; borrow = b; } } // ボロー伝播 for (size_t i = overlap; borrow && i < dst_nw; ++i) { uint64_t old_val = dst[i]; dst[i] -= borrow; borrow = (dst[i] > old_val) ? 1ULL : 0ULL; } return borrow; } // 加算 (インプレース): result += term (ビットレベルアライメント) // 戻り値: false なら term は negligible (収束判定に使用) static bool rf_add(RawFloat& result, const RawFloat& term, size_t nw_max) { if (term.nw == 0) return false; if (result.nw == 0) { std::memcpy(result.d, term.d, term.nw * sizeof(uint64_t)); result.nw = term.nw; result.exp = term.exp; return true; } int64_t exp_diff = result.exp - term.exp; if (exp_diff < 0) { // term の方が大きい指数: result = term + (旧result >> |exp_diff|) size_t neg_diff = static_cast(-exp_diff); if (neg_diff / 64 >= result.nw) { // result は negligible → term で上書き std::memcpy(result.d, term.d, term.nw * sizeof(uint64_t)); result.nw = term.nw; result.exp = term.exp; return true; } // 旧 result を退避 (稀なケース — Taylor 初回のみ) std::vector old_data(result.d, result.d + result.nw); size_t old_nw = result.nw; // result ← term std::memset(result.d, 0, nw_max * sizeof(uint64_t)); std::memcpy(result.d, term.d, term.nw * sizeof(uint64_t)); result.nw = term.nw; result.exp = term.exp; // result += (旧result >> neg_diff) uint64_t carry = shift_add(result.d, result.nw, old_data.data(), old_nw, neg_diff); if (carry && result.nw < nw_max) { result.d[result.nw] = carry; result.nw++; } return true; } // exp_diff >= 0: result += (term >> exp_diff) size_t total_shift = static_cast(exp_diff); if (total_shift / 64 >= term.nw) return false; // negligible uint64_t carry = shift_add(result.d, result.nw, term.d, term.nw, total_shift); if (carry && result.nw < nw_max) { result.d[result.nw] = carry; result.nw++; } return true; } // 減算 (インプレース): result -= term (sin/cos 用) static bool rf_sub(RawFloat& result, const RawFloat& term, size_t nw_max) { if (term.nw == 0) return false; if (result.nw == 0) return false; int64_t exp_diff = result.exp - term.exp; if (exp_diff < 0) return true; // term > result: 理論上到達しない size_t total_shift = static_cast(exp_diff); if (total_shift / 64 >= term.nw) return false; // negligible shift_sub(result.d, result.nw, term.d, term.nw, total_shift); result.nw = mpn::normalized_size(result.d, result.nw); return true; } // Float → RawFloat 抽出 (正規化済み、arena に確保) static RawFloat rf_extract(const Float& x, int nw, ScratchArena& arena) { auto x_vec = x.mantissa().words(); size_t x_nw_orig = x_vec.size(); int64_t x_exp = x.exponent(); uint64_t* x_d; size_t x_nw; if (x_nw_orig > static_cast(nw)) { x_d = arena.alloc_limbs(nw); size_t drop = x_nw_orig - nw; std::memcpy(x_d, x_vec.data() + drop, nw * sizeof(uint64_t)); x_exp += static_cast(drop) * 64; x_nw = mpn::normalized_size(x_d, nw); } else { x_d = arena.alloc_limbs(x_nw_orig); std::memcpy(x_d, x_vec.data(), x_nw_orig * sizeof(uint64_t)); x_nw = x_nw_orig; } RawFloat rf{x_d, x_nw, x_exp}; rf_normalize(rf); return rf; } // RawFloat → Float 変換 static Float rf_to_float(const RawFloat& rf, bool negative, int working_precision) { if (rf.nw == 0) return Float(); std::vector words(rf.d, rf.d + rf.nw); Int mantissa = Int::fromRawWords(words, 1); Float result(mantissa, rf.exp, negative); int wp_bits = Float::precisionToBits(working_precision); result.setEffectiveBits(wp_bits); result.truncateToApprox(working_precision); return result; } //============================================================================= // 指数関数 (exp) — Taylor級数 + 引数半減/二乗復元 //============================================================================= // 素朴な Taylor 級数 (RawFloat 出力版, x の符号対応) // divmod_1 + normalize を分離した版 (normalize は乗算前に遅延可能) static void rf_divmod_1_no_norm(RawFloat& a, uint64_t divisor) { if (a.nw == 0) return; mpn::divmod_1(a.d, a.d, a.nw, divisor); // normalize は呼ばない — 上位ゼロワードだけ除去 a.nw = mpn::normalized_size(a.d, a.nw); } static void rf_exp_naive(RawFloat& result, const RawFloat& x_rf, bool x_negative, int nw, uint64_t* prod_buf, uint64_t* scratch, ScratchArena& arena, int working_precision) { uint64_t* term_d = arena.alloc_limbs(nw + 2); // term = 1.0 std::memset(term_d, 0, (nw + 2) * sizeof(uint64_t)); term_d[nw - 1] = uint64_t(1) << 63; RawFloat term{term_d, static_cast(nw), -static_cast(nw * 64 - 1)}; // result = 1.0 std::memset(result.d, 0, (nw + 2) * sizeof(uint64_t)); result.d[nw - 1] = uint64_t(1) << 63; result.nw = static_cast(nw); result.exp = -static_cast(nw * 64 - 1); int max_terms = static_cast(working_precision * 1.2) + 10; for (int n = 1; n <= max_terms; ++n) { // term = term * x / n // rf_mul は内部で normalize する (乗算結果の正規化が必要なため) rf_mul(term, term, x_rf, prod_buf, scratch, nw); // divmod_1: normalize を省略し、上位ゼロワード除去のみ // (次の rf_mul で正規化されるため、ビットレベル normalize は不要) // ただし rf_add/rf_sub は exp_diff で位置を決めるため、 // 正しい exp が必要 → add の前に normalize する rf_divmod_1_no_norm(term, static_cast(n)); if (term.nw == 0) break; // add/sub の前に normalize (exp の正確さが必要) rf_normalize(term); if (x_negative && (n & 1)) { if (!rf_sub(result, term, nw + 1)) break; } else { if (!rf_add(result, term, nw + 1)) break; } } } // Paterson-Stockmeyer Taylor 級数 (x > 0 前提) // O(sqrt(l)) 回のフルサイズ乗算 + O(l) 回の単語除算 // MPFR exp2_aux2 と同等のアルゴリズム static void rf_exp_ps(RawFloat& result, const RawFloat& x_rf, int nw, uint64_t* prod_buf, uint64_t* scratch, ScratchArena& arena, int working_precision) { int target_bits = nw * 64; // 項数推定: |x| ≈ 2^x_msb, 各項は ~|x_msb| ビットずつ減少 int64_t x_msb = x_rf.exp + static_cast(x_rf.nw) * 64; int x_log2 = (x_msb <= 0) ? static_cast(-x_msb) : 1; if (x_log2 < 1) x_log2 = 1; int l_est = target_bits / x_log2 + 10; int m = static_cast(std::sqrt(static_cast(l_est))); if (m < 2) m = 2; if (m > 256) m = 256; // R[0..m] の事前計算: R[i] = x^i (ヒープ回避のため arena + 固定配列) constexpr int R_MAX = 260; // m の上限は 256 RawFloat R[R_MAX]; for (int i = 0; i <= m; ++i) { R[i].d = arena.alloc_limbs(nw + 2); std::memset(R[i].d, 0, (nw + 2) * sizeof(uint64_t)); R[i].nw = 0; R[i].exp = 0; } // R[0] = 1.0 R[0].d[nw - 1] = uint64_t(1) << 63; R[0].nw = static_cast(nw); R[0].exp = -static_cast(nw * 64 - 1); // R[1] = x std::memcpy(R[1].d, x_rf.d, x_rf.nw * sizeof(uint64_t)); R[1].nw = x_rf.nw; R[1].exp = x_rf.exp; // R[2..m]: 偶数は二乗 (rf_sqr)、奇数は R[i-1]*R[1] // R[1] の NTT キャッシュ (奇数 R[i] の計算で再利用) prime_ntt::NttCache cache_r1; rf_sqr(R[2], R[1], prod_buf, scratch, nw); for (int i = 3; i <= m; ++i) { if ((i & 1) == 0) { rf_sqr(R[i], R[i/2], prod_buf, scratch, nw); } else { rf_mul_cached(R[i], R[i-1], R[1], prod_buf, nw, cache_r1); } } // R[m] の NTT キャッシュ (giant step で繰り返し使用) prime_ntt::NttCache cache_rm; // rr = 1.0 (x^l / l! を追跡) RawFloat rr; rr.d = arena.alloc_limbs(nw + 2); std::memset(rr.d, 0, (nw + 2) * sizeof(uint64_t)); rr.d[nw - 1] = uint64_t(1) << 63; rr.nw = static_cast(nw); rr.exp = -static_cast(nw * 64 - 1); // t = baby step 作業用 RawFloat t; t.d = arena.alloc_limbs(nw + 2); // result = 0 std::memset(result.d, 0, (nw + 2) * sizeof(uint64_t)); result.nw = 0; result.exp = 0; int l = 0; int max_giant_steps = l_est / m + 5; for (int gs = 0; gs < max_giant_steps; ++gs) { // Baby step: Horner で sum_{i=0}^{m-1} R[i] * l!/(l+i)! を評価 std::memcpy(t.d, R[m-1].d, R[m-1].nw * sizeof(uint64_t)); t.nw = R[m-1].nw; t.exp = R[m-1].exp; for (int i = m - 2; i >= 0; --i) { rf_divmod_1(t, static_cast(l + i + 1)); if (t.nw == 0) { std::memcpy(t.d, R[i].d, R[i].nw * sizeof(uint64_t)); t.nw = R[i].nw; t.exp = R[i].exp; } else { rf_add(t, R[i], nw + 1); } } // t *= rr (最初の giant step では rr = 1 なので乗算不要) if (gs > 0) { rf_mul(t, t, rr, prod_buf, scratch, nw); } if (t.nw == 0) break; // result += t rf_add(result, t, nw + 1); // rr 更新: rr = rr * R[m] / ((l+1)(l+2)...(l+m)) rf_mul_cached(rr, rr, R[m], prod_buf, nw, cache_rm); for (int i = 1; i <= m; ++i) { rf_divmod_1(rr, static_cast(l + i)); } l += m; if (rr.nw == 0) break; } } // sin/cos Taylor の項数推定 (Stirling 近似考慮) // u^l / (2l)! < 2^(-target_bits) を解く // log2(u^l / (2l)!) ≈ l*log2(u) - 2l*log2(2l/e) = -l*(u_log2 + 2*log2(2l/e)) static int estimate_sincos_terms(int target_bits, int u_log2) { double tb = static_cast(target_bits); double ul = static_cast(u_log2); // 初回推定 (階乗なし) double l_approx = tb / std::max(1.0, ul); // Stirling 反復で収束 for (int iter = 0; iter < 8; ++iter) { double two_l = 2.0 * l_approx; double factorial_bits = two_l * std::log2(std::max(2.0, two_l / 2.718281828459045)); double bits_per_term = ul + factorial_bits / l_approx; double new_l = tb / std::max(1.0, bits_per_term); if (std::abs(new_l - l_approx) < 1.0) break; l_approx = new_l; } return static_cast(l_approx) + 10; } // Paterson-Stockmeyer for sin Taylor 級数 // sin(x) = x * Σ_{k=0}^{∞} (-1)^k * u^k / (2k+1)! where u = x² // O(sqrt(l)) 回のフルサイズ乗算 + O(l) 回の単語除算 static void rf_sin_ps(RawFloat& result, const RawFloat& x_rf, const RawFloat& x2, int nw, uint64_t* prod_buf, uint64_t* scratch, ScratchArena& arena, int working_precision) { int target_bits = nw * 64; // u = x² の MSB で項数推定 (Stirling 近似) int64_t u_msb = x2.exp + static_cast(x2.nw) * 64; int u_log2 = (u_msb <= 0) ? static_cast(-u_msb) : 1; if (u_log2 < 1) u_log2 = 1; int l_est = estimate_sincos_terms(target_bits, u_log2); int m = static_cast(std::sqrt(static_cast(l_est))); if (m < 2) m = 2; if (m > 256) m = 256; // R[0..m] の事前計算: R[i] = u^i std::vector R(m + 1); for (int i = 0; i <= m; ++i) { R[i].d = arena.alloc_limbs(nw + 2); std::memset(R[i].d, 0, (nw + 2) * sizeof(uint64_t)); R[i].nw = 0; R[i].exp = 0; } R[0].d[nw - 1] = uint64_t(1) << 63; R[0].nw = static_cast(nw); R[0].exp = -static_cast(nw * 64 - 1); std::memcpy(R[1].d, x2.d, x2.nw * sizeof(uint64_t)); R[1].nw = x2.nw; R[1].exp = x2.exp; // R[1] の NTT キャッシュ prime_ntt::NttCache sin_cache_r1; rf_sqr(R[2], R[1], prod_buf, scratch, nw); for (int i = 3; i <= m; ++i) { if ((i & 1) == 0) rf_sqr(R[i], R[i/2], prod_buf, scratch, nw); else rf_mul_cached(R[i], R[i-1], R[1], prod_buf, nw, sin_cache_r1); } // R[m] の NTT キャッシュ (giant step で再利用) prime_ntt::NttCache sin_cache_rm; // rr = 1.0 (|u^l / (2l+1)!| を追跡) RawFloat rr; rr.d = arena.alloc_limbs(nw + 2); std::memset(rr.d, 0, (nw + 2) * sizeof(uint64_t)); rr.d[nw - 1] = uint64_t(1) << 63; rr.nw = static_cast(nw); rr.exp = -static_cast(nw * 64 - 1); // t = baby step 作業用, tmp = R[i]-t 用一時バッファ RawFloat t; t.d = arena.alloc_limbs(nw + 2); uint64_t* tmp_d = arena.alloc_limbs(nw + 2); // result = 0 std::memset(result.d, 0, (nw + 2) * sizeof(uint64_t)); result.nw = 0; result.exp = 0; int l = 0; int max_giant_steps = l_est / m + 5; for (int gs = 0; gs < max_giant_steps; ++gs) { // Baby step: Horner で Σ_{i=0}^{m-1} (-1)^i * u^i / (部分分母積) を評価 // t = R[m-1]; for i=m-2..0: t /= (2(l+i+1))(2(l+i+1)+1); t = R[i] - t std::memcpy(t.d, R[m-1].d, R[m-1].nw * sizeof(uint64_t)); t.nw = R[m-1].nw; t.exp = R[m-1].exp; for (int i = m - 2; i >= 0; --i) { uint64_t d1 = static_cast(2*(l+i+1)); uint64_t d2 = d1 + 1; rf_divmod_1(t, d1 * d2); if (t.nw == 0) { std::memcpy(t.d, R[i].d, R[i].nw * sizeof(uint64_t)); t.nw = R[i].nw; t.exp = R[i].exp; } else { // t = R[i] - t (交互級数の符号処理) std::memset(tmp_d, 0, (nw + 2) * sizeof(uint64_t)); std::memcpy(tmp_d, R[i].d, R[i].nw * sizeof(uint64_t)); RawFloat tmp{tmp_d, R[i].nw, R[i].exp}; rf_sub(tmp, t, nw + 1); std::memcpy(t.d, tmp.d, tmp.nw * sizeof(uint64_t)); std::memset(t.d + tmp.nw, 0, (nw + 2 - tmp.nw) * sizeof(uint64_t)); t.nw = tmp.nw; t.exp = tmp.exp; } } // t *= rr (最初の giant step では rr = 1 なので乗算不要) if (gs > 0) { rf_mul(t, t, rr, prod_buf, scratch, nw); } if (t.nw == 0) break; // result に加減算 ((-1)^l に応じて) if (l % 2 == 0) { rf_add(result, t, nw + 1); } else { rf_sub(result, t, nw + 1); } // rr 更新: rr *= R[m] / Π_{j=0}^{m-1} ((2(l+j)+2)(2(l+j)+3)) rf_mul_cached(rr, rr, R[m], prod_buf, nw, sin_cache_rm); for (int j = 0; j < m; ++j) { uint64_t d1 = static_cast(2*(l+j) + 2); uint64_t d2 = d1 + 1; rf_divmod_1(rr, d1 * d2); } l += m; if (rr.nw == 0) break; } // sin(x) = x * S(u): result *= x rf_mul(result, result, x_rf, prod_buf, scratch, nw); } // Paterson-Stockmeyer for cos Taylor 級数 // cos(x) = Σ_{k=0}^{∞} (-1)^k * u^k / (2k)! where u = x² static void rf_cos_ps(RawFloat& result, const RawFloat& x2, int nw, uint64_t* prod_buf, uint64_t* scratch, ScratchArena& arena, int working_precision) { int target_bits = nw * 64; int64_t u_msb = x2.exp + static_cast(x2.nw) * 64; int u_log2 = (u_msb <= 0) ? static_cast(-u_msb) : 1; if (u_log2 < 1) u_log2 = 1; int l_est = estimate_sincos_terms(target_bits, u_log2); int m = static_cast(std::sqrt(static_cast(l_est))); if (m < 2) m = 2; if (m > 256) m = 256; // R[0..m] = u^i std::vector R(m + 1); for (int i = 0; i <= m; ++i) { R[i].d = arena.alloc_limbs(nw + 2); std::memset(R[i].d, 0, (nw + 2) * sizeof(uint64_t)); R[i].nw = 0; R[i].exp = 0; } R[0].d[nw - 1] = uint64_t(1) << 63; R[0].nw = static_cast(nw); R[0].exp = -static_cast(nw * 64 - 1); std::memcpy(R[1].d, x2.d, x2.nw * sizeof(uint64_t)); R[1].nw = x2.nw; R[1].exp = x2.exp; // R[1] の NTT キャッシュ prime_ntt::NttCache cos_cache_r1; rf_sqr(R[2], R[1], prod_buf, scratch, nw); for (int i = 3; i <= m; ++i) { if ((i & 1) == 0) rf_sqr(R[i], R[i/2], prod_buf, scratch, nw); else rf_mul_cached(R[i], R[i-1], R[1], prod_buf, nw, cos_cache_r1); } // R[m] の NTT キャッシュ prime_ntt::NttCache cos_cache_rm; RawFloat rr; rr.d = arena.alloc_limbs(nw + 2); std::memset(rr.d, 0, (nw + 2) * sizeof(uint64_t)); rr.d[nw - 1] = uint64_t(1) << 63; rr.nw = static_cast(nw); rr.exp = -static_cast(nw * 64 - 1); RawFloat t; t.d = arena.alloc_limbs(nw + 2); uint64_t* tmp_d = arena.alloc_limbs(nw + 2); std::memset(result.d, 0, (nw + 2) * sizeof(uint64_t)); result.nw = 0; result.exp = 0; int l = 0; int max_giant_steps = l_est / m + 5; for (int gs = 0; gs < max_giant_steps; ++gs) { // Baby step: Horner // cos の分母: (2(l+i+1)-1)(2(l+i+1)) = (2l+2i+1)(2l+2i+2) std::memcpy(t.d, R[m-1].d, R[m-1].nw * sizeof(uint64_t)); t.nw = R[m-1].nw; t.exp = R[m-1].exp; for (int i = m - 2; i >= 0; --i) { uint64_t d1 = static_cast(2*(l+i+1) - 1); uint64_t d2 = d1 + 1; rf_divmod_1(t, d1 * d2); if (t.nw == 0) { std::memcpy(t.d, R[i].d, R[i].nw * sizeof(uint64_t)); t.nw = R[i].nw; t.exp = R[i].exp; } else { std::memset(tmp_d, 0, (nw + 2) * sizeof(uint64_t)); std::memcpy(tmp_d, R[i].d, R[i].nw * sizeof(uint64_t)); RawFloat tmp{tmp_d, R[i].nw, R[i].exp}; rf_sub(tmp, t, nw + 1); std::memcpy(t.d, tmp.d, tmp.nw * sizeof(uint64_t)); std::memset(t.d + tmp.nw, 0, (nw + 2 - tmp.nw) * sizeof(uint64_t)); t.nw = tmp.nw; t.exp = tmp.exp; } } if (gs > 0) { rf_mul(t, t, rr, prod_buf, scratch, nw); } if (t.nw == 0) break; if (l % 2 == 0) { rf_add(result, t, nw + 1); } else { rf_sub(result, t, nw + 1); } // rr 更新: rr *= R[m] / Π_{j=0}^{m-1} ((2(l+j)+1)(2(l+j)+2)) rf_mul_cached(rr, rr, R[m], prod_buf, nw, cos_cache_rm); for (int j = 0; j < m; ++j) { uint64_t d1 = static_cast(2*(l+j) + 1); uint64_t d2 = d1 + 1; rf_divmod_1(rr, d1 * d2); } l += m; if (rr.nw == 0) break; } } // 前方宣言: Q128/Q256 ファストパス (定義は q128_mul 等の後) static Float exp_small(const Float& x, int precision); static Float exp_medium(const Float& x, int precision); static Float sin_core(Float x, int eff_x, int precision); static Float cos_core(Float x, int eff_x, int precision); // mpn 直接計算による高速 Taylor 級数 (ループ内アロケーションゼロ) // 旧インターフェース互換 — 低精度 fallback 用 static Float expTaylor(const Float& x, int working_precision) { int wp_bits = Float::precisionToBits(working_precision); int nw = (wp_bits + 63) / 64; ScratchScope scope; auto& arena = getThreadArena(); // バッファ事前確保 uint64_t* term_d = arena.alloc_limbs(nw + 2); uint64_t* result_d = arena.alloc_limbs(nw + 2); size_t prod_alloc = 2 * static_cast(nw) + 4; uint64_t* prod_buf = arena.alloc_limbs(prod_alloc); size_t scratch_sz = mpn::multiply_scratch_size(nw + 1, nw + 1); uint64_t* scratch = arena.alloc_limbs(scratch_sz > 0 ? scratch_sz : 1); // x の仮数/指数を抽出 (正規化済み) RawFloat x_rf = rf_extract(x, nw, arena); bool x_negative = x.isNegative(); // term = 1.0: 仮数 = 2^(nw*64-1), 指数 = -(nw*64-1) std::memset(term_d, 0, (nw + 2) * sizeof(uint64_t)); term_d[nw - 1] = uint64_t(1) << 63; RawFloat term{term_d, static_cast(nw), -static_cast(nw * 64 - 1)}; // result = 1.0 std::memset(result_d, 0, (nw + 2) * sizeof(uint64_t)); result_d[nw - 1] = uint64_t(1) << 63; RawFloat result{result_d, static_cast(nw), -static_cast(nw * 64 - 1)}; int max_terms = static_cast(working_precision * 1.2) + 10; for (int n = 1; n <= max_terms; ++n) { rf_mul(term, term, x_rf, prod_buf, scratch, nw); rf_divmod_1(term, static_cast(n)); if (term.nw == 0) break; // x < 0 のとき奇数項は負 (x^n/n! で n 奇数) if (x_negative && (n & 1)) { if (!rf_sub(result, term, nw + 1)) break; } else { if (!rf_add(result, term, nw + 1)) break; } } return rf_to_float(result, false, working_precision); } // 二乗による exp 計算: K 回半減 → Taylor → K 回二乗復元 // exp(x) = exp(x/2^K)^{2^K} // Taylor: 素朴方式 (小精度) と Paterson-Stockmeyer (大精度) を切り替え。 // 二乗復元は RawFloat で実行し Float オーバーヘッドを回避。 static Float expDoubling(const Float& x, int working_precision) { int wp_bits = Float::precisionToBits(working_precision); // P-S 閾値: ~2000桁 (7000 ビット) 以上で P-S + cuberoot K を使用 // それ以下では PS の baby step スカラーコスト (m × divmod_1 + m × rf_add // per giant step) が乗算コスト削減を上回り、naive Taylor が高速 bool use_ps = (wp_bits >= 7000); int K; if (use_ps) { K = static_cast(std::cbrt(4.0 * wp_bits)); } else { // コストモデル最適化: K ≈ α·√B // Taylor 項数 ≈ B/(K+x_shift) vs 二乗復元 K 回のトレードオフを最小化。 // sqr_cost ≈ 0.7×mul_cost を考慮すると K_opt = √(B/0.7) ≈ 1.2×√B。 // ただし高精度では guard bits (K+log2(K)+10) による nw 増加が支配的になるため // 係数を精度に応じて調整: ≤2000 bits → 1.2, >2000 bits → 1.0 double alpha = (wp_bits <= 2000) ? 1.2 : 1.0; K = static_cast(alpha * std::sqrt(static_cast(wp_bits))); } // 小引数最適化: |x| ≈ 2^{-B} のとき、x は既に小さいため // K 回の二乗復元は不要。K を B だけ削減して二乗コストを節約。 // 例: |x| ≈ 2^{-45} で K=51 → K=6, 二乗 45 回分を節約。 // Taylor/P-S の項数は p/(B+K) で、B が大きいほど少なくて済む。 if (!x.isZero()) { int64_t msb_pos = x.exponent() + static_cast(x.mantissa().bitLength()); if (msb_pos < 0) { int arg_zeros = static_cast( std::min(-msb_pos, static_cast(K))); K -= arg_zeros; } } if (K < 1) return expTaylor(x, working_precision); // Guard bits: 二乗復元で誤差が最大 2^K 倍に増幅されるため K+α ビット追加 int guard_bits = K + static_cast(std::ceil(std::log2(K + 1))) + 10; int wp_inner = working_precision + Float::bitsToPrecision(guard_bits); int wp_inner_bits = Float::precisionToBits(wp_inner); int nw = (wp_inner_bits + 63) / 64; Float x_red = ldexp(x, -K); // x / 2^K — O(1) の指数操作 x_red.truncateToApprox(wp_inner); x_red.setEffectiveBits(wp_inner_bits); ScratchScope scope; auto& arena = getThreadArena(); RawFloat x_rf = rf_extract(x_red, nw, arena); bool x_negative = x.isNegative(); size_t prod_alloc = 2 * static_cast(nw) + 4; uint64_t* prod_buf = arena.alloc_limbs(prod_alloc); size_t mul_scratch = mpn::multiply_scratch_size(nw + 1, nw + 1); size_t sqr_scratch = mpn::square_scratch_size(nw + 1); size_t scratch_sz = std::max(mul_scratch, sqr_scratch); uint64_t* scratch_buf = arena.alloc_limbs(scratch_sz > 0 ? scratch_sz : 1); uint64_t* result_d = arena.alloc_limbs(nw + 2); RawFloat result{result_d, 0, 0}; // exp_core が x ≥ 0 を保証するため、x_negative は常に false // (負の引数は exp_core 内で x + k·ln2 に変換済み) if (use_ps) { rf_exp_ps(result, x_rf, nw, prod_buf, scratch_buf, arena, wp_inner); } else { rf_exp_naive(result, x_rf, x_negative, nw, prod_buf, scratch_buf, arena, wp_inner); } // K 回二乗復元: mpn::square は一般乗算より高速 (対称性を活用) for (int i = 0; i < K; ++i) { rf_sqr(result, result, prod_buf, scratch_buf, nw); } Float out = rf_to_float(result, false, wp_inner); out.truncateToApprox(working_precision); return out; } // Newton 反復による exp: y_{n+1} = y_n · (1 + x - log(y_n)) // log は AGM ベース O(M(n)·log n) なので、精度倍増で総コスト ≈ 2 × log(n)。 // 引数 x は縮小済み (0 <= x < log2)。 static Float exp_newton(const Float& x, int working_precision) { // 初期近似: double 精度の exp double x_d = x.toDouble(); Float y(std::exp(x_d)); int target_bits = Float::precisionToBits(working_precision); // 精度倍増ループ: correct_bits が Newton の二次収束で 53→106→212→... と倍増 // 各ステップの計算精度 = 2*correct_bits + guard (correction を正確に求めるため) int correct_bits = 53; // double 精度の初期近似 int guard = 64; while (correct_bits < target_bits) { // 計算精度: Newton correction を求めるのに 2*correct_bits 必要 int compute_bits = std::min(2 * correct_bits + guard, target_bits + guard); int step_prec = Float::bitsToPrecision(compute_bits); // effective_bits_ を現ステップの精度に合わせる (lessons-learned 参照) y.setEffectiveBits(Float::precisionToBits(step_prec)); y.setResultPrecision(step_prec); // log(y) を現ステップ精度で計算 Float log_y = log(y, step_prec); // delta = x - log(y) Float delta = Float(x); delta.truncateToApprox(step_prec); delta = delta - log_y; // y = y * (1 + delta) = y + y * delta Float one_plus_delta = Float::one(step_prec) + delta; y = y * one_plus_delta; y.truncateToApprox(step_prec); // 二次収束: correct_bits は2倍 (計算精度でキャップ) correct_bits = std::min(2 * correct_bits, compute_bits); } y.truncateToApprox(working_precision); return y; } // 本体: x を値で受け取る (move 済み前提) static Float exp_core(Float x, int eff_x, int precision) { int compute_prec = effectiveComputePrecision(eff_x, precision); int working_precision = compute_prec + 10; // 引数縮小: exp(x) = 2^k * exp(x - k*log2), x - k*log2 ∈ [0, log2) // k は double 精度で十分 (|k| < 1500 なので 53bit double で正確) int k = 0; double x_d = x.toDouble(); if (x_d > 0.5 || x_d < -0.5) { k = static_cast(std::floor(x_d * 1.4426950408889634)); Float log2_val = Float::log2(working_precision); x = x - Float(k) * log2_val; // x ∈ [0, log2) を保証 (丸め誤差で境界を超える場合の補正) if (x.isNegative()) { x = x + log2_val; k--; } } else if (x.isNegative()) { // |x| <= 0.5 で x < 0: exp(x) = exp(x + log2) / 2 Float log2_val = Float::log2(working_precision); x = x + log2_val; k = -1; } x.truncateToApprox(working_precision); // 高精度: Newton 反復 (exp via log), 低精度: Taylor + 引数半減/二乗復元 // PS+二乗復元は O(n^{1/3}·M(n)), exp_newton は O(log²n·M(n)) // n^{1/3} < log²n となるのは n > ~10^7 bits なので、 // 実用範囲ではほぼ常に PS+二乗復元が高速 int wp_bits = Float::precisionToBits(working_precision); Float result; if (wp_bits >= 100000) { result = exp_newton(x, working_precision); } else { result = expDoubling(x, working_precision); } if (k > 0) result <<= k; else if (k < 0) result >>= -k; finalizeResult(result, eff_x, precision); return result; } Float exp(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return x.isNegative() ? Float::zero() : Float::positiveInfinity(); if (x.isZero()) return Float::one(precision); if (x.isNegative() && x <= Float(-1000)) return Float::zero(); if (!x.isNegative() && x >= Float(1000)) return Float::positiveInfinity(); int precision_bits = Float::precisionToBits(precision); if (precision_bits <= 64) { return exp_small(x, precision); } return zivRound([](const Float& a, int e, int p) { return exp_core(Float(a), e, p); }, x, x.effectiveBits(), precision); } Float exp(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return x.isNegative() ? Float::zero() : Float::positiveInfinity(); if (x.isZero()) return Float::one(precision); if (x.isNegative() && x <= Float(-1000)) return Float::zero(); if (!x.isNegative() && x >= Float(1000)) return Float::positiveInfinity(); int precision_bits = Float::precisionToBits(precision); if (precision_bits <= 64) { return exp_small(x, precision); } return zivRound([](const Float& a, int e, int p) { return exp_core(Float(a), e, p); }, x, x.effectiveBits(), precision); } //============================================================================= // 自然対数 (log) //============================================================================= //------------------------------------------------------------------------- // Multi-prime 引数削減 (Johansson 2022, PERF-LOG-D) // // log(x) = Σ c_i·log(p_i) + log(1+δ) // LLL 格子基底簡約で整数 c_i を求め、|δ| を最小化。 // |δ| ≈ 2^{-50} 程度に削減 → Halley の exp が小引数で高速。 // // 効果: 2000-6000 ビット (600-1800 桁) で Halley 拡張により // AGM 比 20-30% 高速化。キャッシュ warm 後はさらに有効。 //------------------------------------------------------------------------- // 前方宣言 (Float.cpp で定義) Float computeAtanhReciprocal(int n, int precision); // 前方宣言 (後方で定義) static Float log_core(Float x, int eff_x, int precision); struct LogfResult { Float logf; int64_t k; bool f_is_one; }; static LogfResult logf_newton_core(Float x, int compute_prec); // 小素数の対数を atanh BS 公式で計算 // log(p) = k·log(2) + 2·atanh(1/n) (p に依存する k, n) // atanh(1/n) = (1/n)·Σ (1/n²)^k/(2k+1) — BS で高速計算 struct LogPrimeFormula { int prime; int log2_coeff; // log(2) の係数 int log3_coeff; // log(3) の係数 int log5_coeff; // log(5) の係数 int log7_coeff; // log(7) の係数 int log11_coeff; // log(11) の係数 int atanh_n; // atanh(1/n) の n (0 = atanh 不要) int atanh_coeff; // atanh(1/n) の係数 }; // 素数 → atanh 恒等式テーブル // log(2) = 2·atanh(1/3) // log(3) = log(2) + 2·atanh(1/5) ∵ atanh(1/5)=(1/2)·log(3/2) // log(5) = 2·log(2) + 2·atanh(1/9) ∵ atanh(1/9)=(1/2)·log(5/4) // log(7) = 3·log(2) - 2·atanh(1/15) ∵ atanh(1/15)=(1/2)·log(8/7) // log(11)= log(2)+log(5) + 2·atanh(1/21) ∵ atanh(1/21)=(1/2)·log(11/10) // log(13)= 2·log(2)+log(3) + 2·atanh(1/25) ∵ atanh(1/25)=(1/2)·log(13/12) // log(17)= 4·log(2) + 2·atanh(1/33) ∵ atanh(1/33)=(1/2)·log(17/16) // log(19)= log(2)+2·log(3) + 2·atanh(1/37) ∵ atanh(1/37)=(1/2)·log(19/18) // log(23)= 3·log(2)+log(3) - 2·atanh(1/47) ∵ atanh(1/47)=(1/2)·log(24/23) // log(29)= 2·log(2)+log(7) + 2·atanh(1/57) ∵ atanh(1/57)=(1/2)·log(29/28) static constexpr int MP_NUM_PRIMES = 10; static constexpr int MP_PRIMES[MP_NUM_PRIMES] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29}; // thread_local キャッシュ: log(p_i) struct LogPrimeCacheEntry { Float value; int precision; }; static LogPrimeCacheEntry& logPrimeCacheAt(int index) { static thread_local LogPrimeCacheEntry cache[MP_NUM_PRIMES] = {}; return cache[index]; } // log(prime) を atanh 恒等式で計算 (既に計算済みの log(2),log(3)等を再利用) static Float computeLogPrime(int index, int precision) { int wp = precision + 15; switch (MP_PRIMES[index]) { case 2: return Float::log2(wp); case 3: { // log(3) = log(2) + 2·atanh(1/5) Float a5 = computeAtanhReciprocal(5, wp); return Float::log2(wp) + ldexp(a5, 1); } case 5: { // log(5) = 2·log(2) + 2·atanh(1/9) Float a9 = computeAtanhReciprocal(9, wp); return ldexp(Float::log2(wp), 1) + ldexp(a9, 1); } case 7: { // log(7) = 3·log(2) - 2·atanh(1/15) Float a15 = computeAtanhReciprocal(15, wp); return mulScalarF(Float::log2(wp), uint64_t(3)) - ldexp(a15, 1); } case 11: { // log(11) = log(10) + 2·atanh(1/21) // = log(2) + log(5) + 2·atanh(1/21) Float a21 = computeAtanhReciprocal(21, wp); Float log5 = computeLogPrime(2, wp); // index 2 = prime 5 return Float::log2(wp) + log5 + ldexp(a21, 1); } case 13: { // log(13) = 2·log(2) + log(3) + 2·atanh(1/25) Float a25 = computeAtanhReciprocal(25, wp); Float log3 = computeLogPrime(1, wp); return ldexp(Float::log2(wp), 1) + log3 + ldexp(a25, 1); } case 17: { // log(17) = 4·log(2) + 2·atanh(1/33) Float a33 = computeAtanhReciprocal(33, wp); return ldexp(Float::log2(wp), 2) + ldexp(a33, 1); } case 19: { // log(19) = log(2) + 2·log(3) + 2·atanh(1/37) Float a37 = computeAtanhReciprocal(37, wp); Float log3 = computeLogPrime(1, wp); return Float::log2(wp) + ldexp(log3, 1) + ldexp(a37, 1); } case 23: { // log(23) = 3·log(2) + log(3) - 2·atanh(1/47) Float a47 = computeAtanhReciprocal(47, wp); Float log3 = computeLogPrime(1, wp); return mulScalarF(Float::log2(wp), uint64_t(3)) + log3 - ldexp(a47, 1); } case 29: { // log(29) = 2·log(2) + log(7) + 2·atanh(1/57) Float a57 = computeAtanhReciprocal(57, wp); Float log7 = computeLogPrime(3, wp); return ldexp(Float::log2(wp), 1) + log7 + ldexp(a57, 1); } default: // fallback: AGM return log_core(Float(MP_PRIMES[index]), 64, wp); } } // キャッシュ付き log(p_i) 取得 static const Float& getLogPrime(int index, int precision) { auto& entry = logPrimeCacheAt(index); if (entry.precision > 0 && precision <= entry.precision) { return entry.value; } entry.value = computeLogPrime(index, precision); entry.value.setResultPrecision(precision); entry.precision = precision; return entry.value; } //------------------------------------------------------------------------- // LLL 格子基底簡約 (小次元, 浮動小数点) // log(p_i) の整数線形結合で log(x) を近似する係数 c_i を求める。 // 格子: b_i = (e_i, C·log(p_i)) ∈ R^{K+1}, i=0,...,K-1 // 目標: t = (0,...,0, C·log(x)) // Babai の最近平面法で最近格子点を求め、第 0..K-1 成分 = c_i //------------------------------------------------------------------------- static constexpr int MP_DIM = MP_NUM_PRIMES + 1; // 11 struct MultiPrimeLattice { int64_t basis[MP_NUM_PRIMES][MP_DIM]; double gs[MP_NUM_PRIMES][MP_DIM]; // Gram-Schmidt 直交基底 double gs_norm2[MP_NUM_PRIMES]; // ||b*_i||² bool ready = false; void init() { if (ready) return; constexpr double SCALE = static_cast(1LL << 52); // 初期基底: b_i = (e_i, round(SCALE·log(p_i))) for (int i = 0; i < MP_NUM_PRIMES; i++) { for (int j = 0; j < MP_DIM; j++) basis[i][j] = 0; basis[i][i] = 1; basis[i][MP_NUM_PRIMES] = static_cast(std::llround( SCALE * std::log(static_cast(MP_PRIMES[i])))); } lll(); computeGS(); ready = true; } // Babai 最近平面法: log(x) に最も近い格子点の座標を返す void findCoeffs(double log_x, int64_t coeffs[MP_NUM_PRIMES]) const { constexpr double SCALE = static_cast(1LL << 52); double target[MP_DIM] = {}; target[MP_NUM_PRIMES] = SCALE * log_x; // Babai: 直交射影 → 丸め → 減算 double b[MP_DIM]; for (int j = 0; j < MP_DIM; j++) b[j] = target[j]; int64_t d[MP_NUM_PRIMES]; for (int i = MP_NUM_PRIMES - 1; i >= 0; i--) { double dot = 0; for (int j = 0; j < MP_DIM; j++) dot += b[j] * gs[i][j]; d[i] = std::llround(dot / gs_norm2[i]); for (int j = 0; j < MP_DIM; j++) b[j] -= d[i] * static_cast(basis[i][j]); } // 格子点 = Σ d_i · basis[i] の第 0..K-1 成分が c_i for (int j = 0; j < MP_NUM_PRIMES; j++) { int64_t sum = 0; for (int i = 0; i < MP_NUM_PRIMES; i++) sum += d[i] * basis[i][j]; coeffs[j] = sum; } } private: void computeGS() { for (int i = 0; i < MP_NUM_PRIMES; i++) { for (int j = 0; j < MP_DIM; j++) gs[i][j] = static_cast(basis[i][j]); for (int k = 0; k < i; k++) { double dot = 0; for (int j = 0; j < MP_DIM; j++) dot += static_cast(basis[i][j]) * gs[k][j]; double mu = dot / gs_norm2[k]; for (int j = 0; j < MP_DIM; j++) gs[i][j] -= mu * gs[k][j]; } gs_norm2[i] = 0; for (int j = 0; j < MP_DIM; j++) gs_norm2[i] += gs[i][j] * gs[i][j]; } } void lll() { constexpr double DELTA = 0.75; double mu_mat[MP_NUM_PRIMES][MP_NUM_PRIMES] = {}; double Bstar[MP_NUM_PRIMES][MP_DIM]; double Bnorm[MP_NUM_PRIMES]; auto recomputeGS = [&]() { for (int i = 0; i < MP_NUM_PRIMES; i++) { for (int j = 0; j < MP_DIM; j++) Bstar[i][j] = static_cast(basis[i][j]); for (int k = 0; k < i; k++) { double dot = 0; for (int j = 0; j < MP_DIM; j++) dot += static_cast(basis[i][j]) * Bstar[k][j]; mu_mat[i][k] = dot / Bnorm[k]; for (int j = 0; j < MP_DIM; j++) Bstar[i][j] -= mu_mat[i][k] * Bstar[k][j]; } Bnorm[i] = 0; for (int j = 0; j < MP_DIM; j++) Bnorm[i] += Bstar[i][j] * Bstar[i][j]; } }; recomputeGS(); int k = 1; while (k < MP_NUM_PRIMES) { // サイズ簡約 for (int j = k - 1; j >= 0; j--) { if (std::abs(mu_mat[k][j]) > 0.5) { int64_t r = std::llround(mu_mat[k][j]); for (int l = 0; l < MP_DIM; l++) basis[k][l] -= r * basis[j][l]; recomputeGS(); } } // Lovász 条件 double lhs = DELTA * Bnorm[k - 1]; double rhs = Bnorm[k] + mu_mat[k][k-1] * mu_mat[k][k-1] * Bnorm[k-1]; if (lhs > rhs) { for (int l = 0; l < MP_DIM; l++) std::swap(basis[k][l], basis[k - 1][l]); recomputeGS(); k = std::max(k - 1, 1); } else { k++; } } } }; static MultiPrimeLattice& getMultiPrimeLattice() { static MultiPrimeLattice lattice; if (!lattice.ready) lattice.init(); return lattice; } // Multi-prime 引数削減による log 計算 // log(x) = Σ c_i·log(p_i) + log(r), r = x · Π p_i^{-c_i} ≈ 1 static Float log_multiprime(Float x, int eff_x, int precision) { auto& lattice = getMultiPrimeLattice(); int compute_prec = effectiveComputePrecision(eff_x, precision); int wp = compute_prec + 20; // 1. log(x) の double 近似 (overflow 回避) int64_t binary_exp = x.exponent() + static_cast(x.mantissa().bitLength()); double log_x; if (binary_exp > 500 || binary_exp < -500) { // 大きな指数: log(mantissa) + exp·log(2) Float normalized = ldexp(x, static_cast(-binary_exp)); log_x = std::log(normalized.toDouble()) + static_cast(binary_exp) * std::log(2.0); } else { log_x = std::log(x.toDouble()); } // 2. LLL + Babai で係数 c_i を求める int64_t coeffs[MP_NUM_PRIMES]; lattice.findCoeffs(log_x, coeffs); // 3. r = x · Π p_i^{-c_i} を高精度で計算 Float r(x); r.setResultPrecision(wp); for (int i = 0; i < MP_NUM_PRIMES; i++) { if (coeffs[i] == 0) continue; if (MP_PRIMES[i] == 2) { r = ldexp(std::move(r), static_cast(-coeffs[i])); } else { Int pk(1); for (int64_t j = 0; j < std::abs(coeffs[i]); j++) pk = pk * Int(MP_PRIMES[i]); Float pk_f(pk); pk_f.setResultPrecision(wp); if (coeffs[i] > 0) { r = r / pk_f; } else { r = r * pk_f; } } } // 4. log(r) を計算 (r ≈ 1, |r-1| ≈ 2^{-50}) // r ≈ 1 なので Halley が効率的: // - exp(y) の引数が小さく、expDoubling の K が自動削減される // - Q256 初期近似 → 2-3 回の Halley で収束 Float log_r; if (r == Float::one()) { log_r = Float::zero(); } else { int eff_r = r.effectiveBits(); auto [logf, k_val, f_is_one] = logf_newton_core(std::move(r), wp); if (f_is_one) { if (k_val == 0) { log_r = Float::zero(); } else { log_r = Float::log2(wp) * Float(k_val); } } else { log_r = std::move(logf); if (k_val != 0) { Float log2_val = Float::log2(wp); log_r = log_r + log2_val * Float(k_val); } } log_r.setResultPrecision(wp); } // 5. log(x) = Σ c_i·log(p_i) + log(r) Float result = std::move(log_r); for (int i = 0; i < MP_NUM_PRIMES; i++) { if (coeffs[i] == 0) continue; const Float& log_p = getLogPrime(i, wp); Float contrib = log_p * Float(coeffs[i]); contrib.setResultPrecision(wp); result = result + contrib; } finalizeResult(result, eff_x, precision); return result; } // AGM 法による log 計算 // log(x) = π / (2 · AGM(1, 4/s)) − m · log(2) // where s = x · 2^m > 2^{p/2} // AGM の各ステップは mul + sqrt = O(M(n)) で、O(log n) ステップ。 // Newton 法 (各ステップに exp = O(M(n)·n^{1/3})) より大幅に軽い。 static Float log_core(Float x, int eff_x, int precision) { int compute_prec = effectiveComputePrecision(eff_x, precision); // x の二進指数: x ∈ [2^{ea-1}, 2^{ea}) int64_t ea = x.exponent() + static_cast(x.mantissa().bitLength()); // 打ち消しのガードビット推定 // term1 = π/(2·AGM) ≈ log(x) + m·log(2), term2 = m·log(2) // cancel ≈ log2(|term1| / |log(x)|) double log_est = std::log(x.toDouble()); int target_bits = static_cast(std::ceil(compute_prec * 3.32192809488736)); int64_t m_est = (target_bits + 64) / 2 - ea; int cancel_bits = 0; if (std::abs(log_est) > 1e-300) { double term1_mag = std::abs(static_cast(m_est)) * 0.6931472 + std::abs(log_est); double ratio = term1_mag / std::abs(log_est); if (ratio > 1.0) { cancel_bits = static_cast(std::ceil(std::log2(ratio))); } } else { // x が 1 に極めて近い → 打ち消しが巨大。ガード 200 ビット追加。 cancel_bits = 200; } // ガードビット: MPFR 方式 + 追加マージン // 第二項 4/s² ≈ 4 ulp, 丸め誤差 ≈ 7 ulp, cancel 推定の不確実性を考慮 int q_bits = Float::precisionToBits(compute_prec); int log2_q = static_cast(std::ceil(std::log2(q_bits))); int p_bits = q_bits + 3 * log2_q + 20 + cancel_bits; int working_precision = Float::bitsToPrecision(p_bits); // m: s = x · 2^m, s > 2^{p/2} int64_t m = (p_bits + 3) / 2 - ea; // s = x · 2^m (指数シフトのみ、O(1)) Float s = ldexp(Float(x), static_cast(m)); s.setResultPrecision(working_precision); // 4/s Float four = Float::one(working_precision); four <<= 2; // 4 を working_precision で作成 Float four_over_s = four / s; four_over_s.setResultPrecision(working_precision); // AGM(1, 4/s) Float ag = agm(Float::one(working_precision), std::move(four_over_s), working_precision); // log(x) = π / (2 · AGM) − m · log(2) Float pi_val = Float::pi(working_precision); Float two_ag = ldexp(std::move(ag), 1); Float term1 = pi_val / two_ag; Float log2_val = Float::log2(working_precision); Float m_float = Float(m); m_float.setResultPrecision(working_precision); Float term2 = log2_val * m_float; Float result = term1 - term2; finalizeResult(result, eff_x, precision); return result; } // Q128 乗算: (a·b) >> 128 の上位 128 bit を返す static void q128_mul(uint64_t a_hi, uint64_t a_lo, uint64_t b_hi, uint64_t b_lo, uint64_t& r_hi, uint64_t& r_lo) { uint64_t ll_hi, lh_hi, hl_hi, hh_hi; /*ll_lo*/ _umul128(a_lo, b_lo, &ll_hi); uint64_t lh_lo = _umul128(a_lo, b_hi, &lh_hi); uint64_t hl_lo = _umul128(a_hi, b_lo, &hl_hi); uint64_t hh_lo = _umul128(a_hi, b_hi, &hh_hi); // p1 = ll_hi + lh_lo + hl_lo (carry → p2) uint64_t p1 = ll_hi; unsigned char c1 = _addcarry_u64(0, p1, lh_lo, &p1); unsigned char c2 = _addcarry_u64(0, p1, hl_lo, &p1); uint64_t carry_p2 = static_cast(c1) + static_cast(c2); // p2 = hh_lo + lh_hi + hl_hi + carry_p2 uint64_t p2 = hh_lo; unsigned char c3 = _addcarry_u64(0, p2, lh_hi, &p2); unsigned char c4 = _addcarry_u64(0, p2, hl_hi, &p2); unsigned char c5 = _addcarry_u64(0, p2, carry_p2, &p2); uint64_t carry_p3 = static_cast(c3) + static_cast(c4) + static_cast(c5); r_hi = hh_hi + carry_p3; r_lo = p2; } // ========================================================================= // Q256 固定小数点演算 (256-bit, 4 × uint64_t, w[0]=LSW, w[3]=MSW) // ========================================================================= // Q256 乗算: (a·b) >> 256 の上位 256 bit を返す // schoolbook 4×4 limb multiply, upper half static void q256_mul(const uint64_t a[4], const uint64_t b[4], uint64_t r[4]) { uint64_t w[8] = {}; for (int i = 0; i < 4; i++) { uint64_t carry = 0; for (int j = 0; j < 4; j++) { uint64_t hi; uint64_t lo = _umul128(a[i], b[j], &hi); unsigned char c1 = _addcarry_u64(0, w[i+j], lo, &w[i+j]); unsigned char c2 = _addcarry_u64(0, w[i+j], carry, &w[i+j]); carry = hi + static_cast(c1) + static_cast(c2); } if (i + 4 < 8) w[i + 4] += carry; } r[0] = w[4]; r[1] = w[5]; r[2] = w[6]; r[3] = w[7]; } // Q256 加算: r = a + b, return carry static unsigned char q256_add(const uint64_t a[4], const uint64_t b[4], uint64_t r[4]) { unsigned char c = _addcarry_u64(0, a[0], b[0], &r[0]); c = _addcarry_u64(c, a[1], b[1], &r[1]); c = _addcarry_u64(c, a[2], b[2], &r[2]); c = _addcarry_u64(c, a[3], b[3], &r[3]); return c; } // Q256 減算: r = a - b, return borrow static unsigned char q256_sub(const uint64_t a[4], const uint64_t b[4], uint64_t r[4]) { unsigned char c = _subborrow_u64(0, a[0], b[0], &r[0]); c = _subborrow_u64(c, a[1], b[1], &r[1]); c = _subborrow_u64(c, a[2], b[2], &r[2]); c = _subborrow_u64(c, a[3], b[3], &r[3]); return c; } // Q256 左シフト 1 bit static void q256_shl1(const uint64_t a[4], uint64_t r[4]) { r[0] = a[0] << 1; r[1] = (a[1] << 1) | (a[0] >> 63); r[2] = (a[2] << 1) | (a[1] >> 63); r[3] = (a[3] << 1) | (a[2] >> 63); } // Q256 スカラー除算: a / divisor (in-place) static void q256_div_scalar(uint64_t a[4], uint64_t divisor) { uint64_t rem = 0; a[3] = _udiv128(rem, a[3], divisor, &rem); a[2] = _udiv128(rem, a[2], divisor, &rem); a[1] = _udiv128(rem, a[1], divisor, &rem); a[0] = _udiv128(rem, a[0], divisor, &rem); } // Q256 がゼロか判定 static bool q256_is_zero(const uint64_t a[4]) { return a[0] == 0 && a[1] == 0 && a[2] == 0 && a[3] == 0; } // Q256 比較: a >= b static bool q256_ge(const uint64_t a[4], const uint64_t b[4]) { if (a[3] != b[3]) return a[3] > b[3]; if (a[2] != b[2]) return a[2] > b[2]; if (a[1] != b[1]) return a[1] > b[1]; return a[0] >= b[0]; } // Q256 → Float 変換 static Float q256_to_float(const uint64_t q[4], int64_t exponent) { // 上位の非ゼロワードを見つける int top = 3; while (top >= 0 && q[top] == 0) top--; if (top < 0) return Float::zero(); int nw = top + 1; auto val = Int::fromRawWordsPreNormalized( std::span(q, nw), 1); return Float(std::move(val), exponent, false); } // double → Q256 変換 static void double_to_q256(double val, uint64_t q[4]) { q[0] = q[1] = q[2] = q[3] = 0; if (val == 0.0) return; int exp; double m = std::frexp(val, &exp); uint64_t m_int = static_cast(m * static_cast(1ULL << 53)); // Q256: val * 2^256 = m_int * 2^(exp + 203) int bit_pos = exp + 203; int word_idx = bit_pos / 64; int bit_in_word = bit_pos % 64; if (word_idx >= 0 && word_idx < 4) { if (bit_in_word + 53 <= 64) { q[word_idx] = m_int << bit_in_word; } else { q[word_idx] = m_int << bit_in_word; if (word_idx + 1 < 4) q[word_idx + 1] = m_int >> (64 - bit_in_word); } } else if (word_idx == 4 && bit_in_word == 0) { // Overflow: val ≈ 1, shouldn't happen for atan args } } // Float → Q256 変換 (仮数部の上位 256 bit を配置, x ∈ (0, 1)) static void float_to_q256(const Float& x, uint64_t q[4]) { const Int& mant = x.mantissa(); const uint64_t* mw = mant.data(); size_t mn = mant.size(); int64_t e = x.exponent(); uint64_t top = mw[mn - 1]; int top_bits = 64 - std::countl_zero(top); int64_t total_bl = static_cast((mn - 1) * 64) + top_bits; // 仮数部を正規化 (MSB を bit 255 に配置) int clz = 64 - top_bits; uint64_t m[4] = {}; for (int i = 0; i < 4 && i < static_cast(mn); i++) { int src = static_cast(mn) - 1 - i; m[3 - i] = mw[src]; } // clz 分左シフト if (clz > 0) { for (int i = 3; i > 0; i--) m[i] = (m[i] << clz) | (m[i - 1] >> (64 - clz)); m[0] <<= clz; } // Q256 = x * 2^256 = m[3:0] * 2^(e + total_bl) int64_t sr = -(e + total_bl); if (sr <= 0) { q[0] = m[0]; q[1] = m[1]; q[2] = m[2]; q[3] = m[3]; } else if (sr < 64) { q[0] = (m[0] >> sr) | (m[1] << (64 - sr)); q[1] = (m[1] >> sr) | (m[2] << (64 - sr)); q[2] = (m[2] >> sr) | (m[3] << (64 - sr)); q[3] = m[3] >> sr; } else if (sr < 128) { int s = static_cast(sr - 64); q[0] = (m[1] >> s) | (m[2] << (64 - s)); q[1] = (m[2] >> s) | (m[3] << (64 - s)); q[2] = m[3] >> s; q[3] = 0; } else { q[0] = q[1] = q[2] = q[3] = 0; } } // Q256 除算: t = u / (2^128 + u) を Q256 で計算 (Knuth Algorithm D, 3 word 除数) // u = (u_hi:u_lo), u_hi < 2^63 (MSB clear) // 結果: t ∈ [0, 1/3) を Q256 で返す static void q256_atanh_arg(const uint64_t u[4], uint64_t q[4]) { // t = u / (2^256 + u) = (f-1)/(f+1) in Q256 // u: 255 bits (MSB of mantissa cleared), u[3] < 2^63 // 除数 v = 2^256 + u (257 bits) = {u[0], u[1], u[2], u[3], 1} (LE 5 words) // 正規化: 63 bit 左シフトで d[4] の MSB を立てる uint64_t d[5]; d[4] = (1ULL << 63) | (u[3] >> 1); d[3] = (u[3] << 63) | (u[2] >> 1); d[2] = (u[2] << 63) | (u[1] >> 1); d[1] = (u[1] << 63) | (u[0] >> 1); d[0] = u[0] << 63; // 被除数: u × 2^256 × 2^63 (正規化) in 9 words uint64_t n[9] = {}; n[4] = u[0] << 63; n[5] = (u[1] << 63) | (u[0] >> 1); n[6] = (u[2] << 63) | (u[1] >> 1); n[7] = (u[3] << 63) | (u[2] >> 1); n[8] = u[3] >> 1; // Algorithm D: 9 word ÷ 5 word → 4 word 商 for (int i = 3; i >= 0; i--) { uint64_t q_hat, r_hat; bool skip_correct = false; // Knuth D3: n[i+5] >= d[4] のとき _udiv128 はオーバーフロー if (n[i + 5] >= d[4]) { q_hat = UINT64_MAX; r_hat = n[i + 4] + d[4]; if (r_hat < d[4]) { skip_correct = true; } } else { q_hat = _udiv128(n[i + 5], n[i + 4], d[4], &r_hat); } // Knuth の 2 ワード補正 if (!skip_correct) { for (;;) { uint64_t ph, pl; pl = _umul128(q_hat, d[3], &ph); if (ph < r_hat || (ph == r_hat && pl <= n[i + 3])) break; q_hat--; r_hat += d[4]; if (r_hat < d[4]) break; } } // q_hat × d を計算して n[i..i+5] から引く uint64_t prod[6]; { uint64_t carry = 0; for (int j = 0; j < 5; j++) { uint64_t hi; uint64_t lo = _umul128(q_hat, d[j], &hi); unsigned char c = _addcarry_u64(0, lo, carry, &prod[j]); carry = hi + c; } prod[5] = carry; } unsigned char borrow = 0; for (int j = 0; j < 6; j++) { borrow = _subborrow_u64(borrow, n[i + j], prod[j], &n[i + j]); } if (borrow) { q_hat--; unsigned char c = 0; for (int j = 0; j < 5; j++) { c = _addcarry_u64(c, n[i + j], d[j], &n[i + j]); } n[i + 5] += c; } q[i] = q_hat; } } // Q128 → Float 変換ヘルパー static Float q128_to_float(uint64_t hi, uint64_t lo, int64_t exponent) { if (hi != 0) { uint64_t words[2] = { lo, hi }; auto val = Int::fromRawWordsPreNormalized( std::span(words, 2), 1); return Float(std::move(val), exponent, false); } else if (lo != 0) { return Float(Int(lo), exponent, false); } return Float::zero(); } // Q128 固定小数点 atanh 系列で log(f) を計算 (1 ワード仮数部) // x = f·2^k, f ∈ [1, 2), log(f) = 2·atanh((f-1)/(f+1)) // 戻り値: logf_hi:logf_lo (Q128), k (二進指数) // f = 1.0 の場合 logf = 0 を返す static void compute_logf_q128(const Float& x, uint64_t& logf_hi, uint64_t& logf_lo, int64_t& k) { const Int& mant = x.mantissa(); const uint64_t* mw = mant.data(); size_t mn = mant.size(); int64_t e = x.exponent(); // 仮数部の最上位ワードから 64-bit を取得 (little-endian: MSW = mw[mn-1]) uint64_t top = mw[mn - 1]; int top_bits = 64 - std::countl_zero(top); int64_t total_bl = static_cast((mn - 1) * 64) + top_bits; k = e + total_bl - 1; // m_norm: MSB が bit 63 に来るよう正規化 int clz = 64 - top_bits; uint64_t m_norm; if (clz == 0) { m_norm = top; } else if (mn >= 2) { m_norm = (top << clz) | (mw[mn - 2] >> (64 - clz)); } else { m_norm = top << clz; } uint64_t u_int = m_norm & 0x7FFFFFFFFFFFFFFFULL; if (u_int == 0) { logf_hi = logf_lo = 0; return; } // v_half = floor(v_int / 2), v_int = m_norm + 2^63 (65 bit) uint64_t v_half = (m_norm >> 1) + (1ULL << 62); // t = u/v in Q128: t·2^128 ≈ u_int·2^127 / v_half uint64_t num_hi = u_int >> 1; uint64_t num_lo = u_int << 63; uint64_t rem1; uint64_t t_hi = _udiv128(num_hi, num_lo, v_half, &rem1); uint64_t rem2; uint64_t t_lo = _udiv128(rem1, 0, v_half, &rem2); // t² in Q128 uint64_t tsq_hi, tsq_lo; q128_mul(t_hi, t_lo, t_hi, t_lo, tsq_hi, tsq_lo); // atanh(t) = t + t³/3 + t⁵/5 + ... を Q128 で計算 uint64_t sum_hi = t_hi, sum_lo = t_lo; uint64_t pow_hi = t_hi, pow_lo = t_lo; for (uint64_t d = 3; d < 200; d += 2) { q128_mul(pow_hi, pow_lo, tsq_hi, tsq_lo, pow_hi, pow_lo); uint64_t r; uint64_t term_hi = _udiv128(0, pow_hi, d, &r); uint64_t term_lo = _udiv128(r, pow_lo, d, &r); if (term_hi == 0 && term_lo == 0) break; unsigned char carry = _addcarry_u64(0, sum_lo, term_lo, &sum_lo); _addcarry_u64(carry, sum_hi, term_hi, &sum_hi); } // log(f) = 2·atanh(t) logf_hi = (sum_hi << 1) | (sum_lo >> 63); logf_lo = sum_lo << 1; } // Q256 固定小数点 atanh 系列で log(f) を計算 (2 ワード仮数部) // x = f·2^k, f ∈ [1, 2), log(f) = 2·atanh((f-1)/(f+1)) // 戻り値: logf[4] (Q256), k (二進指数) static void compute_logf_q256(const Float& x, uint64_t logf[4], int64_t& k) { const Int& mant = x.mantissa(); const uint64_t* mw = mant.data(); size_t mn = mant.size(); int64_t e = x.exponent(); uint64_t top = mw[mn - 1]; int top_bits = 64 - std::countl_zero(top); int64_t total_bl = static_cast((mn - 1) * 64) + top_bits; k = e + total_bl - 1; // 仮数部の上位 256 bit を取得 (MSB = bit 255) // m[3]:m[2]:m[1]:m[0] — MSB 寄せ (clz 分だけ左シフト) int clz = 64 - top_bits; uint64_t m[4] = {}; // 上位ワードから充填: mw[mn-1..mn-5] を clz ビット左シフトして m[3..0] に格納 // m[k] = (mw[src+1] << clz) | (mw[src] >> (64-clz)), src = mn - 1 - (3-k) if (clz == 0) { for (int i = 0; i < 4 && i < static_cast(mn); i++) m[3 - i] = mw[mn - 1 - i]; } else { for (int k = 3; k >= 0; k--) { int hi_idx = static_cast(mn) - 1 - (3 - k); // mw index for high part int lo_idx = hi_idx - 1; // mw index for low part uint64_t hi_part = (hi_idx >= 0 && hi_idx < static_cast(mn)) ? mw[hi_idx] : 0; uint64_t lo_part = (lo_idx >= 0 && lo_idx < static_cast(mn)) ? mw[lo_idx] : 0; m[k] = (hi_part << clz) | (lo_part >> (64 - clz)); } } // u = m - 2^255 (MSB clear) uint64_t u[4] = { m[0], m[1], m[2], m[3] & 0x7FFFFFFFFFFFFFFFULL }; if (q256_is_zero(u)) { logf[0] = logf[1] = logf[2] = logf[3] = 0; return; } // t = u / (2^256 + u) = (f-1)/(f+1) in Q256 uint64_t t[4]; q256_atanh_arg(u, t); // t² in Q256 uint64_t tsq[4]; q256_mul(t, t, tsq); // atanh(t) = t + t³/3 + t⁵/5 + ... in Q256 uint64_t sum[4] = { t[0], t[1], t[2], t[3] }; uint64_t pow[4] = { t[0], t[1], t[2], t[3] }; for (uint64_t d = 3; d < 200; d += 2) { q256_mul(pow, tsq, pow); uint64_t term[4] = { pow[0], pow[1], pow[2], pow[3] }; q256_div_scalar(term, d); if (q256_is_zero(term)) break; q256_add(sum, term, sum); } // log(f) = 2 × atanh(t) q256_shl1(sum, logf); } // exp Q128 ファストパス (19桁, precision_bits ≤ 64) // 引数削減 z = x - k·ln2 ∈ [0, ln2) → Q128 Taylor exp(z)-1 → (1+frac)·2^k static Float exp_small(const Float& x, int precision) { // double で k = floor(x/ln2) を決定 double x_d = x.toDouble(); constexpr double inv_ln2_d = 1.4426950408889634; int k = static_cast(std::floor(x_d * inv_ln2_d)); // ln(2) の Q128 定数 (ln2 = 0.6931471805599453... < 1, Q128 に収まる) constexpr uint64_t ln2_hi = 0xB17217F7D1CF79ABULL; constexpr uint64_t ln2_lo = 0xC9E3B39803F2F6AFULL; // x の仮数部を取得して Q128 で z = x - k·ln2 を正確に計算 // x = mantissa × 2^exponent, mantissa は正整数 (最大 64bit) bool x_neg = x.isNegative(); const Int& mant = x.mantissa(); const uint64_t* mw = mant.data(); uint64_t m_val = mw[0]; // precision ≤ 64bit なので 1 ワード int64_t e = x.exponent(); int m_bits = 64 - std::countl_zero(m_val); // x の絶対値 = m_val × 2^e // x × 2^128 = m_val × 2^(e+128) // Q128 は [0, 1) の値。x の整数部 + 小数部を扱うため、 // k·ln2 の整数部と x の整数部が相殺して z ∈ [0, ln2) になることを利用。 // k·ln2 を Q128+整数部で計算 // k·ln2 = k × (0.B17217F7D1CF79AB_C9E3B39803F2F6AF × 2^0) // 整数部: floor(k·ln2) = floor(k × 0.6931...) // k·ln2 × 2^128 の下位 128bit が小数部の Q128 表現 uint64_t abs_k = static_cast(k < 0 ? -k : k); uint64_t kln2_hi, kln2_lo; { uint64_t p0_hi; uint64_t p0_lo = _umul128(ln2_lo, abs_k, &p0_hi); uint64_t p1_hi; uint64_t p1_lo = _umul128(ln2_hi, abs_k, &p1_hi); unsigned char c = _addcarry_u64(0, p1_lo, p0_hi, &kln2_hi); (void)c; kln2_lo = p0_lo; // p1_hi は整数部 (k·ln2 の整数部 × 2^0)... 不要 } // x の小数部を Q128 で表現 // x = m_val × 2^e → 小数部 = frac(|x|) = frac(m_val × 2^e) // 整数部 = floor(|x|) // e+m_bits-1 = total_bits - 1 の位置が MSB // 例: x = 3.5 → m_val=7, e=-1, m_bits=3 // |x| = 7×2^(-1) = 3.5, 整数部=3, 小数部=0.5 int64_t total_bl = static_cast(m_bits) + e; // total_bl = x の二進桁数 (2^(total_bl-1) ≤ |x| < 2^total_bl) // 小数部を Q128 に: m_val を 2^(128 - total_bl) の位置に配置し、整数部ビットを除去 uint64_t x_frac_hi = 0, x_frac_lo = 0; if (total_bl <= 0) { // |x| < 1 → 整数部なし、全体が小数部 // m_val × 2^e × 2^128 = m_val × 2^(e+128) int shift = static_cast(e + 128 - m_bits); // m_val の MSB を bit (shift+m_bits-1) に // 実質 m_val << (128 + e - m_bits) ... ただし e < 0 で shift < 128 int pos = static_cast(128 + e); // m_val の LSB の位置 (from bit 0) // m_val を pos ビット左シフトして Q128 に配置 if (pos >= 64) { x_frac_hi = m_val << (pos - 64); } else if (pos >= 0) { x_frac_hi = m_val >> (64 - pos); x_frac_lo = m_val << pos; } } else if (total_bl < 64) { // 整数部あり: 整数部ビットをマスクして小数部のみ取り出す // m_val × 2^e の小数部 = (m_val mod 2^(-e)) × 2^e (e < 0 のとき) // = (m_val & ((1<<(-e))-1)) × 2^e if (e < 0) { uint64_t frac_mask = (1ULL << (-e)) - 1; uint64_t frac_val = m_val & frac_mask; if (frac_val != 0) { // frac_val × 2^e × 2^128 = frac_val × 2^(128+e) int pos = static_cast(128 + e); if (pos >= 64) { x_frac_hi = frac_val << (pos - 64); } else if (pos > 0) { x_frac_hi = frac_val >> (64 - pos); x_frac_lo = frac_val << pos; } } } // e >= 0 → 小数部なし (x_frac = 0) } // z = frac(|x|) - frac(|k|·ln2) (mod 2^128 の差) // Q128 減算結果は z mod 1 を正しく表現する (整数部の差はラップで相殺)。 // k の微調整は double 近似で判定する (Q128 の MSB/borrow では 0.5 や整数引数で誤判定)。 uint64_t z_hi, z_lo; if (!x_neg) { unsigned char borrow = _subborrow_u64(0, x_frac_lo, kln2_lo, &z_lo); _subborrow_u64(borrow, x_frac_hi, kln2_hi, &z_hi); } else { unsigned char borrow = _subborrow_u64(0, kln2_lo, x_frac_lo, &z_lo); _subborrow_u64(borrow, kln2_hi, x_frac_hi, &z_hi); } // k の微調整: double 近似 z で判定 (z < 0 → k--, z >= ln2 → k++) constexpr double ln2_d = 0.6931471805599453; double z_approx = std::fabs(x_d) - static_cast(abs_k) * ln2_d; if (x_neg) z_approx = static_cast(abs_k) * ln2_d - std::fabs(x_d); if (z_approx < 0) { // z < 0: k を 1 減らして z += ln2 k--; unsigned char c = _addcarry_u64(0, z_lo, ln2_lo, &z_lo); _addcarry_u64(c, z_hi, ln2_hi, &z_hi); } else if (z_approx >= ln2_d) { // z >= ln2: k を 1 増やして z -= ln2 k++; unsigned char b = _subborrow_u64(0, z_lo, ln2_lo, &z_lo); _subborrow_u64(b, z_hi, ln2_hi, &z_hi); } if (z_hi == 0 && z_lo == 0) { Float result = Float::one(precision); if (k > 0) result <<= k; else if (k < 0) result >>= -k; result.setResultPrecision(precision); return result; } // Q128 Taylor: exp(z) - 1 = z + z²/2! + z³/3! + ... uint64_t sum_hi = z_hi, sum_lo = z_lo; uint64_t term_hi = z_hi, term_lo = z_lo; for (int n = 2; n <= 30; n++) { q128_mul(term_hi, term_lo, z_hi, z_lo, term_hi, term_lo); uint64_t rem; term_hi = _udiv128(0, term_hi, static_cast(n), &rem); term_lo = _udiv128(rem, term_lo, static_cast(n), &rem); if (term_hi == 0 && term_lo == 0) break; unsigned char c = _addcarry_u64(0, sum_lo, term_lo, &sum_lo); _addcarry_u64(c, sum_hi, term_hi, &sum_hi); } // exp(z) = 1 + sum → 仮数 (2^128 + sum) × 2^{-128} uint64_t words[3] = { sum_lo, sum_hi, 1 }; auto mant2 = Int::fromRawWordsPreNormalized( std::span(words, 3), 1); Float result(std::move(mant2), -128, false); if (k > 0) result <<= k; else if (k < 0) result >>= -k; result.setResultPrecision(precision); return result; } // exp Q256 ファストパス (38桁, precision_bits ≤ 128) // Float で引数削減 → z を Q256 化 → Q256 Taylor → Float 復元 static Float exp_medium(const Float& x, int precision) { // Float で z = x - k·ln2 ∈ [0, ln2) を正確に計算 double x_d = x.toDouble(); constexpr double inv_ln2_d = 1.4426950408889634; int k = static_cast(std::floor(x_d * inv_ln2_d)); Float ln2_val = Float::log2(precision + 10); Float z_float = x - Float(k) * ln2_val; if (z_float.isNegative()) { z_float = z_float + ln2_val; k--; } if (z_float.isZero()) { Float result = Float::one(precision); if (k > 0) result <<= k; else if (k < 0) result >>= -k; result.setResultPrecision(precision); return result; } // z ∈ (0, ln2) ⊂ (0, 1) → Q256 に変換 uint64_t z[4]; float_to_q256(z_float, z); // Q256 Taylor: exp(z) - 1 = z + z²/2! + z³/3! + ... uint64_t sum[4] = { z[0], z[1], z[2], z[3] }; uint64_t term[4] = { z[0], z[1], z[2], z[3] }; for (int n = 2; n <= 60; n++) { q256_mul(term, z, term); q256_div_scalar(term, static_cast(n)); if (q256_is_zero(term)) break; q256_add(sum, term, sum); } // exp(z) = 1 + sum → 仮数 (2^256 + sum) × 2^{-256} uint64_t words[5] = { sum[0], sum[1], sum[2], sum[3], 1 }; auto mant2 = Int::fromRawWordsPreNormalized( std::span(words, 5), 1); Float result(std::move(mant2), -256, false); if (k > 0) result <<= k; else if (k < 0) result >>= -k; result.setResultPrecision(precision); return result; } // log ファストパス (≤ 64-bit 精度, 仮数部上位 64 bit で Q128 atanh 計算) static Float log_small(const Float& x, int precision) { uint64_t logf_hi, logf_lo; int64_t k; compute_logf_q128(x, logf_hi, logf_lo, k); if (logf_hi == 0 && logf_lo == 0) { if (k == 0) return Float::zero(); Float result = Float::log2(precision) * Float(k); result.setResultPrecision(precision); return result; } Float result = q128_to_float(logf_hi, logf_lo, -128); if (k != 0) { Float k_log2 = Float::log2(precision) * Float(k); k_log2.setResultPrecision(precision); result = result + k_log2; } result.setResultPrecision(precision); return result; } // log ミディアムパス (≤ 128-bit 精度, 仮数部上位 128 bit で Q256 atanh 計算) static Float log_medium(const Float& x, int precision) { uint64_t logf[4]; int64_t k; compute_logf_q256(x, logf, k); if (q256_is_zero(logf)) { if (k == 0) return Float::zero(); Float result = Float::log2(precision) * Float(k); result.setResultPrecision(precision); return result; } Float result = q256_to_float(logf, -256); if (k != 0) { Float k_log2 = Float::log2(precision) * Float(k); k_log2.setResultPrecision(precision); result = result + k_log2; } result.setResultPrecision(precision); return result; } // log2 Q256 ファストパス: log2(x) = log(f)·log2(e) + k // log2(e) = 1 + frac → log2(f) = logf + logf·frac(log2(e)) static Float log2_medium(const Float& x, int precision) { uint64_t logf[4]; int64_t k; compute_logf_q256(x, logf, k); if (q256_is_zero(logf)) { return Float(Int(k), 0, k < 0); } // log2(e) の小数部 = 0.44269504088896340735992468100189... // Q256: frac(log2(e)) × 2^256 constexpr uint64_t inv_ln2_frac[4] = { 0x164A2CD9A342648FULL, 0xD6AEF551BAD2B4B1ULL, 0x7D0FFDA0D23A7D11ULL, 0x71547652B82FE177ULL }; // log2(f) = logf + logf · frac(log2(e)) uint64_t prod[4]; q256_mul(logf, inv_ln2_frac, prod); uint64_t log2f[4]; q256_add(logf, prod, log2f); // result = log2(f) + k Float result = q256_to_float(log2f, -256); if (k != 0) { result = result + Float(Int(k), 0, k < 0); } result.setResultPrecision(precision); return result; } // log10 Q256 ファストパス: log10(x) = log(f)·log10(e) + k·log10(2) static Float log10_medium(const Float& x, int precision) { uint64_t logf[4]; int64_t k; compute_logf_q256(x, logf, k); // log10(e) = 1/ln(10) = 0.43429448190325182765112891891660508229... constexpr uint64_t log10e[4] = { 0x1D1F96A27BC7529EULL, 0x1F71A30122E4D101ULL, 0x9AADD557D699EE19ULL, 0x6F2DEC549B9438CAULL }; // log10(2) = 0.30102999566398119521373889472449302676... constexpr uint64_t log10_2[4] = { 0xC52F37935BE631E5ULL, 0x13569862A1E8F9A4ULL, 0x47C4ACD605BE48BCULL, 0x4D104D427DE7FBCCULL }; if (q256_is_zero(logf)) { if (k == 0) return Float::zero(); // log10(2^k) = k·log10(2) を Q256 で計算 uint64_t abs_k = static_cast(k < 0 ? -k : k); // Q256 × uint64 → Q256 (k は小さいので上位溢れなし) uint64_t kl2[4]; uint64_t carry = 0; for (int i = 0; i < 4; i++) { uint64_t hi; uint64_t lo = _umul128(log10_2[i], abs_k, &hi); unsigned char c = _addcarry_u64(0, lo, carry, &kl2[i]); carry = hi + c; } Float result = q256_to_float(kl2, -256); if (k < 0) result = -result; result.setResultPrecision(precision); return result; } // log10(f) = logf · log10(e) (Q256 × Q256 → Q256) uint64_t log10f[4]; q256_mul(logf, log10e, log10f); // k·log10(2) を Q256 で計算して加算 if (k != 0) { uint64_t abs_k = static_cast(k < 0 ? -k : k); uint64_t kl2[4]; uint64_t carry = 0; for (int i = 0; i < 4; i++) { uint64_t hi; uint64_t lo = _umul128(log10_2[i], abs_k, &hi); unsigned char c = _addcarry_u64(0, lo, carry, &kl2[i]); carry = hi + c; } if (k > 0) { q256_add(log10f, kl2, log10f); } else { // log10(x) = log10(f) - |k|·log10(2) // log10(f) ∈ [0,1), |k|·log10(2) > 0 // 比較して符号を決定 bool neg = false; for (int i = 3; i >= 0; i--) { if (log10f[i] > kl2[i]) break; if (log10f[i] < kl2[i]) { neg = true; break; } } if (neg) { uint64_t tmp[4]; q256_sub(kl2, log10f, tmp); Float result = q256_to_float(tmp, -256); result = -result; result.setResultPrecision(precision); return result; } else { q256_sub(log10f, kl2, log10f); } } } Float result = q256_to_float(log10f, -256); result.setResultPrecision(precision); return result; } // log2 ファストパス: log2(x) = log(f)/ln(2) + k = log(f)·log2(e) + k // log2(e) = 1 + 0.44269504... → log2(f) = logf + logf·frac(log2(e)) // log2(f) ∈ [0, 1) なので Q128 に収まる static Float log2_small(const Float& x, int precision) { uint64_t logf_hi, logf_lo; int64_t k; compute_logf_q128(x, logf_hi, logf_lo, k); if (logf_hi == 0 && logf_lo == 0) { return Float(Int(k), 0, k < 0); } // log2(e) の小数部 = 0.44269504088896340735992468100189... // Q128: 0.44269504... × 2^128 の hi:lo ワード // Python: int(0.44269504088896340735992468100189213742... * 2**128) // = 150675655792891826045981552050498960470 // hi = 150675655792891826045981552050498960470 >> 64 // = 8168882295414032698 = 0x71547652B82FE177 // lo = 150675655792891826045981552050498960470 & (2^64-1) // = 8927088712929562294 = 0x7BF3F01ADBC79AB6 constexpr uint64_t inv_ln2_frac_hi = 0x71547652B82FE177ULL; constexpr uint64_t inv_ln2_frac_lo = 0x7D0FFDA0D23A7D11ULL; // log2(f) = logf + logf * frac(log2(e)) uint64_t prod_hi, prod_lo; q128_mul(logf_hi, logf_lo, inv_ln2_frac_hi, inv_ln2_frac_lo, prod_hi, prod_lo); // log2f = logf + prod uint64_t log2f_lo, log2f_hi; unsigned char carry = _addcarry_u64(0, logf_lo, prod_lo, &log2f_lo); _addcarry_u64(carry, logf_hi, prod_hi, &log2f_hi); // result = log2(f) + k Float result = q128_to_float(log2f_hi, log2f_lo, -128); if (k != 0) { result = result + Float(Int(k), 0, k < 0); } result.setResultPrecision(precision); return result; } // log10 ファストパス: log10(x) = log(f)·log10(e) + k·log10(2) // log10(2) も Q128 で保持し、k·log10(2) を Float 定数計算なしに算出 static Float log10_small(const Float& x, int precision) { uint64_t logf_hi, logf_lo; int64_t k; compute_logf_q128(x, logf_hi, logf_lo, k); // log10(e) = 1/ln(10) = 0.43429448190325182765112891891660508229... // Q128: int(0.43429448190325182765112891891660508229... * 2**128) constexpr uint64_t log10e_hi = 0x6F2DEC549B9438CAULL; constexpr uint64_t log10e_lo = 0x9AADD557D699EE19ULL; // log10(2) = ln(2)/ln(10) = 0.30102999566398119521373889472449302676... // Q128: int(0.30102999566398119521373889472449302676... * 2**128) constexpr uint64_t log10_2_hi = 0x4D104D427DE7FBCCULL; constexpr uint64_t log10_2_lo = 0x47C4ACD605BE48BCULL; if (logf_hi == 0 && logf_lo == 0) { if (k == 0) return Float::zero(); // log10(2^k) = k·log10(2), k·log10(2) の整数部は高々 19 程度 // Q128 で k*log10_2 を計算: k は小さいので単純乗算 uint64_t abs_k = static_cast(k < 0 ? -k : k); uint64_t kl2_hi, kl2_lo; // Q128 × uint64 → Q128 (上位ワードの溢れは無視、k ≤ 64 なので安全) uint64_t mul_lo_hi; kl2_lo = _umul128(log10_2_lo, abs_k, &mul_lo_hi); uint64_t mul_hi_hi; uint64_t mul_hi_lo = _umul128(log10_2_hi, abs_k, &mul_hi_hi); unsigned char c = _addcarry_u64(0, mul_hi_lo, mul_lo_hi, &kl2_hi); (void)c; // mul_hi_hi への繰り上がりは整数部 (無視可) Float result = q128_to_float(kl2_hi, kl2_lo, -128); if (k < 0) result = -result; result.setResultPrecision(precision); return result; } // log10(f) = logf · log10(e) (Q128 × Q128 → Q128) uint64_t log10f_hi, log10f_lo; q128_mul(logf_hi, logf_lo, log10e_hi, log10e_lo, log10f_hi, log10f_lo); // k·log10(2) も Q128 で計算して加算 if (k != 0) { uint64_t abs_k = static_cast(k < 0 ? -k : k); uint64_t kl2_hi, kl2_lo; uint64_t mul_lo_hi; kl2_lo = _umul128(log10_2_lo, abs_k, &mul_lo_hi); uint64_t mul_hi_hi; uint64_t mul_hi_lo = _umul128(log10_2_hi, abs_k, &mul_hi_hi); unsigned char c = _addcarry_u64(0, mul_hi_lo, mul_lo_hi, &kl2_hi); (void)c; if (k > 0) { unsigned char carry = _addcarry_u64(0, log10f_lo, kl2_lo, &log10f_lo); _addcarry_u64(carry, log10f_hi, kl2_hi, &log10f_hi); } else { // log10f - kl2: log10(f) は正, k < 0 → log10(2^k) は負 // log10(x) = log10(f) + k·log10(2) = log10(f) - |k|·log10(2) // x < 1 の場合 result が負になりうる if (log10f_hi > kl2_hi || (log10f_hi == kl2_hi && log10f_lo >= kl2_lo)) { unsigned char borrow = 0; borrow = _subborrow_u64(0, log10f_lo, kl2_lo, &log10f_lo); _subborrow_u64(borrow, log10f_hi, kl2_hi, &log10f_hi); } else { // 結果が負 unsigned char borrow = 0; uint64_t neg_lo, neg_hi; borrow = _subborrow_u64(0, kl2_lo, log10f_lo, &neg_lo); _subborrow_u64(borrow, kl2_hi, log10f_hi, &neg_hi); Float result = q128_to_float(neg_hi, neg_lo, -128); result = -result; result.setResultPrecision(precision); return result; } } } Float result = q128_to_float(log10f_hi, log10f_lo, -128); result.setResultPrecision(precision); return result; } // Newton 反復による log(f) 計算ヘルパー (f ∈ [1,2)) // 戻り値: log(f) (自然対数), k (二進指数 x = f·2^k) // 初期近似: std::log(double) (~53 bit) + Newton 精度倍増 // 第 1 ステップ: exp_small (Q128 Taylor) — 1 ワード仮数部のまま呼び出し // 重要: setResultPrecision は mantissa を左シフト拡張するため、 // exp_small 呼び出し前には setPrecision しない。 // 第 2 ステップ以降: expDoubling (RawFloat Taylor+二乗復元) // 対象: ≤ 2000 ビット (~600 桁)。600 桁超では AGM が上回る。 // (LogfResult は前方宣言済み) LogfResult logf_newton_core(Float x, int compute_prec) { // x = f · 2^k, f ∈ [1, 2) int64_t ea = x.exponent() + static_cast(x.mantissa().bitLength()); int64_t k = ea - 1; // f = x · 2^(-k) Float f = ldexp(std::move(x), static_cast(-k)); // f = 1.0 exactly ⟺ mantissa = 1 (奇数正規化で LSB=1) if (f.mantissa().bitLength() == 1) { return { Float::zero(), k, true }; } int target_bits = Float::precisionToBits(compute_prec); int guard = 32; // 初期近似の選択 Float y; int correct_bits; if (target_bits <= 230) { // Q256 直接範囲内: double 初期近似 (1-2 反復で完了) double fd = f.toDouble(); y = Float(std::log(fd)); correct_bits = 50; } else { // 大精度: Q256 atanh で ~240 ビット初期近似 → 反復回数削減 // 240→720→2160 vs 50→150→450→1350→4050 // 250d (830b): 3反復→2反復, 500d (1660b): 4反復→2反復 uint64_t logf_q256[4]; int64_t k_q256; compute_logf_q256(f, logf_q256, k_q256); if (q256_is_zero(logf_q256)) { return { Float::zero(), k, true }; } y = q256_to_float(logf_q256, -256); correct_bits = 230; } // Halley 精度3倍増ループ: 50 → 150 → 450 → 1350 → ... // Halley: y_{n+1} = y_n + 2·(f - exp(y_n)) / (f + exp(y_n)) // 3次収束: 各ステップで正確ビット数が3倍 (Newton は2倍) while (correct_bits < target_bits) { int compute_bits = std::min(3 * correct_bits + guard, target_bits + guard); int step_prec = Float::bitsToPrecision(compute_bits); // exp(y_n) の計算: y ∈ [0, ln2) なので引数縮小不要 // exp_small: Q128 Taylor, 仮数部 1 ワード & step_prec ≤ 19 のみ // ※ setResultPrecision は mantissa を左シフト拡張して仮数部が // 複数ワードになるため、exp_small 判定の前には呼ばない。 // exp_medium: Q256 Taylor, step_prec ≤ 38 // expDoubling: RawFloat Taylor+二乗復元 (汎用) Float e_y; int step_bits = Float::precisionToBits(step_prec); if (y.mantissa().size() <= 1 && step_bits <= 120) { // Q128 Taylor ファストパス: 1 ワード入力, ~128-bit 出力 // step_bits ≤ 120 (ガードマージン 8 bit) で使用 e_y = exp_small(y, step_prec); } else if (step_bits <= 240) { // Q256 Taylor: ~256-bit 出力 y.setEffectiveBits(step_bits); y.setResultPrecision(step_prec); e_y = exp_medium(y, step_prec); } else { y.setEffectiveBits(step_bits); y.setResultPrecision(step_prec); e_y = expDoubling(y, step_prec); } // Halley 補正: 2·(f - t) / (f + t) where t = exp(y) // f, t ∈ [1, 2) なので f+t > 0, 数値的に安定 Float f_prec(f); f_prec.truncateToApprox(step_prec); Float num = f_prec - e_y; // f - t (近似的に 0 付近) Float den = f_prec + e_y; // f + t (≈ 2f) Float correction = (num + num) / den; // 2·(f-t)/(f+t) y = y + correction; y.truncateToApprox(step_prec); correct_bits = std::min(3 * correct_bits, compute_bits); } return { std::move(y), k, false }; } // log(x) = log(f) + k · log(2) static Float log_newton(Float x, int eff_x, int precision) { int compute_prec = effectiveComputePrecision(eff_x, precision); auto [logf, k, f_is_one] = logf_newton_core(std::move(x), compute_prec); if (f_is_one) { if (k == 0) return Float::zero(); Float result = Float::log2(compute_prec) * Float(k); finalizeResult(result, eff_x, precision); return result; } if (k != 0) { logf = logf + Float::log2(compute_prec) * Float(k); } finalizeResult(logf, eff_x, precision); return logf; } // log2(x) = log(f) * log2(e) + k (除算を乗算に置換) static Float log2_newton(Float x, int eff_x, int precision) { int compute_prec = effectiveComputePrecision(eff_x, precision); auto [logf, k, f_is_one] = logf_newton_core(std::move(x), compute_prec); if (f_is_one) { if (k == 0) return Float::zero(); Float result(k); finalizeResult(result, eff_x, precision); return result; } // log2(e) = 1/log(2) — 乗算は除算の約 2-3 倍高速 Float log2e = Float::one(compute_prec) / Float::log2(compute_prec); Float result = logf * log2e; if (k != 0) { result = result + Float(k); } finalizeResult(result, eff_x, precision); return result; } // log10(x) = log(f) * log10(e) + k · log10(2) (除算を乗算に置換) static Float log10_newton(Float x, int eff_x, int precision) { int compute_prec = effectiveComputePrecision(eff_x, precision); auto [logf, k, f_is_one] = logf_newton_core(std::move(x), compute_prec); if (f_is_one) { if (k == 0) return Float::zero(); // log10(2^k) = k · log10(2) = k · log(2)/log(10) Float result = Float::log2(compute_prec) * Float(k) / Float::log10(compute_prec); finalizeResult(result, eff_x, precision); return result; } // log10(e) = 1/log(10) — 乗算は除算の約 2-3 倍高速 Float log10e = Float::one(compute_prec) / Float::log10(compute_prec); Float result = logf * log10e; if (k != 0) { // log10(2) = log(2)/log(10) = log(2) · log10(e) Float log10_2 = Float::log2(compute_prec) * log10e; result = result + log10_2 * Float(k); } finalizeResult(result, eff_x, precision); return result; } // log 内部ディスパッチ (精度に応じて最適なアルゴリズムを選択) static Float log_dispatch(const Float& x, int eff_x, int precision) { int precision_bits = Float::precisionToBits(precision); if (precision_bits <= 64) { return log_newton(Float(x), eff_x, precision); } if (precision_bits <= 230) { return log_medium(x, precision); } if (precision_bits <= 4000) { return log_newton(Float(x), eff_x, precision); } if (precision_bits <= 6000) { return log_multiprime(Float(x), eff_x, precision); } return log_core(Float(x), eff_x, precision); } Float log(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity() && !x.isNegative()) return Float::positiveInfinity(); if (x.isZero()) return Float::negativeInfinity(); if (x.isNegative()) return Float::nan(); if (x == Float::one()) return Float::zero(); return zivRound(log_dispatch, x, x.effectiveBits(), precision); } Float log(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity() && !x.isNegative()) return Float::positiveInfinity(); if (x.isZero()) return Float::negativeInfinity(); if (x.isNegative()) return Float::nan(); if (x == Float::one()) return Float::zero(); return zivRound(log_dispatch, x, x.effectiveBits(), precision); } //============================================================================= // log_ui — 正の整数の自然対数 //============================================================================= Float logUi(unsigned long long n, int precision) { if (n == 0) return Float::negativeInfinity(); if (n == 1) return Float::zero(precision); int precision_bits = Float::precisionToBits(precision); int wp = precision_bits + 10; // 小素数で試し割りし、log(n) = Σ e_i·log(p_i) + log(残余) と分解 unsigned long long rem = n; int exponents[MP_NUM_PRIMES] = {}; for (int i = 0; i < MP_NUM_PRIMES && rem > 1; ++i) { unsigned long long p = static_cast(MP_PRIMES[i]); while (rem % p == 0) { exponents[i]++; rem /= p; } } // 完全に分解できた場合: Σ e_i·log(p_i) // 残余がある場合: Σ e_i·log(p_i) + log(Float(rem)) bool has_prime_part = false; for (int i = 0; i < MP_NUM_PRIMES; ++i) { if (exponents[i] != 0) { has_prime_part = true; break; } } if (!has_prime_part) { // 小素数に分解できなかった → 直接 log return log(Float(static_cast(n)), precision); } // 素因数分解部分の合計 Float result = Float::zero(wp); for (int i = 0; i < MP_NUM_PRIMES; ++i) { if (exponents[i] == 0) continue; const Float& lp = getLogPrime(i, wp); if (exponents[i] == 1) result = result + lp; else result = result + lp * Float(exponents[i]); } // 残余がある場合 if (rem > 1) { result = result + log(Float(static_cast(rem)), wp); } result.setResultPrecision(precision); return result; } //============================================================================= // 平方根 (sqrt) — 整数 sqrtRem ベースの correct rounding //============================================================================= static Float sqrt_isqrt(Float x, int precision) { // 方針: sqrt(mantissa * 2^exp) を整数平方根に帰着。 // mantissa を 2*(p+1) ビットの整数にスケーリングし、 // isqrt で (p+1) ビットの根を得る。 // dc_sqrtrem の余りを直接検査して sticky bit を判定 (M(n) の二乗計算を省略)。 int precision_bits = Float::precisionToBits(precision); int target_root_bits = precision_bits + 1; // 仮数部と指数部を取得 const Int& m = x.mantissa(); int64_t e = x.exponent(); size_t mn = m.size(); int bl = static_cast(m.bitLength()); // スケーリング量の計算 int desired_bits = 2 * target_root_bits; int base_shift = desired_bits - bl; if (base_shift < 0) base_shift = 0; int64_t adjusted_exp = e - base_shift; if (adjusted_exp % 2 != 0) { base_shift += 1; adjusted_exp -= 1; } int64_t result_exponent = adjusted_exp / 2; // mpn レベルでスケーリング + sqrtrem (IntSqrt::sqrtRem をバイパス) ScratchScope scope; auto& arena = getThreadArena(); // スケーリング: scaled = m << base_shift (mpn レベル) size_t word_shift = static_cast(base_shift) / 64; unsigned bit_shift = static_cast(base_shift) % 64; size_t an = mn + word_shift + (bit_shift > 0 ? 1 : 0); uint64_t* ap = arena.alloc_limbs(an + 1); std::memset(ap, 0, (an + 1) * sizeof(uint64_t)); const uint64_t* mw = m.data(); for (size_t i = 0; i < mn; i++) ap[i + word_shift] = mw[i]; if (bit_shift > 0) { ap[mn + word_shift] = mpn::lshift(ap + word_shift, ap + word_shift, mn, bit_shift); } // 正規化サイズ while (an > 0 && ap[an - 1] == 0) an--; if (an == 0) { return Float::zero(); } // sqrtrem_check_exact: 余りゼロ判定付き sqrt (M(n) 二乗省略) size_t sn = (an + 1) / 2; uint64_t* sp = arena.alloc_limbs(sn); size_t scratch_sz = mpn::sqrtrem_scratch_size(an); uint64_t* scratch = arena.alloc_limbs(scratch_sz); auto [sp_n, exact] = mpn::sqrtrem_check_exact(sp, ap, an, scratch); // Sticky bit: exact でないなら LSB を 1 にセット if (!exact && sp_n > 0) { sp[0] |= 1; } // Int を構築 (SBO: 小サイズならヒープ確保なし) Int s = (sp_n > 0) ? Int::fromRawWords(std::span(sp, sp_n), 1) : Int::Zero(); Float result(std::move(s), result_exponent, false); result.setResultPrecision(precision); return result; } //============================================================================= // 平方根 (sqrt) — Newton 逆数平方根 (YC-7) // r_{n+1} = r_n * (3 - x * r_n²) / 2 (除算不要、乗算のみ) // sqrt(x) = x * r_n //============================================================================= // Newton invsqrt vs isqrt ベンチマーク結果 (2026-03-16, Zen3, AVX2 NTT): // 2M桁: isqrt 318ms, Newton 528ms (isqrt 40% 高速) // 10M桁: isqrt 3208ms, Newton 4021ms (isqrt 20% 高速) // → isqrt (整数 sqrtRem) が全サイズで優秀。Newton パスは削除候補。 static Float sqrt_core(Float x, int /*eff_x*/, int precision) { return sqrt_isqrt(std::move(x), precision); } // sqrt 超高速パス: 1 ワード仮数部 + double sqrt + Newton 1 回 // double sqrt (~53 bit) → _udiv128 Newton → _umul128 補正 // Float 抽象層を完全にバイパス static Float sqrt_newton_1(const Float& x, int precision) { const Int& mant = x.mantissa(); const uint64_t* mw = mant.data(); int64_t e = x.exponent(); int bl = static_cast(64 - std::countl_zero(mw[0])); // 128 ビットにスケーリング (desired_bits=126: パリティ調整+1 でも 127 ≤ 128) int base_shift = 126 - bl; if (base_shift < 0) base_shift = 0; int64_t adjusted_exp = e - base_shift; if (adjusted_exp % 2 != 0) { base_shift += 1; adjusted_exp -= 1; } int64_t result_exponent = adjusted_exp / 2; // M = mw[0] << base_shift (128 ビット: M_hi:M_lo) uint64_t m = mw[0]; uint64_t M_hi, M_lo; if (base_shift == 0) { M_hi = 0; M_lo = m; } else if (base_shift < 64) { M_hi = m >> (64 - base_shift); M_lo = m << base_shift; } else if (base_shift == 64) { M_hi = m; M_lo = 0; } else { M_hi = m << (base_shift - 64); M_lo = 0; } // 初期値: double sqrt (~53 ビット精度) double M_dbl = static_cast(M_hi) * 0x1p64 + static_cast(M_lo); uint64_t s0 = static_cast(std::sqrt(M_dbl)); if (s0 == 0) s0 = 1; // Newton 反復: s1 = (s0 + M/s0) / 2 // _udiv128 は M_hi < s0 が必要 (商が 64 ビットに収まる条件) while (M_hi >= s0) s0++; uint64_t rem; uint64_t q = _udiv128(M_hi, M_lo, s0, &rem); // オーバーフロー安全な (s0 + q) / 2 uint64_t s1 = (s0 >> 1) + (q >> 1) + ((s0 & q) & 1); // 補正: s1² ≤ M < (s1+1)² を保証 (最大 ±1 の調整) uint64_t sq_hi, sq_lo; sq_lo = _umul128(s1, s1, &sq_hi); // s1² > M なら s1 を下げる if (sq_hi > M_hi || (sq_hi == M_hi && sq_lo > M_lo)) { s1--; sq_lo = _umul128(s1, s1, &sq_hi); } // (s1+1)² ≤ M なら s1 を上げる uint64_t sq1_hi, sq1_lo; sq1_lo = _umul128(s1 + 1, s1 + 1, &sq1_hi); if (sq1_hi < M_hi || (sq1_hi == M_hi && sq1_lo <= M_lo)) { s1++; sq_hi = sq1_hi; sq_lo = sq1_lo; } // 余りチェック (sticky bit) bool has_remainder = (sq_hi != M_hi || sq_lo != M_lo); if (has_remainder) { s1 |= 1; } Int s_int(s1); Float result(std::move(s_int), result_exponent, false); result.setResultPrecision(precision); return result; } // sqrt ファストパス: 仮数部 1-2 ワード、dc_sqrtrem(n=2) で Int のオーバーヘッドを回避 // precision_bits <= 128 (結果は最大 2 ワード) static Float sqrt_small_2(const Float& x, int precision) { int precision_bits = Float::precisionToBits(precision); int target_root_bits = precision_bits + 1; const Int& mant = x.mantissa(); const uint64_t* mw = mant.data(); size_t mn = mant.size(); int64_t e = x.exponent(); int bl = static_cast((mn - 1) * 64 + (64 - std::countl_zero(mw[mn - 1]))); // 4 ワード (256 ビット) バッファに収めるため desired_bits を 255 以下に制限 // パリティ調整で +1 されても 256 ビット以内 int desired_bits = std::min(2 * target_root_bits, 255); int base_shift = desired_bits - bl; if (base_shift < 0) base_shift = 0; int64_t adjusted_exp = e - base_shift; if (adjusted_exp % 2 != 0) { base_shift += 1; adjusted_exp -= 1; } int64_t result_exponent = adjusted_exp / 2; // 4 ワードバッファにスケーリング uint64_t scaled[6] = {0, 0, 0, 0, 0, 0}; // dc_sqrtrem は np[n..n+2l-1] を使うため +2 for (size_t i = 0; i < mn; i++) scaled[i] = mw[i]; if (base_shift > 0) { size_t word_shift = static_cast(base_shift) / 64; unsigned bit_shift = static_cast(base_shift) % 64; if (word_shift > 0) { for (int i = 3; i >= static_cast(word_shift); i--) scaled[i] = scaled[i - word_shift]; for (size_t i = 0; i < word_shift; i++) scaled[i] = 0; } if (bit_shift > 0) { for (int i = 3; i > 0; i--) scaled[i] = (scaled[i] << bit_shift) | (scaled[i - 1] >> (64 - bit_shift)); scaled[0] <<= bit_shift; } } // dc_sqrtrem 用の正規化: scaled[3] >= 2^62 が必要 // scaled[3] が 0 の場合はワード単位シフトも必要 unsigned total_norm = 0; if (scaled[3] == 0) { // scaled[3] が 0 → scaled[2] にデータがある unsigned clz2 = static_cast(std::countl_zero(scaled[2])); total_norm = 64 + (clz2 & ~1u); } else { unsigned clz = static_cast(std::countl_zero(scaled[3])); total_norm = clz & ~1u; } if (total_norm > 0) { size_t word_norm = total_norm / 64; unsigned bit_norm = total_norm % 64; if (word_norm > 0) { for (int i = 3; i >= static_cast(word_norm); i--) scaled[i] = scaled[i - word_norm]; for (size_t i = 0; i < word_norm; i++) scaled[i] = 0; } if (bit_norm > 0) { for (int i = 3; i > 0; i--) scaled[i] = (scaled[i] << bit_norm) | (scaled[i - 1] >> (64 - bit_norm)); scaled[0] <<= bit_norm; } } // dc_sqrtrem(sp, np, n=2, scratch) // sp[0..1] = floor(sqrt(scaled[0..3])), 余りは scaled[0..1] に格納 uint64_t sp[2] = {0, 0}; uint64_t scratch[16]; // dc_sqrtrem_scratch_size(2) ≈ 9 int carry = mpn::dc_sqrtrem(sp, scaled, 2, scratch); // 余りチェック (正規化された入力に対して) bool has_remainder = (carry != 0 || scaled[0] != 0 || scaled[1] != 0); // 逆正規化: root を total_norm/2 ビット右シフト unsigned root_shift = total_norm / 2; if (root_shift > 0) { if (root_shift >= 64) { // ワード跨ぎ: sp[0] = sp[1] >> (root_shift - 64) sp[0] = sp[1] >> (root_shift - 64); sp[1] = 0; } else { sp[0] = (sp[0] >> root_shift) | (sp[1] << (64 - root_shift)); sp[1] >>= root_shift; } } if (has_remainder) { sp[0] |= 1; } // Int を 2 ワードから構築 (SBO: ヒープ確保なし) size_t sp_n = (sp[1] != 0) ? 2 : 1; auto s_int = Int::fromRawWordsPreNormalized( std::span(sp, sp_n), 1); Float result(std::move(s_int), result_exponent, false); result.setResultPrecision(precision); return result; } //============================================================================= // 二乗 (sqr) — IntOps::square による専用自乗 //============================================================================= Float sqr(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::positiveInfinity(); if (x.isZero()) { Float z; z.effective_bits_ = x.effective_bits_; z.requested_bits_ = x.requested_bits_; return z; } int eff = x.effectiveBits(); // オペランド切り詰め const Int* xp = &x.mantissa_; Int x_trunc; int64_t x_exp = x.exponent_; if (eff < INT_MAX) { int compute_bits = eff + 32; int bl = static_cast(xp->bitLength()); if (bl > compute_bits) { int words_to_drop = (bl - compute_bits) / 64; if (words_to_drop > 0) { int shift = words_to_drop * 64; x_trunc = *xp; x_trunc >>= shift; x_exp += shift; xp = &x_trunc; } } } // IntOps::square で専用自乗 Int result_mantissa; IntOps::square(*xp, result_mantissa); int64_t result_exponent = x_exp * 2; Float result(std::move(result_mantissa), result_exponent, false); // x² は常に正 result.effective_bits_ = eff; result.requested_bits_ = x.requested_bits_; finalizeResult(result, eff, precision); return result; } Float sqr(Float&& x, int precision) { return sqr(static_cast(x), precision); } Float sqrt(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isNegative()) return Float::nan(); if (x.isZero()) return Float::zero(); if (x.isInfinity()) return Float::positiveInfinity(); // ファストパス size_t mn = x.mantissa().size(); int precision_bits = Float::precisionToBits(precision); if (mn == 1 && precision_bits <= 64) { return sqrt_newton_1(x, precision); } if (mn <= 2 && precision_bits <= 128) { return sqrt_small_2(x, precision); } // isqrt が全サイズで Newton より高速 → 常に isqrt を使用 return sqrt_core(Float(x), x.effectiveBits(), precision); } Float sqrt(Float&& x, int precision) { // rvalue 版は const-ref 版に委譲 (Newton パスは内部でコピーするため差なし) return sqrt(static_cast(x), precision); } //============================================================================= // 正弦関数 (sin) / 余弦関数 (cos) 共通 //============================================================================= static Float sinTaylor(const Float& x, int working_precision) { // sin(x) = x - x³/3! + x⁵/5! - ... // RawFloat で直接計算 (ループ内アロケーションゼロ) int wp_bits = Float::precisionToBits(working_precision); int nw = (wp_bits + 63) / 64; ScratchScope scope; auto& arena = getThreadArena(); // バッファ事前確保 size_t prod_alloc = 2 * static_cast(nw) + 4; uint64_t* prod_buf = arena.alloc_limbs(prod_alloc); size_t mul_scratch = mpn::multiply_scratch_size(nw + 1, nw + 1); size_t sqr_scratch = mpn::square_scratch_size(nw + 1); size_t scratch_sz = std::max(mul_scratch, sqr_scratch); uint64_t* scratch = arena.alloc_limbs(scratch_sz > 0 ? scratch_sz : 1); // |x| を抽出 RawFloat x_rf = rf_extract(x, nw, arena); bool x_negative = x.isNegative(); // x² を事前計算 (自乗は square で高速化) uint64_t* x2_d = arena.alloc_limbs(nw + 2); RawFloat x2{x2_d, 0, 0}; rf_sqr(x2, x_rf, prod_buf, scratch, nw); uint64_t* result_d = arena.alloc_limbs(nw + 2); std::memset(result_d, 0, (nw + 2) * sizeof(uint64_t)); if (wp_bits >= 10000) { // Paterson-Stockmeyer: O(sqrt(l)) 回のフルサイズ乗算 RawFloat result{result_d, 0, 0}; rf_sin_ps(result, x_rf, x2, nw, prod_buf, scratch, arena, working_precision); return rf_to_float(result, x_negative, working_precision); } // 素朴な Taylor 級数 uint64_t* term_d = arena.alloc_limbs(nw + 2); std::memset(term_d, 0, (nw + 2) * sizeof(uint64_t)); std::memcpy(term_d, x_rf.d, x_rf.nw * sizeof(uint64_t)); RawFloat term{term_d, x_rf.nw, x_rf.exp}; std::memcpy(result_d, x_rf.d, x_rf.nw * sizeof(uint64_t)); RawFloat result{result_d, x_rf.nw, x_rf.exp}; int max_terms = static_cast(working_precision * 1.2) + 10; for (int k = 1; k <= max_terms; ++k) { rf_mul(term, term, x2, prod_buf, scratch, nw); uint64_t divisor = static_cast(2*k) * static_cast(2*k + 1); rf_divmod_1(term, divisor); if (term.nw == 0) break; if (k & 1) { if (!rf_sub(result, term, nw + 1)) break; } else { if (!rf_add(result, term, nw + 1)) break; } } return rf_to_float(result, x_negative, working_precision); } // Taylor級数によるcos(x)の内部計算 (|x| <= π/4 を想定) static Float cosTaylor(const Float& x, int working_precision) { // cos(x) = 1 - x²/2! + x⁴/4! - ... // RawFloat で直接計算 (ループ内アロケーションゼロ) int wp_bits = Float::precisionToBits(working_precision); int nw = (wp_bits + 63) / 64; ScratchScope scope; auto& arena = getThreadArena(); // バッファ事前確保 size_t prod_alloc = 2 * static_cast(nw) + 4; uint64_t* prod_buf = arena.alloc_limbs(prod_alloc); size_t mul_scratch = mpn::multiply_scratch_size(nw + 1, nw + 1); size_t sqr_scratch = mpn::square_scratch_size(nw + 1); size_t scratch_sz = std::max(mul_scratch, sqr_scratch); uint64_t* scratch = arena.alloc_limbs(scratch_sz > 0 ? scratch_sz : 1); // |x| を抽出 RawFloat x_rf = rf_extract(x, nw, arena); // x² を事前計算 (自乗は square で高速化) uint64_t* x2_d = arena.alloc_limbs(nw + 2); RawFloat x2{x2_d, 0, 0}; rf_sqr(x2, x_rf, prod_buf, scratch, nw); uint64_t* result_d = arena.alloc_limbs(nw + 2); std::memset(result_d, 0, (nw + 2) * sizeof(uint64_t)); if (wp_bits >= 10000) { // Paterson-Stockmeyer: O(sqrt(l)) 回のフルサイズ乗算 RawFloat result{result_d, 0, 0}; rf_cos_ps(result, x2, nw, prod_buf, scratch, arena, working_precision); return rf_to_float(result, false, working_precision); } // 素朴な Taylor 級数 uint64_t* term_d = arena.alloc_limbs(nw + 2); std::memset(term_d, 0, (nw + 2) * sizeof(uint64_t)); term_d[nw - 1] = uint64_t(1) << 63; RawFloat term{term_d, static_cast(nw), -static_cast(nw * 64 - 1)}; result_d[nw - 1] = uint64_t(1) << 63; RawFloat result{result_d, static_cast(nw), -static_cast(nw * 64 - 1)}; int max_terms = static_cast(working_precision * 1.2) + 10; for (int k = 1; k <= max_terms; ++k) { rf_mul(term, term, x2, prod_buf, scratch, nw); uint64_t divisor = static_cast(2*k - 1) * static_cast(2*k); rf_divmod_1(term, divisor); if (term.nw == 0) break; if (k & 1) { if (!rf_sub(result, term, nw + 1)) break; } else { if (!rf_add(result, term, nw + 1)) break; } } // cos(x) = cos(-x) ≥ 0 (引数縮小後は |x| ≤ π/4 なのでcos > 0) return rf_to_float(result, false, working_precision); } // 倍角公式による cos 計算: K 回半減 → cosTaylor → K 回倍角復元 // cos(2θ) = 2cos²(θ) - 1 // x は [0, π/2] の範囲を想定 static Float cosDoubling(const Float& x, int working_precision) { int wp_bits = Float::precisionToBits(working_precision); int K; if (wp_bits >= 10000) { // P-S で Taylor が O(√N) → K を削減して復元コスト削減 // 最適 K: min(K + 3√(p/(2K))) → K = cbrt(wp_bits) K = static_cast(std::cbrt(static_cast(wp_bits))); } else { K = static_cast(std::sqrt(wp_bits / 2.0)); } if (K < 1) return cosTaylor(x, working_precision); // Guard bits: 倍角復元で誤差が最大 4^K 倍に増幅されるため 2K+α ビット追加 int guard_bits = 2 * K + static_cast(std::ceil(std::log2(K + 1))) + 10; int wp_inner = working_precision + Float::bitsToPrecision(guard_bits); Float x_red = ldexp(x, -K); // x / 2^K — O(1) の指数操作 x_red.truncateToApprox(wp_inner); // 入力が double (eff=53) でも Taylor を wp_inner 精度で計算させる。 // truncateToApprox で仮数部は wp_inner ビットにパディング済みなので正確。 // 注意: INT_MAX ではなく wp_inner ビットを設定。INT_MAX だと除算で // "両方 exact" 判定になり defaultPrecision にフォールバックしてしまう。 int wp_inner_bits = Float::precisionToBits(wp_inner); x_red.setEffectiveBits(wp_inner_bits); Float c = cosTaylor(x_red, wp_inner); // K 回倍角復元 (RawFloat — Float per-op オーバーヘッドを回避) // cos(2θ) = 2cos²(θ) - 1 int nw = (wp_inner_bits + 63) / 64; ScratchScope scope; auto& arena = getThreadArena(); uint64_t* c_d = arena.alloc_limbs(nw + 2); RawFloat c_rf = rf_extract(c, nw, arena); // c_rf のデータを c_d にコピー (rf_extract は別バッファに確保するため) std::memcpy(c_d, c_rf.d, c_rf.nw * sizeof(uint64_t)); c_rf.d = c_d; size_t prod_alloc = 2 * static_cast(nw) + 4; uint64_t* prod_buf = arena.alloc_limbs(prod_alloc); size_t sqr_scratch = mpn::square_scratch_size(nw + 1); size_t scratch_sz = std::max(mpn::multiply_scratch_size(nw + 1, nw + 1), sqr_scratch); uint64_t* scratch_buf = arena.alloc_limbs(scratch_sz > 0 ? scratch_sz : 1); bool c_negative = false; for (int i = 0; i < K; ++i) { // c = c² (truncated to nw words) — 符号は消える (自乗で高速化) rf_sqr(c_rf, c_rf, prod_buf, scratch_buf, nw); c_negative = false; // c *= 2 (指数シフト) c_rf.exp += 1; // c -= 1.0: 仮数 V から 2^(-exp) を引く // 1.0 のビット位置 = -exp ビット目。word_idx = (-exp)/64, bit_idx = (-exp)%64 // word_idx >= nw の場合: 1.0 のビットが仮数範囲外 (MSB の上) // → 2c² < 1 なので結果は 1 - 2c² (負) if (c_rf.nw > 0 && c_rf.exp < 0) { int64_t neg_exp = -c_rf.exp; size_t word_idx = static_cast(neg_exp / 64); unsigned bit_idx = static_cast(neg_exp % 64); if (word_idx < c_rf.nw) { uint64_t borrow = mpn::sub_1( c_rf.d + word_idx, c_rf.nw - word_idx, 1ULL << bit_idx); if (borrow) { // 2c² < 1: 2の補数反転で |1 - 2c²| を得る for (size_t j = 0; j < c_rf.nw; ++j) c_rf.d[j] = ~c_rf.d[j]; mpn::add_1(c_rf.d, c_rf.nw, 1); c_negative = true; } } else { // word_idx >= nw: 1.0 > 2c² → c = 1 - 2c² (符号反転) // 1.0 の仮数を構築して減算: one - V // one = 2^(-exp) = 2^(word_idx*64 + bit_idx) // V = mantissa (nw words) // |1 - 2c²| = 1 - V*2^exp // nw+1 ワード以上必要になるが、word_idx==nw かつ bit_idx==0 が典型 // この場合 1.0 = 2^(nw*64) → V をそのまま反転 if (word_idx == static_cast(nw) && bit_idx == 0) { // |1 - V*2^exp| = (2^(nw*64) - V) * 2^exp // = two's complement of V for (size_t j = 0; j < c_rf.nw; ++j) c_rf.d[j] = ~c_rf.d[j]; mpn::add_1(c_rf.d, c_rf.nw, 1); c_negative = true; // 2c² < 1 → 2c²-1 < 0 } else { // 一般ケース: Float にフォールバック Float c_tmp = rf_to_float(c_rf, false, wp_inner); c_tmp = c_tmp - Float::one(wp_inner); if (c_tmp.isNegative()) { c_tmp = -c_tmp; c_negative = true; } c_rf = rf_extract(c_tmp, nw, arena); std::memcpy(c_d, c_rf.d, c_rf.nw * sizeof(uint64_t)); c_rf.d = c_d; } } } c_rf.nw = mpn::normalized_size(c_rf.d, c_rf.nw); rf_normalize(c_rf); } Float out = rf_to_float(c_rf, c_negative, wp_inner); out.truncateToApprox(working_precision); return out; } // 倍角公式による (sin, cos) 同時計算 // sin(2θ) = 2sinθcosθ, cos(2θ) = 2cos²θ - 1 // x は [0, π/2] の範囲を想定 static std::pair sinCosDoubling(const Float& x, int working_precision) { int wp_bits = Float::precisionToBits(working_precision); int K; if (wp_bits >= 10000) { // P-S で Taylor が O(√N) → 復元 2K + Taylor 6√(p/(2K)) // 最適 K = cbrt(2·wp_bits) (復元が 2M(n)/step のため) K = static_cast(std::cbrt(2.0 * wp_bits)); } else { K = static_cast(std::sqrt(wp_bits / 2.0)); } if (K < 1) return { sinTaylor(x, working_precision), cosTaylor(x, working_precision) }; int guard_bits = 2 * K + static_cast(std::ceil(std::log2(K + 1))) + 10; int wp_inner = working_precision + Float::bitsToPrecision(guard_bits); Float x_red = ldexp(x, -K); x_red.truncateToApprox(wp_inner); // 入力が double (eff=53) でも Taylor を wp_inner 精度で計算させる。 // INT_MAX ではなく wp_inner ビットを設定 (除算の defaultPrecision 問題回避)。 int wp_inner_bits = Float::precisionToBits(wp_inner); x_red.setEffectiveBits(wp_inner_bits); Float s_float = sinTaylor(x_red, wp_inner); Float c_float = cosTaylor(x_red, wp_inner); // K 回同時復元 (RawFloat — Float per-op オーバーヘッドを回避) // sin(2θ) = 2sinθcosθ, cos(2θ) = 2cos²θ - 1 int nw = (wp_inner_bits + 63) / 64; ScratchScope scope; auto& arena = getThreadArena(); uint64_t* s_d = arena.alloc_limbs(nw + 2); RawFloat s_rf = rf_extract(s_float, nw, arena); std::memcpy(s_d, s_rf.d, s_rf.nw * sizeof(uint64_t)); s_rf.d = s_d; uint64_t* c_d = arena.alloc_limbs(nw + 2); RawFloat c_rf = rf_extract(c_float, nw, arena); std::memcpy(c_d, c_rf.d, c_rf.nw * sizeof(uint64_t)); c_rf.d = c_d; size_t prod_alloc = 2 * static_cast(nw) + 4; uint64_t* prod_buf = arena.alloc_limbs(prod_alloc); size_t sqr_scratch = mpn::square_scratch_size(nw + 1); size_t scratch_sz = std::max(mpn::multiply_scratch_size(nw + 1, nw + 1), sqr_scratch); uint64_t* scratch_buf = arena.alloc_limbs(scratch_sz > 0 ? scratch_sz : 1); // new_s 用の一時バッファ uint64_t* tmp_d = arena.alloc_limbs(nw + 2); bool c_negative = false; bool s_negative = false; for (int i = 0; i < K; ++i) { // new_s = 2 * s * c (一時バッファに格納) // 符号: s_neg XOR c_neg (乗算の符号規則) RawFloat tmp_rf{tmp_d, 0, 0}; rf_mul(tmp_rf, s_rf, c_rf, prod_buf, scratch_buf, nw); tmp_rf.exp += 1; // ×2 bool new_s_neg = (s_negative != c_negative); // c = 2 * c² - 1 (c² で符号消える, 自乗で高速化) rf_sqr(c_rf, c_rf, prod_buf, scratch_buf, nw); c_negative = false; c_rf.exp += 1; // ×2 // c -= 1.0: 仮数から 2^(-exp) を引く if (c_rf.nw > 0 && c_rf.exp < 0) { int64_t neg_exp = -c_rf.exp; size_t word_idx = static_cast(neg_exp / 64); unsigned bit_idx = static_cast(neg_exp % 64); if (word_idx < c_rf.nw) { uint64_t borrow = mpn::sub_1( c_rf.d + word_idx, c_rf.nw - word_idx, 1ULL << bit_idx); if (borrow) { for (size_t j = 0; j < c_rf.nw; ++j) c_rf.d[j] = ~c_rf.d[j]; mpn::add_1(c_rf.d, c_rf.nw, 1); c_negative = true; } } else { // word_idx >= nw: 1.0 > 2c² → c = 1 - 2c² (符号反転) if (word_idx == static_cast(nw) && bit_idx == 0) { for (size_t j = 0; j < c_rf.nw; ++j) c_rf.d[j] = ~c_rf.d[j]; mpn::add_1(c_rf.d, c_rf.nw, 1); c_negative = true; } else { Float c_tmp = rf_to_float(c_rf, false, wp_inner); c_tmp = c_tmp - Float::one(wp_inner); if (c_tmp.isNegative()) { c_tmp = -c_tmp; c_negative = true; } c_rf = rf_extract(c_tmp, nw, arena); std::memcpy(c_d, c_rf.d, c_rf.nw * sizeof(uint64_t)); c_rf.d = c_d; } } } c_rf.nw = mpn::normalized_size(c_rf.d, c_rf.nw); rf_normalize(c_rf); // s = new_s std::memcpy(s_rf.d, tmp_rf.d, tmp_rf.nw * sizeof(uint64_t)); s_rf.nw = tmp_rf.nw; s_rf.exp = tmp_rf.exp; s_negative = new_s_neg; } Float s_out = rf_to_float(s_rf, s_negative, wp_inner); Float c_out = rf_to_float(c_rf, c_negative, wp_inner); s_out.truncateToApprox(working_precision); c_out.truncateToApprox(working_precision); return { std::move(s_out), std::move(c_out) }; } //============================================================================= // Bit-burst sin/cos 同時計算 (Smith 2001) //============================================================================= // x を B ビットチャンクに分割し、加法定理 sin(a+b) = sin(a)cos(b)+cos(a)sin(b) で合成。 // 各チャンクは極小 → Taylor が少項で収束。 // 計算量: O(M(n) · log²n) (固定小数点 BS 併用時、現在は PS Taylor) // MPFR は 30000bit 以上でこの手法に切替。 // x ∈ [0, π/2] (reduceToFirstQuadrant 済み), working_precision は10進桁数 static std::pair sincos_bitburst(const Float& x, int working_precision) { int wp_bits = Float::precisionToBits(working_precision); // ハルビング回数 K: PS Taylor の最適値 int K; if (wp_bits >= 10000) { K = static_cast(std::cbrt(static_cast(wp_bits))); } else { K = static_cast(std::sqrt(wp_bits / 2.0)); } if (K < 1) K = 1; // チャンク数 L: 合成コスト (4L·M(p)) を抑えつつ後続チャンクの Taylor を軽くする // L が大きいと合成コスト増、L が小さいとチャンク 0 が重い (=通常パスと同等) // 経験的に L=8-16 で合成と Taylor のバランスが取れる int L = 12; if (wp_bits < 40000) L = 8; if (wp_bits > 200000) L = 16; // ガードビット: 倍角復元 4^K 倍増幅 + L 回合成の丸め誤差 int guard_bits = 2 * K + static_cast(std::ceil(std::log2(K + L + 1))) + 30; int wp_inner = working_precision + Float::bitsToPrecision(guard_bits); int wp_inner_bits = Float::precisionToBits(wp_inner); // 引数半減: x_red = x / 2^K Float x_red = ldexp(x, -K); x_red.truncateToApprox(wp_inner); x_red.setEffectiveBits(wp_inner_bits); if (x_red.isZero()) { return { Float::zero(), Float::one(working_precision) }; } // 仮数部を L 個のチャンクに分割 const Int& mant = x_red.mantissa(); int64_t base_exp = x_red.exponent(); int mant_bl = static_cast(mant.bitLength()); int B = (mant_bl + L - 1) / L; // チャンクサイズ (ビット) if (B < 1) B = 1; int actual_L = (mant_bl + B - 1) / B; // マスク: (1 << B) - 1 Int mask = (Int(1) << B) - 1; // (sin, cos) 累積変数を Float で初期化 Float S = Float::zero(); Float C = Float::one(wp_inner); bool first_chunk = true; for (int j = 0; j < actual_L; ++j) { // チャンク j: ビット [mant_bl - (j+1)*B, mant_bl - j*B) を抽出 int bit_start = mant_bl - (j + 1) * B; Int chunk_val; if (bit_start >= 0) { chunk_val = (mant >> bit_start) & mask; } else { // 最後の端数チャンク int remaining_bits = mant_bl - j * B; Int last_mask = (Int(1) << remaining_bits) - 1; chunk_val = mant & last_mask; bit_start = 0; } if (chunk_val.isZero()) continue; // δ_j = chunk_val · 2^{base_exp + bit_start} int64_t chunk_exp = base_exp + bit_start; Float delta(std::move(chunk_val)); delta = ldexp(std::move(delta), static_cast(chunk_exp)); delta.truncateToApprox(wp_inner); delta.setEffectiveBits(wp_inner_bits); // δ_j の大きさを推定: MSB 位置 int64_t delta_msb = delta.exponent() + static_cast(delta.mantissa().bitLength()); Float sd, cd; if (2 * (-delta_msb) > wp_inner_bits) { // |δ_j²| < 2^{-wp_inner_bits} → sin(δ) = δ, cos(δ) = 1 (精度内) sd = std::move(delta); cd = Float::one(wp_inner); } else { // sinTaylor/cosTaylor: PS を内部で自動使用 (≥10000bit) sd = sinTaylor(delta, wp_inner); cd = cosTaylor(delta, wp_inner); } if (first_chunk) { S = std::move(sd); C = std::move(cd); first_chunk = false; } else { // 加法定理: sin(a+δ) = sin(a)cos(δ) + cos(a)sin(δ) // cos(a+δ) = cos(a)cos(δ) - sin(a)sin(δ) // FloatOps 3引数版で乗算の一時 Float 構築を削減 Float t1, t2; FloatOps::mul(S, cd, t1); // t1 = S*cos(δ) FloatOps::mul(C, sd, t2); // t2 = C*sin(δ) Float new_S = t1 + t2; FloatOps::mul(C, cd, t1); // t1 = C*cos(δ) FloatOps::mul(S, sd, t2); // t2 = S*sin(δ) S = std::move(new_S); C = t1 - t2; } S.truncateToApprox(wp_inner); C.truncateToApprox(wp_inner); } if (first_chunk) { // 全チャンクが 0 → x_red = 0 return { Float::zero(), Float::one(working_precision) }; } // K 回倍角復元 (RawFloat で高速化) // sin(2θ) = 2sinθcosθ, cos(2θ) = 2cos²θ - 1 int nw = (wp_inner_bits + 63) / 64; ScratchScope scope; auto& arena = getThreadArena(); uint64_t* s_d = arena.alloc_limbs(nw + 2); RawFloat s_rf = rf_extract(S, nw, arena); std::memcpy(s_d, s_rf.d, s_rf.nw * sizeof(uint64_t)); s_rf.d = s_d; uint64_t* c_d = arena.alloc_limbs(nw + 2); RawFloat c_rf = rf_extract(C, nw, arena); std::memcpy(c_d, c_rf.d, c_rf.nw * sizeof(uint64_t)); c_rf.d = c_d; size_t prod_alloc = 2 * static_cast(nw) + 4; uint64_t* prod_buf = arena.alloc_limbs(prod_alloc); size_t sqr_scratch = mpn::square_scratch_size(nw + 1); size_t scratch_sz = std::max(mpn::multiply_scratch_size(nw + 1, nw + 1), sqr_scratch); uint64_t* scratch_buf = arena.alloc_limbs(scratch_sz > 0 ? scratch_sz : 1); uint64_t* tmp_d = arena.alloc_limbs(nw + 2); bool c_negative = false; bool s_negative = false; for (int i = 0; i < K; ++i) { // new_s = 2 * s * c RawFloat tmp_rf{tmp_d, 0, 0}; rf_mul(tmp_rf, s_rf, c_rf, prod_buf, scratch_buf, nw); tmp_rf.exp += 1; // ×2 bool new_s_neg = (s_negative != c_negative); // c = 2 * c² - 1 rf_sqr(c_rf, c_rf, prod_buf, scratch_buf, nw); c_negative = false; c_rf.exp += 1; // ×2 // c -= 1.0 if (c_rf.nw > 0 && c_rf.exp < 0) { int64_t neg_exp = -c_rf.exp; size_t word_idx = static_cast(neg_exp / 64); unsigned bit_idx = static_cast(neg_exp % 64); if (word_idx < c_rf.nw) { uint64_t borrow = mpn::sub_1( c_rf.d + word_idx, c_rf.nw - word_idx, 1ULL << bit_idx); if (borrow) { for (size_t jj = 0; jj < c_rf.nw; ++jj) c_rf.d[jj] = ~c_rf.d[jj]; mpn::add_1(c_rf.d, c_rf.nw, 1); c_negative = true; } } else { if (word_idx == static_cast(nw) && bit_idx == 0) { for (size_t jj = 0; jj < c_rf.nw; ++jj) c_rf.d[jj] = ~c_rf.d[jj]; mpn::add_1(c_rf.d, c_rf.nw, 1); c_negative = true; } else { Float c_tmp = rf_to_float(c_rf, false, wp_inner); c_tmp = c_tmp - Float::one(wp_inner); if (c_tmp.isNegative()) { c_tmp = -c_tmp; c_negative = true; } c_rf = rf_extract(c_tmp, nw, arena); std::memcpy(c_d, c_rf.d, c_rf.nw * sizeof(uint64_t)); c_rf.d = c_d; } } } c_rf.nw = mpn::normalized_size(c_rf.d, c_rf.nw); rf_normalize(c_rf); // s = new_s std::memcpy(s_rf.d, tmp_rf.d, tmp_rf.nw * sizeof(uint64_t)); s_rf.nw = tmp_rf.nw; s_rf.exp = tmp_rf.exp; s_negative = new_s_neg; } Float s_out = rf_to_float(s_rf, s_negative, wp_inner); Float c_out = rf_to_float(c_rf, c_negative, wp_inner); s_out.truncateToApprox(working_precision); c_out.truncateToApprox(working_precision); return { std::move(s_out), std::move(c_out) }; } //============================================================================= // 三角関数 共通範囲縮小 //============================================================================= // x > 0 を [0, π/2] に縮小し、sin/cos の符号フラグを返す struct TrigReduced { Float x; // [0, π/2] に縮小された値 bool sin_negative; // sin の符号反転フラグ bool cos_negative; // cos の符号反転フラグ }; static TrigReduced reduceToFirstQuadrant(Float x, int working_precision) { x.truncateToApprox(working_precision); Float pi = Float::pi(working_precision); Float pi2 = ldexp(pi, 1); // x mod 2π if (x > pi2) { Int k = (x / pi2).toInt(); x = x - Float(k) * pi2; } // [0, π/2] に縮小 — 四象限の符号テーブル: // Q1 [0, π/2] : sin+, cos+ // Q2 (π/2, π] : sin+, cos- // Q3 (π, 3π/2] : sin-, cos- // Q4 (3π/2, 2π) : sin-, cos+ bool sin_neg = false; bool cos_neg = false; Float pi_half = ldexp(pi, -1); if (x > pi_half && x <= pi) { x = pi - x; cos_neg = true; } else if (x > pi && x <= pi + pi_half) { x = x - pi; sin_neg = true; cos_neg = true; } else if (x > pi + pi_half) { x = pi2 - x; sin_neg = true; } x.truncateToApprox(working_precision); return { std::move(x), sin_neg, cos_neg }; } //============================================================================= // 正弦関数 (sin) //============================================================================= // 本体: x を値で受け取る (move 済み前提, x > 0) // TRIG-2: cosDoubling + sqrt(1 - cos²) で Taylor 1本分を節約 // Bit-burst 切替閾値 // 現在の実装は既存 PS Taylor + Float 合成で、cosDoubling+sqrt より低速。 // 将来の固定小数点 BS 最適化で各チャンクの Taylor を高速化すれば // O(M(n)·log²n) が達成可能。閾値を高く設定して実用パスに影響させない。 static constexpr int BITBURST_THRESHOLD = 200000; static Float sin_core(Float x, int eff_x, int precision) { int compute_prec = effectiveComputePrecision(eff_x, precision); int working_precision = compute_prec + 10; auto reduced = reduceToFirstQuadrant(std::move(x), working_precision); Float result; if (reduced.x.isZero()) { result = Float::zero(); } else { int64_t x_msb = reduced.x.exponent() + static_cast(reduced.x.mantissa().bitLength()); int wp_bits = Float::precisionToBits(working_precision); if (x_msb < -(wp_bits / 4) || wp_bits < 1000) { result = sinTaylor(reduced.x, working_precision); } else if (wp_bits >= BITBURST_THRESHOLD) { // Bit-burst: sin/cos 同時計算、sqrt 不要 auto [s, c] = sincos_bitburst(reduced.x, working_precision); result = std::move(s); } else { // 高精度パス: cos + sqrt(1 - cos²) int extra_guard = (x_msb < 0) ? static_cast(-2 * x_msb) : 0; int wp_cos = working_precision + Float::bitsToPrecision(extra_guard); Float c = cosDoubling(reduced.x, wp_cos); Float one_minus_c2 = Float::one(wp_cos) - c * c; result = sqrt(std::move(one_minus_c2), working_precision); } } if (reduced.sin_negative) result = -result; finalizeResult(result, eff_x, precision); return result; } // sin/cos Q128 ファストパス (19桁, precision_bits ≤ 64) // 引数縮約 z = x - k·(π/2) ∈ [-π/4, π/4] → Q128 Taylor sin/cos // x > 0 を前提とする (sin の呼び出し側で保証、cos は fabs で対応) static Float trig_small_impl(const Float& x, bool want_sin, int precision) { const Int& mant = x.mantissa(); const uint64_t* mw = mant.data(); uint64_t m_val = mw[0]; int64_t e = x.exponent(); int m_bits = 64 - std::countl_zero(m_val); int64_t total_bl = static_cast(m_bits) + e; // x が極小 (< 2^{-64}) → sin(x) ≈ x, cos(x) ≈ 1 if (total_bl < -64) { if (want_sin) { Float r(x); r.setResultPrecision(precision); return r; } return Float::one(precision); } // k = round(x / (π/2)) double x_d = x.toDouble(); if (!want_sin) x_d = std::fabs(x_d); // cos(-x) = cos(x) constexpr double two_over_pi = 0.6366197723675814; int k = static_cast(std::round(x_d * two_over_pi)); // 象限マッピング // sin(k·π/2 + z): k%4 = 0→sin(z), 1→cos(z), 2→-sin(z), 3→-cos(z) // cos(k·π/2 + z): k%4 = 0→cos(z), 1→-sin(z), 2→-cos(z), 3→sin(z) int quad = ((k % 4) + 4) % 4; bool use_cos_taylor, negate; if (want_sin) { use_cos_taylor = (quad == 1 || quad == 3); negate = (quad == 2 || quad == 3); } else { use_cos_taylor = (quad == 0 || quad == 2); negate = (quad == 1 || quad == 2); } // (π/2 - 1) の Q128 定数 // π/2 = 1.921FB54442D18469_898CC51701B839A2...₁₆ constexpr uint64_t phf_hi = 0x921FB54442D18469ULL; constexpr uint64_t phf_lo = 0x898CC51701B839A2ULL; uint64_t abs_k = static_cast(k < 0 ? -k : k); // k·(π/2-1) → 整数部 + Q128 小数部 uint64_t kphf_frac_hi, kphf_frac_lo; uint64_t kphf_int; { uint64_t p0_hi; uint64_t p0_lo = _umul128(phf_lo, abs_k, &p0_hi); uint64_t p1_hi; uint64_t p1_lo = _umul128(phf_hi, abs_k, &p1_hi); unsigned char c = _addcarry_u64(0, p1_lo, p0_hi, &kphf_frac_hi); kphf_frac_lo = p0_lo; kphf_int = p1_hi + static_cast(c); } // x の整数部と Q128 小数部を抽出 (仮数部は常に |x| を表す) uint64_t int_x = 0; uint64_t x_frac_hi = 0, x_frac_lo = 0; if (total_bl <= 0) { // |x| < 1: 整数部なし int pos = static_cast(128 + e); if (pos >= 64) { x_frac_hi = m_val << (pos - 64); } else if (pos > 0) { x_frac_hi = m_val >> (64 - pos); x_frac_lo = m_val << pos; } else if (pos == 0) { x_frac_lo = m_val; } } else { if (e < 0) { int_x = m_val >> (-e); uint64_t frac_mask = (1ULL << (-e)) - 1; uint64_t frac_val = m_val & frac_mask; if (frac_val != 0) { int pos = static_cast(128 + e); if (pos >= 64) { x_frac_hi = frac_val << (pos - 64); } else if (pos > 0) { x_frac_hi = frac_val >> (64 - pos); x_frac_lo = frac_val << pos; } } } else { // e >= 0: x は整数、小数部なし int_x = m_val << e; } } // frac(x) - frac(k·(π/2-1)) を unsigned Q128 で計算 uint64_t fd_hi, fd_lo; unsigned char borrow1 = _subborrow_u64(0, x_frac_lo, kphf_frac_lo, &fd_lo); unsigned char borrow2 = _subborrow_u64(borrow1, x_frac_hi, kphf_frac_hi, &fd_hi); // 整数補正: z = (int_x - k - kphf_int - borrow) + fd/2^128 // k·(π/2) = k + k·(π/2-1) なので int(k·(π/2)) = k + kphf_int int64_t effective_corr = static_cast(int_x) - static_cast(k) - static_cast(kphf_int) - static_cast(borrow2); // z = effective_corr + fd/2^128, |z| < π/4 < 1 // effective_corr は 0 (z ≥ 0) または -1 (z < 0) のみ uint64_t z_hi, z_lo; bool z_neg; if (effective_corr == 0) { z_hi = fd_hi; z_lo = fd_lo; z_neg = false; } else if (effective_corr == -1) { // z < 0: |z| = (2^128 - fd) / 2^128 unsigned char nb = _subborrow_u64(0, 0, fd_lo, &z_lo); _subborrow_u64(nb, 0, fd_hi, &z_hi); z_neg = true; } else { // 想定外 → 汎用パスにフォールバック Float x_copy(x); if (!want_sin) x_copy = abs(x_copy); if (want_sin) return sin_core(std::move(x_copy), x.effectiveBits(), precision); else return cos_core(std::move(x_copy), x.effectiveBits(), precision); } // sin(z): sin(-z) = -sin(z) → 符号反転 // cos(z): cos(-z) = cos(z) → 変更なし if (!use_cos_taylor && z_neg) negate = !negate; if (z_hi == 0 && z_lo == 0) { if (use_cos_taylor) { Float result = Float::one(precision); return negate ? -result : result; } return Float::zero(precision); } // z² を Q128 で計算 uint64_t z2_hi, z2_lo; q128_mul(z_hi, z_lo, z_hi, z_lo, z2_hi, z2_lo); Float result; if (use_cos_taylor) { // 1 - cos(|z|) = z²/2! - z⁴/4! + z⁶/6! - ... // term₀ = z²/2, 漸化式: term_{n+1} = term_n · z² / ((2n+1)(2n+2)) uint64_t rem; uint64_t term_hi = _udiv128(0, z2_hi, 2, &rem); uint64_t term_lo = _udiv128(rem, z2_lo, 2, &rem); uint64_t sum_hi = term_hi, sum_lo = term_lo; for (int n = 1; n <= 20; n++) { q128_mul(term_hi, term_lo, z2_hi, z2_lo, term_hi, term_lo); uint64_t divisor = static_cast(2*n + 1) * static_cast(2*n + 2); term_hi = _udiv128(0, term_hi, divisor, &rem); term_lo = _udiv128(rem, term_lo, divisor, &rem); if (term_hi == 0 && term_lo == 0) break; if (n & 1) { unsigned char b = _subborrow_u64(0, sum_lo, term_lo, &sum_lo); _subborrow_u64(b, sum_hi, term_hi, &sum_hi); } else { unsigned char c = _addcarry_u64(0, sum_lo, term_lo, &sum_lo); _addcarry_u64(c, sum_hi, term_hi, &sum_hi); } } // cos(z) = 1 - sum → mantissa = 2^128 - sum, exponent = -128 // |z| < π/4 → cos(z) > cos(π/4) ≈ 0.707 → mantissa > 2^127 → 常に 2 ワード uint64_t cos_lo, cos_hi; unsigned char b = _subborrow_u64(0, 0, sum_lo, &cos_lo); _subborrow_u64(b, 0, sum_hi, &cos_hi); uint64_t words[2] = { cos_lo, cos_hi }; auto mant2 = Int::fromRawWordsPreNormalized( std::span(words, 2), 1); result = Float(std::move(mant2), -128, negate); } else { // sin(|z|) = z - z³/3! + z⁵/5! - ... // term₀ = z, 漸化式: term_{n+1} = term_n · z² / ((2n)(2n+1)) uint64_t sum_hi = z_hi, sum_lo = z_lo; uint64_t term_hi = z_hi, term_lo = z_lo; for (int n = 1; n <= 20; n++) { q128_mul(term_hi, term_lo, z2_hi, z2_lo, term_hi, term_lo); uint64_t divisor = static_cast(2*n) * static_cast(2*n + 1); uint64_t rem; term_hi = _udiv128(0, term_hi, divisor, &rem); term_lo = _udiv128(rem, term_lo, divisor, &rem); if (term_hi == 0 && term_lo == 0) break; if (n & 1) { unsigned char b = _subborrow_u64(0, sum_lo, term_lo, &sum_lo); _subborrow_u64(b, sum_hi, term_hi, &sum_hi); } else { unsigned char c = _addcarry_u64(0, sum_lo, term_lo, &sum_lo); _addcarry_u64(c, sum_hi, term_hi, &sum_hi); } } size_t wn = (sum_hi != 0) ? 2 : ((sum_lo != 0) ? 1 : 0); if (wn == 0) return Float::zero(precision); uint64_t words[2] = { sum_lo, sum_hi }; auto mant2 = Int::fromRawWordsPreNormalized( std::span(words, wn), 1); result = Float(std::move(mant2), -128, negate); } result.setResultPrecision(precision); return result; } // sin/cos 38桁ファストパス: 軽量 Float 引数縮約 + sinTaylor/cosTaylor // x > 0 を前提 (呼び出し側で保証) static Float trig_medium_impl(const Float& x, bool want_sin, int precision) { double x_d = x.toDouble(); // k = round(x / (π/2)) constexpr double two_over_pi = 0.6366197723675814; int k = static_cast(std::round(x_d * two_over_pi)); // z = |x| - k·(π/2) int wp = precision + 6; Float z; if (k == 0) { z = x; } else { Float pi_half = Float::pi(wp); pi_half >>= 1; z = x - Float(k) * pi_half; } // k%4 で象限決定 int quad = ((k % 4) + 4) % 4; bool negate = false; bool use_cos = false; if (want_sin) { // sin(z + k·π/2): 0→sin, 1→cos, 2→-sin, 3→-cos switch (quad) { case 0: break; case 1: use_cos = true; break; case 2: negate = true; break; case 3: use_cos = true; negate = true; break; } } else { // cos(z + k·π/2): 0→cos, 1→-sin, 2→-cos, 3→sin switch (quad) { case 0: use_cos = true; break; case 1: negate = true; break; case 2: use_cos = true; negate = true; break; case 3: break; } } Float result; if (use_cos) { result = cosTaylor(z, wp); } else { result = sinTaylor(z, wp); } if (negate) result = -result; result.setResultPrecision(precision); return result; } Float sin(const Float& x, int precision) { // 特殊値の処理 if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (x.isZero()) return Float::zero(); // 範囲縮小: sin(-x) = -sin(x) if (x.isNegative()) return -sin(-x, precision); int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) { int64_t total_bl = static_cast(x.mantissa().bitLength()) + x.exponent(); if (total_bl <= 52) return trig_small_impl(x, true, precision); } if (x.mantissa().size() <= 2 && precision_bits <= 128) return trig_medium_impl(x, true, precision); return zivRound([](const Float& a, int e, int p) { return sin_core(Float(a), e, p); }, x, x.effectiveBits(), precision); } Float sin(Float&& x, int precision) { // 特殊値の処理 if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (x.isZero()) return Float::zero(); // 範囲縮小: sin(-x) = -sin(x) if (x.isNegative()) return -sin(-x, precision); int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) { int64_t total_bl = static_cast(x.mantissa().bitLength()) + x.exponent(); if (total_bl <= 52) return trig_small_impl(x, true, precision); } if (x.mantissa().size() <= 2 && precision_bits <= 128) return trig_medium_impl(x, true, precision); return zivRound([](const Float& a, int e, int p) { return sin_core(Float(a), e, p); }, x, x.effectiveBits(), precision); } //============================================================================= // 余弦関数 (cos) //============================================================================= // 本体: x を値で受け取る (move 済み前提) // cos(-x) = cos(x) なので呼び出し側で abs 不要 — reduceToFirstQuadrant が処理 static Float cos_core(Float x, int eff_x, int precision) { int compute_prec = effectiveComputePrecision(eff_x, precision); int working_precision = compute_prec + 10; // cos(-x) = cos(x) x = abs(x); auto reduced = reduceToFirstQuadrant(std::move(x), working_precision); int wp_bits = Float::precisionToBits(working_precision); Float result; if (reduced.x.isZero()) { result = Float::one(working_precision); } else if (wp_bits >= BITBURST_THRESHOLD) { // Bit-burst: sin/cos 同時計算 auto [s, c] = sincos_bitburst(reduced.x, working_precision); result = std::move(c); } else { int64_t x_msb = reduced.x.exponent() + static_cast(reduced.x.mantissa().bitLength()); if (wp_bits < 1000 && x_msb <= -1) { result = cosTaylor(reduced.x, working_precision); } else { result = cosDoubling(reduced.x, working_precision); } } if (reduced.cos_negative) result = -result; finalizeResult(result, eff_x, precision); return result; } Float cos(const Float& x, int precision) { // 特殊値の処理 if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (x.isZero()) return Float::one(precision); int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) { int64_t total_bl = static_cast(x.mantissa().bitLength()) + x.exponent(); if (total_bl <= 52) return trig_small_impl(x, false, precision); } if (x.mantissa().size() <= 2 && precision_bits <= 128) { Float abs_x = abs(x); return trig_medium_impl(abs_x, false, precision); } return zivRound([](const Float& a, int e, int p) { return cos_core(Float(a), e, p); }, x, x.effectiveBits(), precision); } Float cos(Float&& x, int precision) { // 特殊値の処理 if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (x.isZero()) return Float::one(precision); int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) { int64_t total_bl = static_cast(x.mantissa().bitLength()) + x.exponent(); if (total_bl <= 52) return trig_small_impl(x, false, precision); } if (x.mantissa().size() <= 2 && precision_bits <= 128) { Float abs_x = abs(x); return trig_medium_impl(abs_x, false, precision); } return zivRound([](const Float& a, int e, int p) { return cos_core(Float(a), e, p); }, x, x.effectiveBits(), precision); } //============================================================================= // 正接関数 (tan) //============================================================================= // TRIG-2: sinCosDoubling 直接利用で範囲縮小1回 + Taylor 2本に削減 static Float tan_core(Float x, int eff_x, int precision) { int compute_prec = effectiveComputePrecision(eff_x, precision); int working_precision = compute_prec + 10; bool input_negative = x.isNegative(); x = abs(x); auto reduced = reduceToFirstQuadrant(std::move(x), working_precision); int wp_bits = Float::precisionToBits(working_precision); Float s, c; if (reduced.x.isZero()) { s = Float::zero(); c = Float::one(working_precision); } else if (wp_bits >= 1000) { // 高精度: cosDoubling + sqrt(1 - cos²) で Taylor 1 本分を節約 // sinCosDoubling: 2 Taylor + K×4M(n) 復元 // cosDoubling+sqrt: 1 Taylor + K×2M(n) 復元 + sqrt ≈ 半分のコスト int64_t x_msb = reduced.x.exponent() + static_cast(reduced.x.mantissa().bitLength()); int extra_guard = (x_msb < 0) ? static_cast(-2 * x_msb) : 0; int wp_cos = working_precision + Float::bitsToPrecision(extra_guard); c = cosDoubling(reduced.x, wp_cos); Float one_minus_c2 = Float::one(wp_cos) - c * c; s = sqrt(std::move(one_minus_c2), working_precision); c.truncateToApprox(working_precision); } else { // 低精度: sinCosDoubling (オーバーヘッドが支配的) auto [sv, cv] = sinCosDoubling(reduced.x, working_precision); s = std::move(sv); c = std::move(cv); } if (reduced.sin_negative) s = -s; if (reduced.cos_negative) c = -c; // cos ≈ 0 → tan → ±∞ if (c.isZero()) { bool result_neg = input_negative ? !s.isNegative() : s.isNegative(); return result_neg ? Float::negativeInfinity() : Float::positiveInfinity(); } Float result = s / c; if (input_negative) result = -result; // tan(-x) = -tan(x) finalizeResult(result, eff_x, precision); return result; } // tan Q128 ファストパス: sin_small / cos_small → Float 除算 static Float tan_small(const Float& x, int precision) { Float s = trig_small_impl(x, true, precision + 1); Float c = trig_small_impl(x, false, precision + 1); if (c.isZero()) { return s.isNegative() ? Float::negativeInfinity() : Float::positiveInfinity(); } Float result = s / c; result.setResultPrecision(precision); return result; } // tan 38桁ファストパス: 引数縮約を共有して sin/cos を一度に計算 static Float tan_medium(const Float& x, int precision) { double x_d = x.toDouble(); constexpr double two_over_pi = 0.6366197723675814; int k = static_cast(std::round(x_d * two_over_pi)); int wp = precision + 4; Float z; if (k == 0) { z = x; } else { Float pi_half = Float::pi(wp); pi_half >>= 1; z = x - Float(k) * pi_half; } // sinTaylor と cosTaylor を両方計算 Float sv = sinTaylor(z, wp); Float cv = cosTaylor(z, wp); // k%4 で符号調整 int quad = ((k % 4) + 4) % 4; // tan(z + k·π/2): // k%4=0: sin/cos, k%4=1: cos/(-sin), k%4=2: (-sin)/(-cos)=sin/cos, k%4=3: (-cos)/sin Float num, den; switch (quad) { case 0: num = std::move(sv); den = std::move(cv); break; case 1: num = std::move(cv); den = -std::move(sv); break; case 2: num = std::move(sv); den = std::move(cv); break; case 3: num = -std::move(cv); den = std::move(sv); break; } if (den.isZero()) { return num.isNegative() ? Float::negativeInfinity() : Float::positiveInfinity(); } Float result = num / den; result.setResultPrecision(precision); return result; } Float tan(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (x.isZero()) return Float::zero(); int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) { const Float& abs_x = x.isNegative() ? static_cast(-x) : x; int64_t total_bl = static_cast(x.mantissa().bitLength()) + x.exponent(); if (total_bl <= 52) { // tan(-x) = -tan(x): trig_small_impl は x > 0 前提 if (x.isNegative()) { Float neg_x = -x; return -tan_small(neg_x, precision); } return tan_small(x, precision); } } if (x.mantissa().size() <= 2 && precision_bits <= 128) { if (x.isNegative()) { Float neg_x = -x; return -tan_medium(neg_x, precision); } return tan_medium(x, precision); } return zivRound([](const Float& a, int e, int p) { return tan_core(Float(a), e, p); }, x, x.effectiveBits(), precision); } Float tan(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (x.isZero()) return Float::zero(); int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) { int64_t total_bl = static_cast(x.mantissa().bitLength()) + x.exponent(); if (total_bl <= 52) { if (x.isNegative()) { Float neg_x = -x; return -tan_small(neg_x, precision); } return tan_small(x, precision); } } if (x.mantissa().size() <= 2 && precision_bits <= 128) { if (x.isNegative()) { Float neg_x = -x; return -tan_medium(neg_x, precision); } return tan_medium(x, precision); } return zivRound([](const Float& a, int e, int p) { return tan_core(Float(a), e, p); }, x, x.effectiveBits(), precision); } //============================================================================= // 双曲線関数 共通: Taylor 級数 + 倍角復元 //============================================================================= // sinh(x) = x + x³/3! + x⁵/5! + ... (全項プラス, sinTaylor の交互符号なし版) static Float sinhTaylor(const Float& x, int working_precision) { int wp_bits = Float::precisionToBits(working_precision); int nw = (wp_bits + 63) / 64; ScratchScope scope; auto& arena = getThreadArena(); size_t prod_alloc = 2 * static_cast(nw) + 4; uint64_t* prod_buf = arena.alloc_limbs(prod_alloc); size_t mul_scratch = mpn::multiply_scratch_size(nw + 1, nw + 1); size_t sqr_scratch = mpn::square_scratch_size(nw + 1); size_t scratch_sz = std::max(mul_scratch, sqr_scratch); uint64_t* scratch = arena.alloc_limbs(scratch_sz > 0 ? scratch_sz : 1); RawFloat x_rf = rf_extract(x, nw, arena); bool x_negative = x.isNegative(); uint64_t* x2_d = arena.alloc_limbs(nw + 2); RawFloat x2{x2_d, 0, 0}; rf_sqr(x2, x_rf, prod_buf, scratch, nw); uint64_t* result_d = arena.alloc_limbs(nw + 2); std::memset(result_d, 0, (nw + 2) * sizeof(uint64_t)); uint64_t* term_d = arena.alloc_limbs(nw + 2); std::memset(term_d, 0, (nw + 2) * sizeof(uint64_t)); std::memcpy(term_d, x_rf.d, x_rf.nw * sizeof(uint64_t)); RawFloat term{term_d, x_rf.nw, x_rf.exp}; std::memcpy(result_d, x_rf.d, x_rf.nw * sizeof(uint64_t)); RawFloat result{result_d, x_rf.nw, x_rf.exp}; int max_terms = static_cast(working_precision * 1.2) + 10; for (int k = 1; k <= max_terms; ++k) { rf_mul(term, term, x2, prod_buf, scratch, nw); uint64_t divisor = static_cast(2*k) * static_cast(2*k + 1); rf_divmod_1(term, divisor); if (term.nw == 0) break; if (!rf_add(result, term, nw + 1)) break; // 全項プラス } return rf_to_float(result, x_negative, working_precision); } // cosh(x) = 1 + x²/2! + x⁴/4! + ... (全項プラス, cosTaylor の交互符号なし版) static Float coshTaylor(const Float& x, int working_precision) { int wp_bits = Float::precisionToBits(working_precision); int nw = (wp_bits + 63) / 64; ScratchScope scope; auto& arena = getThreadArena(); size_t prod_alloc = 2 * static_cast(nw) + 4; uint64_t* prod_buf = arena.alloc_limbs(prod_alloc); size_t mul_scratch = mpn::multiply_scratch_size(nw + 1, nw + 1); size_t sqr_scratch = mpn::square_scratch_size(nw + 1); size_t scratch_sz = std::max(mul_scratch, sqr_scratch); uint64_t* scratch = arena.alloc_limbs(scratch_sz > 0 ? scratch_sz : 1); RawFloat x_rf = rf_extract(x, nw, arena); uint64_t* x2_d = arena.alloc_limbs(nw + 2); RawFloat x2{x2_d, 0, 0}; rf_sqr(x2, x_rf, prod_buf, scratch, nw); uint64_t* result_d = arena.alloc_limbs(nw + 2); std::memset(result_d, 0, (nw + 2) * sizeof(uint64_t)); uint64_t* term_d = arena.alloc_limbs(nw + 2); std::memset(term_d, 0, (nw + 2) * sizeof(uint64_t)); // term = 1.0, result = 1.0 term_d[nw - 1] = uint64_t(1) << 63; RawFloat term{term_d, static_cast(nw), -static_cast(nw * 64 - 1)}; result_d[nw - 1] = uint64_t(1) << 63; RawFloat result{result_d, static_cast(nw), -static_cast(nw * 64 - 1)}; int max_terms = static_cast(working_precision * 1.2) + 10; for (int k = 1; k <= max_terms; ++k) { rf_mul(term, term, x2, prod_buf, scratch, nw); uint64_t divisor = static_cast(2*k - 1) * static_cast(2*k); rf_divmod_1(term, divisor); if (term.nw == 0) break; if (!rf_add(result, term, nw + 1)) break; // 全項プラス } // cosh(x) = cosh(-x) ≥ 1 return rf_to_float(result, false, working_precision); } // 倍角公式による (sinh, cosh) 同時計算 // sinh(2t) = 2sinh(t)cosh(t), cosh(2t) = 2cosh²(t) - 1 // sinCosDoubling と同一構造。cosh ≥ 1 なので 2cosh²-1 ≥ 1 (borrow なし) static std::pair sinhCoshDoubling(const Float& x, int working_precision) { int wp_bits = Float::precisionToBits(working_precision); int K = static_cast(std::sqrt(wp_bits / 3.0)); if (K < 1) return { sinhTaylor(x, working_precision), coshTaylor(x, working_precision) }; int guard_bits = 2 * K + static_cast(std::ceil(std::log2(K + 1))) + 10; int wp_inner = working_precision + Float::bitsToPrecision(guard_bits); Float x_red = ldexp(abs(x), -K); // |x| / 2^K x_red.truncateToApprox(wp_inner); int wp_inner_bits = Float::precisionToBits(wp_inner); x_red.setEffectiveBits(wp_inner_bits); Float s_float = sinhTaylor(x_red, wp_inner); Float c_float = coshTaylor(x_red, wp_inner); // K 回同時復元 (RawFloat) // sinh(2t) = 2sinh(t)cosh(t), cosh(2t) = 2cosh²(t) - 1 int nw = (wp_inner_bits + 63) / 64; ScratchScope scope; auto& arena = getThreadArena(); uint64_t* s_d = arena.alloc_limbs(nw + 2); RawFloat s_rf = rf_extract(s_float, nw, arena); std::memcpy(s_d, s_rf.d, s_rf.nw * sizeof(uint64_t)); s_rf.d = s_d; uint64_t* c_d = arena.alloc_limbs(nw + 2); RawFloat c_rf = rf_extract(c_float, nw, arena); std::memcpy(c_d, c_rf.d, c_rf.nw * sizeof(uint64_t)); c_rf.d = c_d; size_t prod_alloc = 2 * static_cast(nw) + 4; uint64_t* prod_buf = arena.alloc_limbs(prod_alloc); size_t sqr_scratch = mpn::square_scratch_size(nw + 1); size_t scratch_sz = std::max(mpn::multiply_scratch_size(nw + 1, nw + 1), sqr_scratch); uint64_t* scratch_buf = arena.alloc_limbs(scratch_sz > 0 ? scratch_sz : 1); uint64_t* tmp_d = arena.alloc_limbs(nw + 2); for (int i = 0; i < K; ++i) { // new_s = 2 * s * c (一時バッファに格納) RawFloat tmp_rf{tmp_d, 0, 0}; rf_mul(tmp_rf, s_rf, c_rf, prod_buf, scratch_buf, nw); tmp_rf.exp += 1; // ×2 // c = 2 * c² - 1 (cosh ≥ 1 なので 2cosh² ≥ 2 > 1, borrow なし, 自乗で高速化) rf_sqr(c_rf, c_rf, prod_buf, scratch_buf, nw); c_rf.exp += 1; // ×2 // c -= 1.0: 仮数から 2^(-exp) を引く if (c_rf.nw > 0 && c_rf.exp < 0) { int64_t neg_exp = -c_rf.exp; size_t word_idx = static_cast(neg_exp / 64); unsigned bit_idx = static_cast(neg_exp % 64); if (word_idx < c_rf.nw) { mpn::sub_1( c_rf.d + word_idx, c_rf.nw - word_idx, 1ULL << bit_idx); // cosh ≥ 1 → 2cosh²-1 ≥ 1 → borrow は発生しない } // word_idx >= nw は cosh ≥ 1 なので発生しない } c_rf.nw = mpn::normalized_size(c_rf.d, c_rf.nw); rf_normalize(c_rf); // s = new_s std::memcpy(s_rf.d, tmp_rf.d, tmp_rf.nw * sizeof(uint64_t)); s_rf.nw = tmp_rf.nw; s_rf.exp = tmp_rf.exp; } // sinh は x の符号を継承、cosh は常に正 Float s_out = rf_to_float(s_rf, x.isNegative(), wp_inner); Float c_out = rf_to_float(c_rf, false, wp_inner); s_out.truncateToApprox(working_precision); c_out.truncateToApprox(working_precision); return { std::move(s_out), std::move(c_out) }; } //============================================================================= // 双曲線正弦 (sinh) //============================================================================= // sinh/cosh/tanh 共通: exp_small を使う 19桁ファストパス static Float sinh_small(const Float& x, int precision) { int wp = precision + 2; Float ex = exp_small(x, wp); Float emx = Float::one(wp) / ex; Float result = (ex - emx) >>= 1; // / 2 result.setResultPrecision(precision); return result; } static Float cosh_small(const Float& x, int precision) { int wp = precision + 2; Float abs_x = abs(x); Float ex = exp_small(abs_x, wp); Float emx = Float::one(wp) / ex; Float result = (ex + emx) >>= 1; // / 2 result.setResultPrecision(precision); return result; } static Float tanh_small(const Float& x, int precision) { int wp = precision + 2; Float ex = exp_small(x, wp); Float emx = Float::one(wp) / ex; Float result = (ex - emx) / (ex + emx); result.setResultPrecision(precision); return result; } // Ziv 用ディスパッチ: 指定 precision で計算し finalizeResult せず返す static Float sinh_dispatch(const Float& x, int eff_x, int precision) { // sinhCoshDoubling: Taylor + K 回倍角復元 (除算不要, RawFloat) // exp+1/exp 方式は Float 除算 (Newton 逆数) が支配的で遅い int wp = effectiveComputePrecision(eff_x, precision); auto [s, c] = sinhCoshDoubling(x, wp); return s; } Float sinh(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { return x.isNegative() ? Float::negativeInfinity() : Float::positiveInfinity(); } if (x.isZero()) return Float::zero(); int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) return sinh_small(x, precision); return zivRound(sinh_dispatch, x, x.effectiveBits(), precision); } Float sinh(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { return x.isNegative() ? Float::negativeInfinity() : Float::positiveInfinity(); } if (x.isZero()) return Float::zero(); int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) return sinh_small(x, precision); return zivRound(sinh_dispatch, x, x.effectiveBits(), precision); } //============================================================================= // 双曲線余弦 (cosh) //============================================================================= static Float cosh_dispatch(const Float& x, int eff_x, int precision) { // sinhCoshDoubling: Taylor + K 回倍角復元 (除算不要, RawFloat) int wp = effectiveComputePrecision(eff_x, precision); auto [s, c] = sinhCoshDoubling(x, wp); return c; } Float cosh(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::positiveInfinity(); if (x.isZero()) return Float::one(precision); int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) return cosh_small(x, precision); return zivRound(cosh_dispatch, x, x.effectiveBits(), precision); } Float cosh(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::positiveInfinity(); if (x.isZero()) return Float::one(precision); int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) return cosh_small(x, precision); return zivRound(cosh_dispatch, x, x.effectiveBits(), precision); } //============================================================================= // 双曲線正接 (tanh) //============================================================================= static Float tanh_dispatch(const Float& x, int eff_x, int precision) { // sinhCoshDoubling + 1 回の除算 (exp+1/exp の 2 重除算を回避) int wp = effectiveComputePrecision(eff_x, precision); auto [s, c] = sinhCoshDoubling(x, wp); return s / c; } Float tanh(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { return x.isNegative() ? Float(-1) : Float(1); } if (x.isZero()) return Float::zero(); double x_d = x.toDouble(); if (std::fabs(x_d) > 20.0) { return x.isNegative() ? Float(-1) : Float(1); } int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) return tanh_small(x, precision); return zivRound(tanh_dispatch, x, x.effectiveBits(), precision); } Float tanh(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { return x.isNegative() ? Float(-1) : Float(1); } if (x.isZero()) return Float::zero(); double x_d = x.toDouble(); if (std::fabs(x_d) > 20.0) { return x.isNegative() ? Float(-1) : Float(1); } int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) return tanh_small(x, precision); return zivRound(tanh_dispatch, x, x.effectiveBits(), precision); } //============================================================================= // 累乗 (pow) - 整数指数 //============================================================================= static Float pow_core(Float x, int eff_x, int n, int precision) { // 有効ビット数に基づく計算精度の決定 int compute_prec = effectiveComputePrecision(eff_x, precision); // 作業精度 int working_precision = compute_prec + 10; x.truncateToApprox(working_precision); // 二分累乗法 // 各乗算後に truncateToApprox で仮数部を切り詰める。 // 乗算は仮数部のビット数を倍増させるため、放置すると // n=1000 で仮数が ~10000 ビットに肥大化し O(n²) になる。 Float result = Float::one(working_precision); int exp = n; while (exp > 0) { if (exp % 2 == 1) { result *= x; result.truncateToApprox(working_precision); } if (exp > 1) { x *= x; x.truncateToApprox(working_precision); } exp /= 2; } finalizeResult(result, eff_x, precision); return result; } Float pow(const Float& x, int n, int precision) { if (x.isNaN()) return Float::nan(); if (n == 0) return Float::one(precision); if (n == 1) return Float(x); if (x.isZero()) return (n < 0) ? Float::positiveInfinity() : Float::zero(); if (x.isInfinity()) { if (n < 0) return Float::zero(); if (x.isNegative() && (n % 2 != 0)) return Float::negativeInfinity(); return Float::positiveInfinity(); } if (n == -1) return Float::one(precision) / x; if (n < 0) return Float::one(precision) / pow(x, -n, precision); return pow_core(Float(x), x.effectiveBits(), n, precision); } Float pow(Float&& x, int n, int precision) { if (x.isNaN()) return Float::nan(); if (n == 0) return Float::one(precision); if (n == 1) return std::move(x); if (x.isZero()) return (n < 0) ? Float::positiveInfinity() : Float::zero(); if (x.isInfinity()) { if (n < 0) return Float::zero(); if (x.isNegative() && (n % 2 != 0)) return Float::negativeInfinity(); return Float::positiveInfinity(); } if (n == -1) return Float::one(precision) / x; if (n < 0) return Float::one(precision) / pow(std::move(x), -n, precision); int eff = x.effectiveBits(); return pow_core(std::move(x), eff, n, precision); } //============================================================================= // 累乗 (pow) - 一般の指数 //============================================================================= // pow ファストパス (19桁): Q128 log + Float mul + Q128 exp static Float pow_small(const Float& a, const Float& b, int precision) { // Q128 log → Float 化 → Float 乗算 → Q128 exp Float lna = log_small(a, precision); Float L = b * lna; return exp_small(L, precision); } // pow ファストパス (38桁): Q256 log + Float mul + exp static Float pow_medium(const Float& a, const Float& b, int precision) { int wp = precision + 5; // ガードビット (log→mul→exp の丸め誤差累積) Float lna = log_medium(a, wp); Float L = b * lna; Float result = exp(std::move(L), wp); result.setResultPrecision(precision); return result; } // pow 一般パス (Q128/Q256 判定 + フォールバック) static Float pow_general(const Float& a, const Float& b, int precision) { int precision_bits = Float::precisionToBits(precision); if (precision_bits <= 64) { return pow_small(a, b, precision); } if (precision_bits <= 128) { return pow_medium(a, b, precision); } // フォールバック: Float 演算 (Ziv 反復) int eff = std::min(a.effectiveBits(), b.effectiveBits()); return zivRound([&b](const Float& base, int e, int p) { int ep = p + 10; Float log_x = log(abs(base), ep); Float y_log_x = b * log_x; return exp(std::move(y_log_x), ep); }, a, eff, precision); } Float pow(const Float& x, const Float& y, int precision) { // 特殊値の処理 if (x.isNaN() || y.isNaN()) return Float::nan(); if (y.isZero()) return Float::one(precision); if (x.isZero()) { if (y.isNegative()) return Float::positiveInfinity(); return Float::zero(); } if (x.isInfinity()) { // 特殊ケースの処理 if (y.isPositive()) { return Float::positiveInfinity(); } else { return Float::zero(); } } if (y.isInfinity()) { // 特殊ケースの処理 Float one = Float::one(precision); if (abs(x) > one) { return y.isPositive() ? Float::positiveInfinity() : Float::zero(); } else if (abs(x) < one) { return y.isPositive() ? Float::zero() : Float::positiveInfinity(); } else { // |x| = 1 return Float::one(precision); } } // 整数指数の場合は専用の関数を使用 // mantissa * 2^exponent で exponent >= 0 なら自明に整数。 // exponent < 0 でも、mantissa の末尾ゼロビット数 >= |exponent| なら整数。 { bool y_is_integer = (y.exponent() >= 0) || (static_cast(y.mantissa().countTrailingZeros()) >= -y.exponent()); if (y_is_integer && abs(y) < Float(1e9)) { Int temp_int = y.toInt(); int n = temp_int.toInt(); return pow(x, n, precision); } } // 負の底の場合、非整数指数では複素数になる if (x.isNegative()) return Float::nan(); return pow_general(x, y, precision); } Float pow(Float&& x, Float&& y, int precision) { // 特殊値の処理 if (x.isNaN() || y.isNaN()) return Float::nan(); if (y.isZero()) return Float::one(precision); if (x.isZero()) { if (y.isNegative()) return Float::positiveInfinity(); return Float::zero(); } if (x.isInfinity()) { // 特殊ケースの処理 if (y.isPositive()) { return Float::positiveInfinity(); } else { return Float::zero(); } } if (y.isInfinity()) { // 特殊ケースの処理 Float one = Float::one(precision); if (abs(x) > one) { return y.isPositive() ? Float::positiveInfinity() : Float::zero(); } else if (abs(x) < one) { return y.isPositive() ? Float::zero() : Float::positiveInfinity(); } else { // |x| = 1 return Float::one(precision); } } // 整数指数の場合は専用の関数を使用 // mantissa * 2^exponent で exponent >= 0 なら自明に整数。 // exponent < 0 でも、mantissa の末尾ゼロビット数 >= |exponent| なら整数。 { bool y_is_integer = (y.exponent() >= 0) || (static_cast(y.mantissa().countTrailingZeros()) >= -y.exponent()); if (y_is_integer && abs(y) < Float(1e9)) { Int temp_int = y.toInt(); int n = temp_int.toInt(); return pow(std::move(x), n, precision); } } // 負の底の場合、非整数指数では複素数になる if (x.isNegative()) return Float::nan(); return pow_general(x, y, precision); } //============================================================================= // 逆正接関数 (atan) — Taylor 級数 + 引数半減 //============================================================================= // // ■ アルゴリズム // // atan(x) = x − x³/3 + x⁵/5 − x⁷/7 + ... // 漸化式: term_{k+1} = term_k · x² · (2k−1) / (2k+1) // // 引数半減公式: atan(x) = 2 · atan(x / (1 + √(1 + x²))) // K 回適用後 → Taylor 計算 → ldexp(result, K) で復元 // // |x| > 1 のとき: atan(x) = π/2 − atan(1/x) で |x| < 1 に縮小 // // Taylor 級数による atan(x) の内部計算 (|x| << 1 を想定) // sinTaylor/cosTaylor と同じ RawFloat パターン // Paterson-Stockmeyer for atan Taylor 級数 // atan(x) = x · Σ_{k=0}^{∞} (-1)^k · u^k / (2k+1) where u = x² // O(√N) 回のフルサイズ乗算 + O(N) 回のスカラー乗除算 // giant step の rr 更新: テレスコーピング積 Π(2l+2j+1)/Π(2l+2j+3) = (2l+1)/(2l+2m+1) static void rf_atan_ps(RawFloat& result, const RawFloat& x_rf, const RawFloat& x2, int nw, uint64_t* prod_buf, uint64_t* scratch, ScratchArena& arena, int working_precision) { int target_bits = nw * 64; // u = x² の MSB で項数推定 (階乗なし: N ≈ target_bits / u_log2) int64_t u_msb = x2.exp + static_cast(x2.nw) * 64; int u_log2 = (u_msb <= 0) ? static_cast(-u_msb) : 1; if (u_log2 < 1) u_log2 = 1; int l_est = target_bits / u_log2 + 5; int m = static_cast(std::sqrt(static_cast(l_est))); if (m < 2) m = 2; if (m > 256) m = 256; // m を偶数に揃える (Horner giant step で符号処理を簡略化) if (m % 2 != 0) m++; int G = l_est / m + 1; // giant step (ブロック) 数 // R[0..m] の事前計算: R[i] = u^i std::vector R(m + 1); for (int i = 0; i <= m; ++i) { R[i].d = arena.alloc_limbs(nw + 2); std::memset(R[i].d, 0, (nw + 2) * sizeof(uint64_t)); R[i].nw = 0; R[i].exp = 0; } R[0].d[nw - 1] = uint64_t(1) << 63; R[0].nw = static_cast(nw); R[0].exp = -static_cast(nw * 64 - 1); std::memcpy(R[1].d, x2.d, x2.nw * sizeof(uint64_t)); R[1].nw = x2.nw; R[1].exp = x2.exp; // R[1] の NTT キャッシュ prime_ntt::NttCache atan_cache_r1; rf_sqr(R[2], R[1], prod_buf, scratch, nw); for (int i = 3; i <= m; ++i) { if ((i & 1) == 0) rf_sqr(R[i], R[i/2], prod_buf, scratch, nw); else rf_mul_cached(R[i], R[i-1], R[1], prod_buf, nw, atan_cache_r1); } // W = u^m = R[m] (Horner giant step の乗算に使用) // R[m] の NTT キャッシュ prime_ntt::NttCache atan_cache_rm; // t = baby step 作業用, tmp = R[i]-t 用一時バッファ RawFloat t; t.d = arena.alloc_limbs(nw + 2); uint64_t* tmp_d = arena.alloc_limbs(nw + 2); // Horner giant step: 逆順 (j = G-1 → 0) で評価 // S(u) = Σ_{j=0}^{G-1} W^j · C_j(u) (m 偶数なので符号は C_j 内で処理) // Horner: acc = C_{G-1}; for j=G-2..0: acc = acc·W + C_j // rr 追跡不要 → giant step あたり 1 full mul (旧: 2 full mul) // result = 0 (accumulator) std::memset(result.d, 0, (nw + 2) * sizeof(uint64_t)); result.nw = 0; result.exp = 0; for (int j = G - 1; j >= 0; --j) { int l = j * m; // Giant step: acc = acc · W (最初のブロック以外) if (j < G - 1) { rf_mul_cached(result, result, R[m], prod_buf, nw, atan_cache_rm); } // Baby step: ブロック多項式 C_j(u) を評価 // C_j(u) = Σ_{i=0}^{m-1} (-1)^i · u^i / (2(l+i)+1) // Horner: t = R[m-1]/(2(l+m-1)+1), then down to R[0]/(2l+1) std::memcpy(t.d, R[m-1].d, R[m-1].nw * sizeof(uint64_t)); t.nw = R[m-1].nw; t.exp = R[m-1].exp; // t に最高次の係数 1/(2(l+m-1)+1) を反映 uint64_t top_den = static_cast(2*(l+m-1) + 1); rf_divmod_1(t, top_den); for (int i = m - 2; i >= 0; --i) { if (t.nw == 0) { // t = 0 → R[i]/(2(l+i)+1) に置換 std::memcpy(t.d, R[i].d, R[i].nw * sizeof(uint64_t)); t.nw = R[i].nw; t.exp = R[i].exp; uint64_t d = static_cast(2*(l+i) + 1); rf_divmod_1(t, d); } else { // t = R[i]/(2(l+i)+1) - t (交互級数の符号処理) std::memset(tmp_d, 0, (nw + 2) * sizeof(uint64_t)); std::memcpy(tmp_d, R[i].d, R[i].nw * sizeof(uint64_t)); RawFloat tmp{tmp_d, R[i].nw, R[i].exp}; uint64_t d = static_cast(2*(l+i) + 1); rf_divmod_1(tmp, d); rf_sub(tmp, t, nw + 1); std::memcpy(t.d, tmp.d, tmp.nw * sizeof(uint64_t)); std::memset(t.d + tmp.nw, 0, (nw + 2 - tmp.nw) * sizeof(uint64_t)); t.nw = tmp.nw; t.exp = tmp.exp; } } if (t.nw == 0) continue; // C_j を accumulator に加算 rf_add(result, t, nw + 1); } // atan(x) = x · S(u): result *= x rf_mul(result, result, x_rf, prod_buf, scratch, nw); } static Float atanTaylor(const Float& x, int working_precision) { int wp_bits = Float::precisionToBits(working_precision); int nw = (wp_bits + 63) / 64; ScratchScope scope; auto& arena = getThreadArena(); // バッファ事前確保 size_t prod_alloc = 2 * static_cast(nw) + 4; uint64_t* prod_buf = arena.alloc_limbs(prod_alloc); size_t mul_scratch = mpn::multiply_scratch_size(nw + 2, nw + 2); size_t sqr_scratch = mpn::square_scratch_size(nw + 2); size_t scratch_sz = std::max(mul_scratch, sqr_scratch); uint64_t* scratch = arena.alloc_limbs(scratch_sz > 0 ? scratch_sz : 1); // |x| を抽出 RawFloat x_rf = rf_extract(x, nw, arena); // x² を事前計算 (自乗は square で高速化) uint64_t* x2_d = arena.alloc_limbs(nw + 2); RawFloat x2{x2_d, 0, 0}; rf_sqr(x2, x_rf, prod_buf, scratch, nw); uint64_t* result_d = arena.alloc_limbs(nw + 2); std::memset(result_d, 0, (nw + 2) * sizeof(uint64_t)); if (wp_bits >= 10000) { // Paterson-Stockmeyer: O(√N) 回のフルサイズ乗算 RawFloat result{result_d, 0, 0}; rf_atan_ps(result, x_rf, x2, nw, prod_buf, scratch, arena, working_precision); return rf_to_float(result, x.isNegative(), working_precision); } // 素朴な Taylor 級数 (低精度) uint64_t* term_d = arena.alloc_limbs(nw + 2); std::memset(term_d, 0, (nw + 2) * sizeof(uint64_t)); std::memcpy(term_d, x_rf.d, x_rf.nw * sizeof(uint64_t)); RawFloat term{term_d, x_rf.nw, x_rf.exp}; std::memcpy(result_d, x_rf.d, x_rf.nw * sizeof(uint64_t)); RawFloat result{result_d, x_rf.nw, x_rf.exp}; int max_terms = static_cast(working_precision * 1.2) + 10; for (int k = 1; k <= max_terms; ++k) { // term *= x² rf_mul(term, term, x2, prod_buf, scratch, nw); // term *= (2k-1) — k=1 のとき 2k-1=1 なので skip if (k > 1) { rf_mul_1(term, static_cast(2*k - 1)); } // term /= (2k+1) rf_divmod_1(term, static_cast(2*k + 1)); if (term.nw == 0) break; if (k & 1) { if (!rf_sub(result, term, nw + 1)) break; } else { if (!rf_add(result, term, nw + 1)) break; } } return rf_to_float(result, x.isNegative(), working_precision); } // 引数半減 + Taylor による atan 計算 // atan(x) = 2^K · atan(t), t = K 回半減した引数 // P-S 使用時は Taylor 部分が O(√N) なので K を大幅に減らし、 // 高コストな半減 (各回 sqrt+div) の回数を削減する。 static Float atanHalving(const Float& x, int working_precision) { int wp_bits = Float::precisionToBits(working_precision); int K; if (wp_bits >= 10000) { // P-S 有効: 最適 K = (√(n/2)/C_halv)^{2/3} ≈ cbrt(n/50) K = static_cast(std::cbrt(wp_bits / 50.0)); } else { K = static_cast(std::sqrt(wp_bits / 20.0)); } if (K < 1) return atanTaylor(x, working_precision); // ガードビット: K 回半減の誤差蓄積分 int guard_bits = 2 * K + static_cast(std::ceil(std::log2(K + 1))) + 10; int wp_inner = working_precision + Float::bitsToPrecision(guard_bits); // K 回半減: t = x / (1 + sqrt(1 + x²)) Float t = x; t.truncateToApprox(wp_inner); int wp_inner_bits = Float::precisionToBits(wp_inner); t.setEffectiveBits(wp_inner_bits); Float one_val = Float::one(wp_inner); for (int i = 0; i < K; ++i) { Float t2 = t * t; t2.truncateToApprox(wp_inner); t = t / (one_val + sqrt(one_val + t2, wp_inner)); t.truncateToApprox(wp_inner); } t.setEffectiveBits(wp_inner_bits); // 縮小引数に対して Taylor 級数 Float result = atanTaylor(t, wp_inner); // 復元: atan(x) = 2^K · atan(t) result = ldexp(result, K); result.truncateToApprox(working_precision); return result; } // double → Q128 変換 (53-bit 精度を Q128 の適切な位置に配置) // val ∈ [0, 1) を想定。val = 0 の場合 hi=lo=0 を返す。 static void double_to_q128(double val, uint64_t& hi, uint64_t& lo) { if (val == 0.0) { hi = lo = 0; return; } int exp; double m = std::frexp(val, &exp); // m ∈ [0.5, 1), val = m * 2^exp // m * 2^53 は 53-bit 整数 uint64_t m_int = static_cast(m * static_cast(1ULL << 53)); // Q128: val * 2^128 = m_int * 2^(exp - 53 + 128) = m_int * 2^(exp + 75) int shift = exp + 75; if (shift >= 64) { hi = m_int << (shift - 64); lo = 0; } else if (shift >= 0) { hi = m_int >> (64 - shift); lo = m_int << shift; } else { hi = 0; lo = m_int >> (-shift); } } // Float → Q128 変換 (仮数部の上位 128 bit を Q128 に配置) // x ∈ (0, 1) を想定 static void float_to_q128(const Float& x, uint64_t& hi, uint64_t& lo) { const Int& mant = x.mantissa(); const uint64_t* mw = mant.data(); size_t mn = mant.size(); int64_t e = x.exponent(); uint64_t top = mw[mn - 1]; int top_bits = 64 - std::countl_zero(top); int64_t total_bl = static_cast((mn - 1) * 64) + top_bits; // 仮数部を正規化 (MSB を bit 127 に配置) int clz = 64 - top_bits; uint64_t m_hi, m_lo; if (clz == 0) { m_hi = top; m_lo = (mn >= 2) ? mw[mn - 2] : 0; } else { m_hi = (top << clz) | ((mn >= 2) ? (mw[mn - 2] >> (64 - clz)) : 0); if (mn >= 2) { m_lo = (mw[mn - 2] << clz) | ((mn >= 3) ? (mw[mn - 3] >> (64 - clz)) : 0); } else { m_lo = 0; } } // x = mant * 2^e, mant ≈ m_hi:m_lo * 2^(total_bl - 128) // Q128 = x * 2^128 = m_hi:m_lo * 2^(e + total_bl) int64_t sr = -(e + total_bl); // 右シフト量 (x < 1 なので sr > 0) if (sr <= 0) { // x >= 1: ここには来ないはず hi = m_hi; lo = m_lo; } else if (sr < 64) { hi = (m_hi >> sr); lo = (m_hi << (64 - sr)) | (m_lo >> sr); } else if (sr < 128) { hi = 0; lo = m_hi >> (sr - 64); } else { hi = lo = 0; } } // Float → Q128 逆数変換: 1/x を Q128 に変換 (x > 1) // 仮数部上位 64 bit から 3 段 _udiv128 で ~130 bit 精度の逆数を計算 static void float_recip_q128(const Float& x, uint64_t& hi, uint64_t& lo) { const Int& mant = x.mantissa(); const uint64_t* mw = mant.data(); size_t mn = mant.size(); int64_t e = x.exponent(); uint64_t top = mw[mn - 1]; int top_bits = 64 - std::countl_zero(top); int64_t total_bl = static_cast((mn - 1) * 64) + top_bits; int clz = 64 - top_bits; uint64_t m_norm; if (clz == 0) { m_norm = top; } else if (mn >= 2) { m_norm = (top << clz) | (mw[mn - 2] >> (64 - clz)); } else { m_norm = top << clz; } // 3 段除算: 2^192 / m_norm → (q1:q2:q3), ~130 bit 精度 uint64_t r1, r2, r3; uint64_t q1 = _udiv128(1, 0, m_norm, &r1); uint64_t q2 = _udiv128(r1, 0, m_norm, &r2); uint64_t q3 = _udiv128(r2, 0, m_norm, &r3); // Q128(1/x) = (q1:q2:q3) * 2^{-total_bl - e} // x > 1 → total_bl + e >= 1 int64_t sr = total_bl + e; if (sr > 0 && sr < 64) { hi = (q1 << (64 - sr)) | (q2 >> sr); lo = (q2 << (64 - sr)) | (q3 >> sr); } else if (sr == 0) { hi = q2; lo = q3; } else if (sr >= 64 && sr < 128) { int s2 = static_cast(sr - 64); hi = q2 >> s2; lo = (q2 << (64 - s2)) | (q3 >> s2); } else { hi = lo = 0; } } // Q128 atan ファストパス (≤ 64-bit 精度) // Newton 法: y₁ = y₀ + (x·cos(y₀) - sin(y₀)) / (cos(y₀) + x·sin(y₀)) // y₀ = std::atan(x) (53-bit), Newton 1 ステップで ~106-bit に倍増 // sin(y₀), vers(y₀) = 1 - cos(y₀) を Q128 Taylor+角度倍増で計算 static Float atan_small(const Float& x, int precision) { double x_double = x.toDouble(); bool negative = x_double < 0; if (negative) x_double = -x_double; // x = 0 (既に呼び出し側で処理済みだが念のため) if (x_double == 0.0) return Float::zero(precision); // |x| > 1: atan(x) = π/2 - atan(1/x) bool complemented = false; if (x_double > 1.0) { x_double = 1.0 / x_double; complemented = true; } else if (x_double == 1.0) { // atan(1) = π/4 Float result = ldexp(Float::pi(precision), -2); if (negative) result = -result; result.setResultPrecision(precision); return result; } // x を Q128 に変換 (x ∈ (0, 1)) uint64_t x_hi, x_lo; if (complemented) { float_recip_q128(abs(x), x_hi, x_lo); } else { float_to_q128(abs(x), x_hi, x_lo); } // y₀ = atan(x_double), 53-bit 初期近似 double y0 = std::atan(x_double); // === Q128 sincos: sin(y₀) と vers(y₀) = 1-cos(y₀) を計算 === // 引数縮小: y_red = y₀ / 2^K constexpr int K = 4; double y_red = y0 * 0.0625; // y0 / 16, |y_red| < π/64 ≈ 0.049 // y_red → Q128 uint64_t yr_hi, yr_lo; double_to_q128(y_red, yr_hi, yr_lo); // y² in Q128 uint64_t y2_hi, y2_lo; q128_mul(yr_hi, yr_lo, yr_hi, yr_lo, y2_hi, y2_lo); // sin(y_red) Taylor: y - y³/3! + y⁵/5! - y⁷/7! + ... uint64_t sin_hi = yr_hi, sin_lo = yr_lo; { uint64_t term_hi = yr_hi, term_lo = yr_lo; for (int k = 1; k <= 9; k++) { q128_mul(term_hi, term_lo, y2_hi, y2_lo, term_hi, term_lo); uint64_t divisor = static_cast(2 * k) * static_cast(2 * k + 1); uint64_t rem; term_hi = _udiv128(0, term_hi, divisor, &rem); term_lo = _udiv128(rem, term_lo, divisor, &rem); if (term_hi == 0 && term_lo == 0) break; if (k & 1) { // k=1: -y³/6, k=3: -y⁷/5040, ... unsigned char borrow = _subborrow_u64(0, sin_lo, term_lo, &sin_lo); _subborrow_u64(borrow, sin_hi, term_hi, &sin_hi); } else { // k=2: +y⁵/120, k=4: +y⁹/362880, ... unsigned char carry = _addcarry_u64(0, sin_lo, term_lo, &sin_lo); _addcarry_u64(carry, sin_hi, term_hi, &sin_hi); } } } // vers(y_red) Taylor: y²/2! - y⁴/4! + y⁶/6! - ... uint64_t vers_hi, vers_lo; { uint64_t rem; vers_hi = _udiv128(0, y2_hi, 2, &rem); vers_lo = _udiv128(rem, y2_lo, 2, &rem); } { uint64_t term_hi = vers_hi, term_lo = vers_lo; for (int k = 1; k <= 9; k++) { q128_mul(term_hi, term_lo, y2_hi, y2_lo, term_hi, term_lo); uint64_t divisor = static_cast(2 * k + 1) * static_cast(2 * k + 2); uint64_t rem; term_hi = _udiv128(0, term_hi, divisor, &rem); term_lo = _udiv128(rem, term_lo, divisor, &rem); if (term_hi == 0 && term_lo == 0) break; if (k & 1) { // k=1: -y⁴/24, k=3: -y⁸/40320, ... unsigned char borrow = _subborrow_u64(0, vers_lo, term_lo, &vers_lo); _subborrow_u64(borrow, vers_hi, term_hi, &vers_hi); } else { // k=2: +y⁶/720, k=4: +y^10/..., ... unsigned char carry = _addcarry_u64(0, vers_lo, term_lo, &vers_lo); _addcarry_u64(carry, vers_hi, term_hi, &vers_hi); } } } // 角度倍増 K 回: sin(2θ) = 2sin(θ)(1-vers(θ)), vers(2θ) = 2sin²(θ) for (int i = 0; i < K; i++) { // sin(2θ) = 2·sin·cos = 2·sin·(1 - vers) = 2(sin - sin·vers) uint64_t sv_hi, sv_lo; q128_mul(sin_hi, sin_lo, vers_hi, vers_lo, sv_hi, sv_lo); uint64_t diff_hi, diff_lo; unsigned char borrow = _subborrow_u64(0, sin_lo, sv_lo, &diff_lo); _subborrow_u64(borrow, sin_hi, sv_hi, &diff_hi); uint64_t sin_new_hi = (diff_hi << 1) | (diff_lo >> 63); uint64_t sin_new_lo = diff_lo << 1; // vers(2θ) = 2·sin²(θ) uint64_t ss_hi, ss_lo; q128_mul(sin_hi, sin_lo, sin_hi, sin_lo, ss_hi, ss_lo); uint64_t vers_new_hi = (ss_hi << 1) | (ss_lo >> 63); uint64_t vers_new_lo = ss_lo << 1; sin_hi = sin_new_hi; sin_lo = sin_new_lo; vers_hi = vers_new_hi; vers_lo = vers_new_lo; } // === Newton 補正 === // numerator = x·cos(y₀) - sin(y₀) = (x - sin) - x·vers uint64_t xv_hi, xv_lo; q128_mul(x_hi, x_lo, vers_hi, vers_lo, xv_hi, xv_lo); // x - sin (符号付き) bool xms_neg; uint64_t xms_hi, xms_lo; if (x_hi > sin_hi || (x_hi == sin_hi && x_lo >= sin_lo)) { xms_neg = false; unsigned char b = _subborrow_u64(0, x_lo, sin_lo, &xms_lo); _subborrow_u64(b, x_hi, sin_hi, &xms_hi); } else { xms_neg = true; unsigned char b = _subborrow_u64(0, sin_lo, x_lo, &xms_lo); _subborrow_u64(b, sin_hi, x_hi, &xms_hi); } // num = (x - sin) - x·vers bool num_neg; uint64_t num_hi, num_lo; if (!xms_neg) { if (xms_hi > xv_hi || (xms_hi == xv_hi && xms_lo >= xv_lo)) { num_neg = false; unsigned char b = _subborrow_u64(0, xms_lo, xv_lo, &num_lo); _subborrow_u64(b, xms_hi, xv_hi, &num_hi); } else { num_neg = true; unsigned char b = _subborrow_u64(0, xv_lo, xms_lo, &num_lo); _subborrow_u64(b, xv_hi, xms_hi, &num_hi); } } else { // (x - sin) < 0: num = -(|x-sin| + x·vers) < 0 num_neg = true; unsigned char c = _addcarry_u64(0, xms_lo, xv_lo, &num_lo); _addcarry_u64(c, xms_hi, xv_hi, &num_hi); } // denominator (double 精度で十分): d = cos(y₀) + x·sin(y₀) ≈ sec(y₀) double sin_d = std::sin(y0); double cos_d = std::cos(y0); double denom = cos_d + x_double * sin_d; // δ = num / denom (Q128 × double 逆数) // frexp で分解して 128×64 乗算 + シフト int d_exp; double d_m = std::frexp(1.0 / denom, &d_exp); // d_m ∈ [0.5, 1) uint64_t d_int = static_cast(d_m * static_cast(1ULL << 53)); // 128-bit × 64-bit → 192-bit uint64_t p1_hi; uint64_t p1_lo = _umul128(num_lo, d_int, &p1_hi); uint64_t p2_hi; uint64_t p2_lo = _umul128(num_hi, d_int, &p2_hi); unsigned char c = _addcarry_u64(0, p2_lo, p1_hi, &p2_lo); _addcarry_u64(c, p2_hi, 0, &p2_hi); // 右シフト (53 - d_exp) で Q128 に戻す int shift = 53 - d_exp; uint64_t delta_hi, delta_lo; if (shift >= 64) { int s2 = shift - 64; if (s2 < 64) { delta_hi = p2_hi >> s2; delta_lo = (p2_hi << (64 - s2)) | (p2_lo >> s2); } else { delta_hi = 0; delta_lo = (s2 < 128) ? (p2_hi >> (s2 - 64)) : 0; } } else if (shift > 0) { delta_hi = (p2_hi << (64 - shift)) | (p2_lo >> shift); delta_lo = (p2_lo << (64 - shift)) | (p1_lo >> shift); } else { delta_hi = p2_lo; delta_lo = p1_lo; } // y₁ = y₀ ± δ uint64_t y0_hi, y0_lo; double_to_q128(y0, y0_hi, y0_lo); uint64_t r_hi, r_lo; if (!num_neg) { unsigned char carry = _addcarry_u64(0, y0_lo, delta_lo, &r_lo); _addcarry_u64(carry, y0_hi, delta_hi, &r_hi); } else { unsigned char borrow = _subborrow_u64(0, y0_lo, delta_lo, &r_lo); _subborrow_u64(borrow, y0_hi, delta_hi, &r_hi); } // === 精度に応じた結果生成 === int precision_bits = Float::precisionToBits(precision); if (precision_bits <= 64) { // 19 桁: Q128 結果で十分 (~106 bit 精度) Float result = q128_to_float(r_hi, r_lo, -128); if (complemented) { Float pi_half = ldexp(Float::pi(precision), -1); result = pi_half - result; } if (negative) result = -result; result.setResultPrecision(precision); return result; } // === 38 桁 (≤ 128 bit): Q256 Newton 第2段 === // y₁ (Q128, ~106 bit) → y₂ (Q256, ~212 bit) // y₁ を Q256 に昇格: Q128 (hi:lo) → Q256 (hi:lo:0:0) uint64_t y1_256[4] = { 0, 0, r_lo, r_hi }; // x を Q256 に変換 (完全精度) uint64_t x256[4]; if (complemented) { Float x_inv = Float::one(precision + 10) / abs(x); x_inv.truncateToApprox(precision + 10); float_to_q256(x_inv, x256); } else { float_to_q256(abs(x), x256); } // Q256 sincos(y₁): Taylor + 角度倍増 constexpr int K2 = 4; uint64_t yr2[4]; // y₁ / 2^K2 (右シフト K2 bits) yr2[0] = (y1_256[0] >> K2) | (y1_256[1] << (64 - K2)); yr2[1] = (y1_256[1] >> K2) | (y1_256[2] << (64 - K2)); yr2[2] = (y1_256[2] >> K2) | (y1_256[3] << (64 - K2)); yr2[3] = y1_256[3] >> K2; uint64_t y2sq[4]; q256_mul(yr2, yr2, y2sq); // sin Taylor uint64_t sin2[4] = { yr2[0], yr2[1], yr2[2], yr2[3] }; { uint64_t term[4] = { yr2[0], yr2[1], yr2[2], yr2[3] }; for (int k = 1; k <= 15; k++) { q256_mul(term, y2sq, term); q256_div_scalar(term, static_cast(2*k) * static_cast(2*k+1)); if (q256_is_zero(term)) break; if (k & 1) q256_sub(sin2, term, sin2); else q256_add(sin2, term, sin2); } } // vers Taylor uint64_t vers2[4] = { y2sq[0], y2sq[1], y2sq[2], y2sq[3] }; q256_div_scalar(vers2, 2); { uint64_t term[4] = { vers2[0], vers2[1], vers2[2], vers2[3] }; for (int k = 1; k <= 15; k++) { q256_mul(term, y2sq, term); q256_div_scalar(term, static_cast(2*k+1) * static_cast(2*k+2)); if (q256_is_zero(term)) break; if (k & 1) q256_sub(vers2, term, vers2); else q256_add(vers2, term, vers2); } } // 角度倍増 K2 回 (sin² を先に計算してから sin を更新) for (int i = 0; i < K2; i++) { uint64_t ss[4]; // sin²(θ) — 更新前の sin で計算 q256_mul(sin2, sin2, ss); uint64_t sv[4]; // sin(θ)·vers(θ) q256_mul(sin2, vers2, sv); // sin(2θ) = 2(sin - sin·vers) uint64_t diff[4]; q256_sub(sin2, sv, diff); q256_shl1(diff, sin2); // vers(2θ) = 2·sin²(θ) q256_shl1(ss, vers2); } // Newton 補正 (Q256): num = (x - sin) - x·vers uint64_t xv2[4]; q256_mul(x256, vers2, xv2); bool num2_neg; uint64_t num2[4]; { uint64_t xms[4]; bool xms_neg2 = !q256_ge(x256, sin2); if (!xms_neg2) { q256_sub(x256, sin2, xms); } else { q256_sub(sin2, x256, xms); } if (!xms_neg2) { if (q256_ge(xms, xv2)) { num2_neg = false; q256_sub(xms, xv2, num2); } else { num2_neg = true; q256_sub(xv2, xms, num2); } } else { num2_neg = true; q256_add(xms, xv2, num2); } } // denominator (double 精度): y₁ の double 近似で十分 double y1_d = static_cast(r_hi) * 5.421010862427522e-20; // * 2^{-64} // y₁ ≈ (r_hi:r_lo) * 2^{-128}, double では r_hi * 2^{-64} が主項 // より正確に: y₁ ∈ [0, π/4), y₀ を使う方が簡単 double sin2_d = std::sin(y0); // y₁ ≈ y₀ (53-bit 一致) double cos2_d = std::cos(y0); double denom2 = cos2_d + x_double * sin2_d; // δ₂ = num2 / denom2 (Q256 × double 逆数) int d2_exp; double d2_m = std::frexp(1.0 / denom2, &d2_exp); uint64_t d2_int = static_cast(d2_m * static_cast(1ULL << 53)); // 256-bit × 64-bit → 320-bit, 上位 256 bit を取得 uint64_t prod[5] = {}; for (int i = 0; i < 4; i++) { uint64_t hi; uint64_t lo = _umul128(num2[i], d2_int, &hi); unsigned char c1 = _addcarry_u64(0, prod[i], lo, &prod[i]); unsigned char c2 = _addcarry_u64(0, prod[i + 1], hi, &prod[i + 1]); if (c1) { unsigned char c3 = _addcarry_u64(0, prod[i + 1], 1, &prod[i + 1]); if (c3 && i + 2 < 5) prod[i + 2]++; } if (c2 && i + 2 < 5) prod[i + 2]++; } // 右シフト (53 - d2_exp) で Q256 に戻す int shift2 = 53 - d2_exp; uint64_t delta2[4]; if (shift2 >= 64) { int s = shift2 - 64; if (s < 64) { delta2[0] = (prod[1] >> s) | (prod[2] << (64 - s)); delta2[1] = (prod[2] >> s) | (prod[3] << (64 - s)); delta2[2] = (prod[3] >> s) | (prod[4] << (64 - s)); delta2[3] = prod[4] >> s; } else { delta2[0] = delta2[1] = delta2[2] = delta2[3] = 0; } } else if (shift2 > 0) { delta2[0] = (prod[0] >> shift2) | (prod[1] << (64 - shift2)); delta2[1] = (prod[1] >> shift2) | (prod[2] << (64 - shift2)); delta2[2] = (prod[2] >> shift2) | (prod[3] << (64 - shift2)); delta2[3] = (prod[3] >> shift2) | (prod[4] << (64 - shift2)); } else { delta2[0] = prod[0]; delta2[1] = prod[1]; delta2[2] = prod[2]; delta2[3] = prod[3]; } // y₂ = y₁ ± δ₂ uint64_t res256[4]; if (!num2_neg) { q256_add(y1_256, delta2, res256); } else { q256_sub(y1_256, delta2, res256); } Float result = q256_to_float(res256, -256); if (complemented) { Float pi_half = ldexp(Float::pi(precision), -1); result = pi_half - result; } if (negative) result = -result; result.setResultPrecision(precision); return result; } static Float atan_core(Float x, int eff_x, int precision) { int compute_prec = effectiveComputePrecision(eff_x, precision); int working_precision = compute_prec + 10; bool negative = x.isNegative(); x = abs(x); // |x| > 1: atan(x) = π/2 − atan(1/x) で |x| < 1 に縮小 bool complemented = false; Float one_wp = Float::one(working_precision); if (x > one_wp) { x = one_wp / x; x.truncateToApprox(working_precision); complemented = true; } Float result = atanHalving(x, working_precision); if (complemented) { result = ldexp(Float::pi(working_precision), -1) - result; } if (negative) result = -result; finalizeResult(result, eff_x, precision); return result; } Float atan(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { Float pi_half = ldexp(Float::pi(precision), -1); return x.isNegative() ? -pi_half : pi_half; } if (x.isZero()) return Float::zero(precision); int precision_bits = Float::precisionToBits(precision); if (precision_bits <= 128) { return atan_small(x, precision); } return zivRound([](const Float& a, int e, int p) { return atan_core(Float(a), e, p); }, x, x.effectiveBits(), precision); } Float atan(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { Float pi_half = ldexp(Float::pi(precision), -1); return x.isNegative() ? -pi_half : pi_half; } if (x.isZero()) return Float::zero(precision); int precision_bits = Float::precisionToBits(precision); if (precision_bits <= 128) { return atan_small(x, precision); } return zivRound([](const Float& a, int e, int p) { return atan_core(Float(a), e, p); }, x, x.effectiveBits(), precision); } //============================================================================= // 逆正弦関数 (asin) //============================================================================= // // アルゴリズム: // asin(x) = atan(x / sqrt(1 - x²)) // |x| = 1 のとき: asin(±1) = ±π/2 // // この恒等式により、asin の計算を atan に帰着させる。 // atan は AGM 法で高効率に計算されるため、asin も高精度。 // // 参考: 高精度計算プログラム (lib++20), Brent 1976 // // asin Q128 ファストパス: 軽量 Float パス → atan_small static Float asin_small(const Float& x, int precision) { // asin(x) = atan(x / sqrt(1 - x²)) int wp = precision + 2; Float x2 = x * x; Float u = Float::one(wp) - std::move(x2); Float sq = sqrt(std::move(u), wp); Float arg = x / sq; Float result = atan(std::move(arg), precision); result.setResultPrecision(precision); return result; } // asin 38桁ファストパス: 軽量 Float パス static Float asin_medium(const Float& x, int precision) { int wp = precision + 4; Float x2 = x * x; Float u = Float::one(wp) - std::move(x2); Float sq = sqrt(std::move(u), wp); Float arg = x / sq; Float result = atan(std::move(arg), precision); result.setResultPrecision(precision); return result; } Float asin(const Float& x, int precision) { // 特殊値の処理 if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); // 定義域外 if (x.isZero()) return Float::zero(precision); // |x| > 1 は定義域外 Float one_p = Float::one(precision); Float abs_x = abs(x); if (abs_x > one_p) { return Float::nan(); } // |x| = 1 のとき: asin(±1) = ±π/2 if (abs_x == one_p) { Float pi_half = Float::pi(precision); pi_half >>= 1; return x.isNegative() ? -pi_half : pi_half; } int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) return asin_small(x, precision); if (x.mantissa().size() <= 2 && precision_bits <= 128) return asin_medium(x, precision); return zivRound([](const Float& a, int e, int p) { int wp = p + 4; Float X = a; X.truncateToApprox(wp); X.setEffectiveBits(Float::precisionToBits(wp)); Float u = Float::one(wp) - X * X; u.truncateToApprox(wp); Float sq = sqrt(u, wp); Float arg = X / sq; arg.truncateToApprox(wp); return atan(std::move(arg), wp); }, x, x.effectiveBits(), precision); } Float asin(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (x.isZero()) return Float::zero(precision); Float one_p = Float::one(precision); Float abs_x = abs(x); if (abs_x > one_p) return Float::nan(); if (abs_x == one_p) { Float pi_half = Float::pi(precision); pi_half >>= 1; return x.isNegative() ? -pi_half : pi_half; } int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) return asin_small(x, precision); if (x.mantissa().size() <= 2 && precision_bits <= 128) return asin_medium(x, precision); return zivRound([](const Float& a, int e, int p) { int wp = p + 4; Float X = a; X.truncateToApprox(wp); X.setEffectiveBits(Float::precisionToBits(wp)); Float u = Float::one(wp) - X * X; u.truncateToApprox(wp); Float sq = sqrt(u, wp); Float arg = X / sq; arg.truncateToApprox(wp); return atan(std::move(arg), wp); }, x, x.effectiveBits(), precision); } //============================================================================= // 逆余弦関数 (acos) //============================================================================= // // アルゴリズム: // acos(x) = π/2 - asin(x) // // 最も単純かつ正確な実装。asin が高精度であれば acos も高精度。 // Float acos(const Float& x, int precision) { // 特殊値の処理 if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); // 定義域外 if (x.isZero()) { Float result = Float::pi(precision); result >>= 1; return result; } // 19桁ファストパス: 重い Float 比較の前に分岐 int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) { // |x| > 1 チェック (double で十分) double x_d = std::fabs(x.toDouble()); if (x_d > 1.0) return Float::nan(); if (x_d == 1.0) { return x.isNegative() ? Float::pi(precision) : Float::zero(precision); } Float asin_x = asin_small(x, precision); Float pi_half = Float::pi(precision); pi_half >>= 1; Float result = std::move(pi_half) - std::move(asin_x); result.setResultPrecision(precision); return result; } // 38桁ファストパス if (x.mantissa().size() <= 2 && precision_bits <= 128) { double x_d = std::fabs(x.toDouble()); if (x_d > 1.0) return Float::nan(); if (x_d == 1.0) { return x.isNegative() ? Float::pi(precision) : Float::zero(precision); } Float asin_x = asin_medium(x, precision); Float pi_half = Float::pi(precision); pi_half >>= 1; Float result = std::move(pi_half) - std::move(asin_x); result.setResultPrecision(precision); return result; } // |x| > 1 は定義域外 Float one_p = Float::one(precision); Float abs_x = abs(x); if (abs_x > one_p) { return Float::nan(); } // acos(1) = 0, acos(-1) = π if (x == one_p) return Float::zero(precision); if (x == -one_p) return Float::pi(precision); return zivRound([](const Float& a, int e, int p) { int wp = p + 4; Float pi_half = Float::pi(wp); pi_half >>= 1; Float asin_x = asin(a, wp); return pi_half - asin_x; }, x, x.effectiveBits(), precision); } Float acos(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (x.isZero()) { Float result = Float::pi(precision); result >>= 1; return result; } int precision_bits = Float::precisionToBits(precision); if (x.mantissa().size() <= 1 && precision_bits <= 64) { double x_d = std::fabs(x.toDouble()); if (x_d > 1.0) return Float::nan(); if (x_d == 1.0) { return x.isNegative() ? Float::pi(precision) : Float::zero(precision); } Float asin_x = asin_small(x, precision); Float pi_half = Float::pi(precision); pi_half >>= 1; Float result = std::move(pi_half) - std::move(asin_x); result.setResultPrecision(precision); return result; } if (x.mantissa().size() <= 2 && precision_bits <= 128) { double x_d = std::fabs(x.toDouble()); if (x_d > 1.0) return Float::nan(); if (x_d == 1.0) { return x.isNegative() ? Float::pi(precision) : Float::zero(precision); } Float asin_x = asin_medium(x, precision); Float pi_half = Float::pi(precision); pi_half >>= 1; Float result = std::move(pi_half) - std::move(asin_x); result.setResultPrecision(precision); return result; } Float one_p = Float::one(precision); Float abs_x = abs(x); if (abs_x > one_p) return Float::nan(); if (x == one_p) return Float::zero(precision); if (x == -one_p) return Float::pi(precision); return zivRound([](const Float& a, int e, int p) { int wp = p + 4; Float pi_half = Float::pi(wp); pi_half >>= 1; Float asin_x = asin(a, wp); return pi_half - asin_x; }, x, x.effectiveBits(), precision); } //============================================================================= // 2引数逆正接関数 (atan2) //============================================================================= // // atan2(y, x) は点 (x, y) の偏角を (-π, π] の範囲で返す。 // 象限ごとの場合分けにより atan(y/x) に帰着させる。 // // x > 0: atan(y/x) // x < 0, y >= 0: atan(y/x) + π // x < 0, y < 0: atan(y/x) - π // x = 0, y > 0: +π/2 // x = 0, y < 0: -π/2 // x = 0, y = 0: 0 (IEEE 754 準拠、符号による細分化は省略) // Float atan2(const Float& y, const Float& x, int precision) { // 特殊値の処理 if (x.isNaN() || y.isNaN()) return Float::nan(); int eff = std::min(x.effectiveBits(), y.effectiveBits()); int wp = precision + 4; // x = 0 の場合 if (x.isZero()) { if (y.isZero()) { return Float::zero(precision); } else if (y.isNegative()) { Float result = -ldexp(Float::pi(wp), -1); result.setResultPrecision(precision); return result; } else { Float result = ldexp(Float::pi(wp), -1); result.setResultPrecision(precision); return result; } } // y = 0 の場合 if (y.isZero()) { if (x.isNegative()) { return Float::pi(precision); // atan2(0, -x) = π } else { return Float::zero(precision); // atan2(0, +x) = 0 } } // 無限大の処理 if (x.isInfinity() && y.isInfinity()) { // 両方無限大の場合 Float pi_quarter = ldexp(Float::pi(wp), -2); Float result; if (x.isNegative()) { result = y.isNegative() ? -(mulScalarF(pi_quarter, uint64_t(3))) : (mulScalarF(pi_quarter, uint64_t(3))); } else { result = y.isNegative() ? -pi_quarter : pi_quarter; } result.setResultPrecision(precision); return result; } if (x.isInfinity()) { // x = ±∞, y は有限 if (x.isNegative()) { Float result = y.isNegative() ? -Float::pi(wp) : Float::pi(wp); result.setResultPrecision(precision); return result; } else { return Float::zero(precision); } } if (y.isInfinity()) { // y = ±∞, x は有限 Float pi_half = ldexp(Float::pi(wp), -1); Float result = y.isNegative() ? -pi_half : pi_half; result.setResultPrecision(precision); return result; } // 通常の場合: 象限判定 + atan(y/x) Float Y = y; Y.truncateToApprox(wp); Float X = x; X.truncateToApprox(wp); Float ratio = Y / X; ratio.truncateToApprox(wp); Float base = atan(ratio, wp); if (x.isNegative()) { Float pi_wp = Float::pi(wp); if (y.isNegative()) { base = base - pi_wp; // 第3象限 } else { base = base + pi_wp; // 第2象限 } } finalizeResult(base, eff, precision); return base; } Float atan2(Float&& y, Float&& x, int precision) { // 特殊値の処理 if (x.isNaN() || y.isNaN()) return Float::nan(); int eff = std::min(x.effectiveBits(), y.effectiveBits()); int wp = precision + 4; // x = 0 の場合 if (x.isZero()) { if (y.isZero()) { return Float::zero(precision); } else if (y.isNegative()) { Float result = -ldexp(Float::pi(wp), -1); result.setResultPrecision(precision); return result; } else { Float result = ldexp(Float::pi(wp), -1); result.setResultPrecision(precision); return result; } } // y = 0 の場合 if (y.isZero()) { if (x.isNegative()) { return Float::pi(precision); // atan2(0, -x) = pi } else { return Float::zero(precision); // atan2(0, +x) = 0 } } // 無限大の処理 if (x.isInfinity() && y.isInfinity()) { // 両方無限大の場合 Float pi_quarter = ldexp(Float::pi(wp), -2); Float result; if (x.isNegative()) { result = y.isNegative() ? -(mulScalarF(pi_quarter, uint64_t(3))) : (mulScalarF(pi_quarter, uint64_t(3))); } else { result = y.isNegative() ? -pi_quarter : pi_quarter; } result.setResultPrecision(precision); return result; } if (x.isInfinity()) { // x = +/-inf, y は有限 if (x.isNegative()) { Float result = y.isNegative() ? -Float::pi(wp) : Float::pi(wp); result.setResultPrecision(precision); return result; } else { return Float::zero(precision); } } if (y.isInfinity()) { // y = +/-inf, x は有限 Float pi_half = ldexp(Float::pi(wp), -1); Float result = y.isNegative() ? -pi_half : pi_half; result.setResultPrecision(precision); return result; } // 通常の場合: 象限判定 + atan(y/x) bool x_neg = x.isNegative(); bool y_neg = y.isNegative(); y.truncateToApprox(wp); x.truncateToApprox(wp); Float ratio = std::move(y) / std::move(x); ratio.truncateToApprox(wp); Float base = atan(std::move(ratio), wp); if (x_neg) { Float pi_wp = Float::pi(wp); if (y_neg) { base = base - pi_wp; // 第3象限 } else { base = base + pi_wp; // 第2象限 } } finalizeResult(base, eff, precision); return base; } //============================================================================= // 逆双曲線関数 //============================================================================= Float asinh(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return x; if (x.isZero()) return Float(0); return zivRound([](const Float& a, int e, int p) { int wp = p + 10; Float X = a; X.truncateToApprox(wp); Float x2 = X * X; x2.truncateToApprox(wp); Float inner = x2 + Float::one(wp); inner.truncateToApprox(wp); return log(X + sqrt(inner, wp), wp); }, x, x.effectiveBits(), precision); } Float asinh(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return x; if (x.isZero()) return Float(0); return zivRound([](const Float& a, int e, int p) { int wp = p + 10; Float X = a; X.truncateToApprox(wp); Float x2 = X * X; x2.truncateToApprox(wp); Float inner = x2 + Float::one(wp); inner.truncateToApprox(wp); return log(X + sqrt(inner, wp), wp); }, x, x.effectiveBits(), precision); } Float acosh(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float::nan(); return Float::positiveInfinity(); } Float one_val = Float::one(precision); if (x < one_val) return Float::nan(); if (x == one_val) return Float(0); return zivRound([](const Float& a, int e, int p) { int wp = p + 10; Float X = a; X.truncateToApprox(wp); Float x2 = X * X; x2.truncateToApprox(wp); Float inner = x2 - Float::one(wp); inner.truncateToApprox(wp); return log(X + sqrt(inner, wp), wp); }, x, x.effectiveBits(), precision); } Float acosh(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float::nan(); return Float::positiveInfinity(); } Float one_val = Float::one(precision); if (x < one_val) return Float::nan(); if (x == one_val) return Float(0); return zivRound([](const Float& a, int e, int p) { int wp = p + 10; Float X = a; X.truncateToApprox(wp); Float x2 = X * X; x2.truncateToApprox(wp); Float inner = x2 - Float::one(wp); inner.truncateToApprox(wp); return log(X + sqrt(inner, wp), wp); }, x, x.effectiveBits(), precision); } Float atanh(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); Float one_val = Float::one(precision); Float abs_x = abs(x); if (abs_x == one_val) { return x.isNegative() ? Float::negativeInfinity() : Float::positiveInfinity(); } if (abs_x > one_val) return Float::nan(); return zivRound([](const Float& a, int e, int p) { int wp = p + 10; Float X = a; X.truncateToApprox(wp); Float one_wp = Float::one(wp); Float num = one_wp + X; num.truncateToApprox(wp); Float den = one_wp - X; den.truncateToApprox(wp); Float ratio = num / den; ratio.truncateToApprox(wp); Float half(Int(1), -1, false); return half * log(ratio, wp); }, x, x.effectiveBits(), precision); } Float atanh(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); Float one_val = Float::one(precision); Float abs_x = abs(x); if (abs_x == one_val) { return x.isNegative() ? Float::negativeInfinity() : Float::positiveInfinity(); } if (abs_x > one_val) return Float::nan(); return zivRound([](const Float& a, int e, int p) { int wp = p + 10; Float X = a; X.truncateToApprox(wp); Float one_wp = Float::one(wp); Float num = one_wp + X; num.truncateToApprox(wp); Float den = one_wp - X; den.truncateToApprox(wp); Float ratio = num / den; ratio.truncateToApprox(wp); Float half(Int(1), -1, false); return half * log(ratio, wp); }, x, x.effectiveBits(), precision); } //============================================================================= // log1p / exp2 / exp10 / expm1 //============================================================================= static Float log2_dispatch(const Float& x, int eff_x, int precision) { int precision_bits = Float::precisionToBits(precision); if (precision_bits <= 64) return log2_small(x, precision); if (precision_bits <= 128) return log2_medium(x, precision); if (precision_bits <= 2000) return log2_newton(Float(x), eff_x, precision); int wp = precision + 10; return log(x, wp) / Float::log2(wp); } Float log2(const Float& x, int precision) { if (x.isNaN() || (x.isNegative() && !x.isZero())) return Float::nan(); if (x.isZero()) return Float::negativeInfinity(); if (x.isInfinity() && !x.isNegative()) return Float::positiveInfinity(); if (x == Float::one()) return Float::zero(); return zivRound(log2_dispatch, x, x.effectiveBits(), precision); } Float log2(Float&& x, int precision) { if (x.isNaN() || (x.isNegative() && !x.isZero())) return Float::nan(); if (x.isZero()) return Float::negativeInfinity(); if (x.isInfinity() && !x.isNegative()) return Float::positiveInfinity(); if (x == Float::one()) return Float::zero(); return zivRound(log2_dispatch, x, x.effectiveBits(), precision); } static Float log10_dispatch(const Float& x, int eff_x, int precision) { int precision_bits = Float::precisionToBits(precision); if (precision_bits <= 64) return log10_small(x, precision); if (precision_bits <= 128) return log10_medium(x, precision); if (precision_bits <= 2000) return log10_newton(Float(x), eff_x, precision); int wp = precision + 10; return log(x, wp) / Float::log10(wp); } Float log10(const Float& x, int precision) { if (x.isNaN() || (x.isNegative() && !x.isZero())) return Float::nan(); if (x.isZero()) return Float::negativeInfinity(); if (x.isInfinity() && !x.isNegative()) return Float::positiveInfinity(); if (x == Float::one()) return Float::zero(); return zivRound(log10_dispatch, x, x.effectiveBits(), precision); } Float log10(Float&& x, int precision) { if (x.isNaN() || (x.isNegative() && !x.isZero())) return Float::nan(); if (x.isZero()) return Float::negativeInfinity(); if (x.isInfinity() && !x.isNegative()) return Float::positiveInfinity(); if (x == Float::one()) return Float::zero(); return zivRound(log10_dispatch, x, x.effectiveBits(), precision); } Float log1p(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); Float neg_one(Int(1), 0, true); if (x == neg_one) return Float::negativeInfinity(); if (x < neg_one) return Float::nan(); if (x.isInfinity() && !x.isNegative()) return Float::positiveInfinity(); if (x.isInfinity() && x.isNegative()) return Float::nan(); return zivRound([](const Float& a, int e, int p) { int wp = p + 10; return log(Float::one(wp) + a, wp); }, x, x.effectiveBits(), precision); } Float log1p(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); Float neg_one(Int(1), 0, true); if (x == neg_one) return Float::negativeInfinity(); if (x < neg_one) return Float::nan(); if (x.isInfinity() && !x.isNegative()) return Float::positiveInfinity(); if (x.isInfinity() && x.isNegative()) return Float::nan(); return zivRound([](const Float& a, int e, int p) { int wp = p + 10; return log(Float::one(wp) + a, wp); }, x, x.effectiveBits(), precision); } Float exp2(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float(0); return Float::positiveInfinity(); } if (x.isZero()) return Float::one(precision); return zivRound([](const Float& a, int e, int p) { int wp = p + 10; return exp(a * Float::log2(wp), wp); }, x, x.effectiveBits(), precision); } Float exp2(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float(0); return Float::positiveInfinity(); } if (x.isZero()) return Float::one(precision); return zivRound([](const Float& a, int e, int p) { int wp = p + 10; return exp(a * Float::log2(wp), wp); }, x, x.effectiveBits(), precision); } Float exp10(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float(0); return Float::positiveInfinity(); } if (x.isZero()) return Float::one(precision); return zivRound([](const Float& a, int e, int p) { int wp = p + 10; return exp(a * Float::log10(wp), wp); }, x, x.effectiveBits(), precision); } Float exp10(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float(0); return Float::positiveInfinity(); } if (x.isZero()) return Float::one(precision); return zivRound([](const Float& a, int e, int p) { int wp = p + 10; return exp(a * Float::log10(wp), wp); }, x, x.effectiveBits(), precision); } Float expm1(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); if (x.isInfinity()) { if (x.isNegative()) return Float(-1); return Float::positiveInfinity(); } return zivRound([](const Float& a, int e, int p) { int wp = p + 10; return exp(a, wp) - Float::one(wp); }, x, x.effectiveBits(), precision); } Float expm1(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); if (x.isInfinity()) { if (x.isNegative()) return Float(-1); return Float::positiveInfinity(); } return zivRound([](const Float& a, int e, int p) { int wp = p + 10; return exp(a, wp) - Float::one(wp); }, x, x.effectiveBits(), precision); } //============================================================================= // exp2m1 / exp10m1 — 2^x - 1, 10^x - 1 (小 x で精度保証) //============================================================================= Float exp2m1(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); if (x.isInfinity()) { if (x.isNegative()) return Float(-1); return Float::positiveInfinity(); } // exp2m1(x) = expm1(x * ln2) // 小 x では x*ln2 も小さいので expm1 が精度保証 // 大 x では exp2(x) - 1 で相殺問題なし return zivRound([](const Float& a, int e, int p) { int wp = p + 10; double ad = a.toDouble(); if (std::fabs(ad) < 10.0) { // 小 x: expm1(x * ln2) で精度保証 Float arg = a * Float::log2(wp); arg.truncateToApprox(wp); return expm1(arg, wp); } // 大 x: exp2(x) - 1 (相殺なし) return exp2(a, wp) - Float::one(wp); }, x, x.effectiveBits(), precision); } Float exp2m1(Float&& x, int precision) { return exp2m1(static_cast(x), precision); } Float exp10m1(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); if (x.isInfinity()) { if (x.isNegative()) return Float(-1); return Float::positiveInfinity(); } // exp10m1(x) = expm1(x * ln10) return zivRound([](const Float& a, int e, int p) { int wp = p + 10; double ad = a.toDouble(); if (std::fabs(ad) < 10.0) { Float arg = a * Float::log10(wp); arg.truncateToApprox(wp); return expm1(arg, wp); } return exp10(a, wp) - Float::one(wp); }, x, x.effectiveBits(), precision); } Float exp10m1(Float&& x, int precision) { return exp10m1(static_cast(x), precision); } //============================================================================= // log2p1 / log10p1 / compound / minPrec //============================================================================= Float log2p1(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); Float neg_one(Int(1), 0, true); if (x == neg_one) return Float::negativeInfinity(); if (x < neg_one) return Float::nan(); if (x.isInfinity() && !x.isNegative()) return Float::positiveInfinity(); if (x == Float::one()) return Float::one(); // log2(1+1) = log2(2) = 1 return zivRound([](const Float& a, int e, int p) { int wp = p + 10; return log1p(a, wp) / Float::log2(wp); }, x, x.effectiveBits(), precision); } Float log2p1(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); Float neg_one(Int(1), 0, true); if (x == neg_one) return Float::negativeInfinity(); if (x < neg_one) return Float::nan(); if (x.isInfinity() && !x.isNegative()) return Float::positiveInfinity(); if (x == Float::one()) return Float::one(); // log2(1+1) = log2(2) = 1 return zivRound([](const Float& a, int e, int p) { int wp = p + 10; return log1p(a, wp) / Float::log2(wp); }, x, x.effectiveBits(), precision); } Float log10p1(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); Float neg_one(Int(1), 0, true); if (x == neg_one) return Float::negativeInfinity(); if (x < neg_one) return Float::nan(); if (x.isInfinity() && !x.isNegative()) return Float::positiveInfinity(); if (x == Float(9)) return Float::one(); // log10(1+9) = log10(10) = 1 return zivRound([](const Float& a, int e, int p) { int wp = p + 10; return log1p(a, wp) / Float::log10(wp); }, x, x.effectiveBits(), precision); } Float log10p1(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); Float neg_one(Int(1), 0, true); if (x == neg_one) return Float::negativeInfinity(); if (x < neg_one) return Float::nan(); if (x.isInfinity() && !x.isNegative()) return Float::positiveInfinity(); if (x == Float(9)) return Float::one(); // log10(1+9) = log10(10) = 1 return zivRound([](const Float& a, int e, int p) { int wp = p + 10; return log1p(a, wp) / Float::log10(wp); }, x, x.effectiveBits(), precision); } Float compound(const Float& x, int n, int precision) { if (x.isNaN()) return Float::nan(); Float neg_one(Int(1), 0, true); if (x < neg_one) return Float::nan(); if (n == 0) return Float::one(precision); if (x.isZero()) return Float::one(precision); if (x == neg_one) { if (n > 0) return Float(0); return Float::positiveInfinity(); // (1+(-1))^(negative) = 0^(negative) = +∞ } int wp = precision + 10; return pow(Float::one(wp) + x, n, precision); } Float compound(Float&& x, int n, int precision) { if (x.isNaN()) return Float::nan(); Float neg_one(Int(1), 0, true); if (x < neg_one) return Float::nan(); if (n == 0) return Float::one(precision); if (x.isZero()) return Float::one(precision); if (x == neg_one) { if (n > 0) return Float(0); return Float::positiveInfinity(); } int wp = precision + 10; return pow(Float::one(wp) + std::move(x), n, precision); } int Float::minPrec() const { if (isZero() || isNaN() || isInfinity()) return 0; int bl = static_cast(mantissa_.bitLength()); int tz = static_cast(mantissa_.countTrailingZeros()); return bl - tz; } //============================================================================= // fmod / remainder //============================================================================= Float fmod(const Float& x, const Float& y) { // fmod(x, y) = x - trunc(x/y) * y if (x.isNaN() || y.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); // fmod(±∞, y) = NaN if (y.isZero()) return Float::nan(); // fmod(x, 0) = NaN if (x.isZero()) return Float(0); // fmod(0, y) = 0 if (y.isInfinity()) return x; // fmod(x, ±∞) = x Float quotient = trunc(x / y); return x - quotient * y; } Float fmod(Float&& x, Float&& y) { if (x.isNaN() || y.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (y.isZero()) return Float::nan(); if (x.isZero()) return Float(0); if (y.isInfinity()) return std::move(x); Float quotient = trunc(x / y); return std::move(x) - quotient * std::move(y); } Float remainder(const Float& x, const Float& y) { // IEEE 754 remainder: x - roundEven(x/y) * y if (x.isNaN() || y.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (y.isZero()) return Float::nan(); if (x.isZero()) return Float(0); if (y.isInfinity()) return x; Float quotient = roundEven(x / y); return x - quotient * y; } Float remainder(Float&& x, Float&& y) { if (x.isNaN() || y.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (y.isZero()) return Float::nan(); if (x.isZero()) return Float(0); if (y.isInfinity()) return std::move(x); Float quotient = roundEven(x / y); return std::move(x) - quotient * std::move(y); } // remquo: remainder + 商の下位ビットを返す // 戻り値: {remainder, 商の符号付き下位3ビット} std::pair remquo(const Float& x, const Float& y) { if (x.isNaN() || y.isNaN()) return {Float::nan(), 0}; if (x.isInfinity()) return {Float::nan(), 0}; if (y.isZero()) return {Float::nan(), 0}; if (x.isZero()) return {Float(0), 0}; if (y.isInfinity()) return {x, 0}; Float q = roundEven(x / y); Float r = x - q * y; // 商の下位3ビットと符号を取得 Int qi = q.toInt(); bool q_neg = qi.isNegative(); if (q_neg) qi = -qi; // 下位3ビット: getBit(0..2) int quo = 0; if (qi.getBit(0)) quo |= 1; if (qi.getBit(1)) quo |= 2; if (qi.getBit(2)) quo |= 4; if (q_neg) quo = -quo; return {std::move(r), quo}; } std::pair remquo(Float&& x, Float&& y) { if (x.isNaN() || y.isNaN()) return {Float::nan(), 0}; if (x.isInfinity()) return {Float::nan(), 0}; if (y.isZero()) return {Float::nan(), 0}; if (x.isZero()) return {Float(0), 0}; if (y.isInfinity()) return {std::move(x), 0}; Float q = roundEven(x / y); Float r = std::move(x) - q * std::move(y); Int qi = q.toInt(); bool q_neg = qi.isNegative(); if (q_neg) qi = -qi; int quo = 0; if (qi.getBit(0)) quo |= 1; if (qi.getBit(1)) quo |= 2; if (qi.getBit(2)) quo |= 4; if (q_neg) quo = -quo; return {std::move(r), quo}; } //============================================================================= // hypot //============================================================================= Float hypot(const Float& x, const Float& y, int precision) { // hypot(x, y) = sqrt(x^2 + y^2) if (x.isNaN() || y.isNaN()) { // hypot(±∞, NaN) = +∞ (IEEE 754) if (x.isInfinity() || y.isInfinity()) return Float::positiveInfinity(); return Float::nan(); } if (x.isInfinity() || y.isInfinity()) return Float::positiveInfinity(); if (x.isZero() && y.isZero()) return Float(0); if (x.isZero()) return abs(y); if (y.isZero()) return abs(x); int eff = std::min(x.effectiveBits(), y.effectiveBits()); int wp = precision + 10; Float X = abs(x); X.truncateToApprox(wp); Float Y = abs(y); Y.truncateToApprox(wp); Float sum = X * X + Y * Y; sum.truncateToApprox(wp); Float result = sqrt(sum, wp); finalizeResult(result, eff, precision); return result; } Float hypot(Float&& x, Float&& y, int precision) { // hypot(x, y) = sqrt(x^2 + y^2) if (x.isNaN() || y.isNaN()) { // hypot(+/-inf, NaN) = +inf (IEEE 754) if (x.isInfinity() || y.isInfinity()) return Float::positiveInfinity(); return Float::nan(); } if (x.isInfinity() || y.isInfinity()) return Float::positiveInfinity(); if (x.isZero() && y.isZero()) return Float(0); if (x.isZero()) return abs(std::move(y)); if (y.isZero()) return abs(std::move(x)); int eff = std::min(x.effectiveBits(), y.effectiveBits()); int wp = precision + 10; Float X = abs(std::move(x)); X.truncateToApprox(wp); Float Y = abs(std::move(y)); Y.truncateToApprox(wp); Float sum = X * X + Y * Y; sum.truncateToApprox(wp); Float result = sqrt(std::move(sum), wp); finalizeResult(result, eff, precision); return result; } //============================================================================= // cbrt / nthRoot / recSqrt //============================================================================= // Newton 精度倍増 n 乗根 (cbrt/nthRoot 共通コア) // x_{k+1} = ((n-1)*x_k + a / x_k^{n-1}) / n // 二次収束: 各反復で有効桁が倍増 static Float nthRoot_newton(const Float& a, int n, int eff_x, int precision) { int wp = precision + 20; int precision_bits = Float::precisionToBits(wp); // 初期近似: double double a_d = a.toDouble(); double r0 = std::pow(a_d, 1.0 / n); if (!std::isfinite(r0) || r0 <= 0.0) { // オーバーフロー/アンダーフロー時: 指数を使って近似 int64_t e = a.exponent(); int64_t mant_bits = static_cast(a.mantissa().bitLength()); int64_t total_exp = e + mant_bits; int64_t root_exp = total_exp / n; double m_d = std::ldexp(a_d, static_cast(-(total_exp - 53))); if (!std::isfinite(m_d) || m_d <= 0.0) m_d = 1.0; r0 = std::pow(m_d, 1.0 / n) * std::pow(2.0, static_cast(root_exp) - 53.0 / n); if (!std::isfinite(r0) || r0 <= 0.0) r0 = 1.0; } Float r(r0); // 精度倍増スケジュール: 53 → 106 → 212 → ... → precision_bits std::vector precisions; int p = precision_bits; while (p > 53) { precisions.push_back(p); p = (p + 1) / 2; } precisions.push_back(53); std::reverse(precisions.begin(), precisions.end()); Float n_float(n); Float nm1_float(n - 1); for (int target_p : precisions) { // bits → digits: inverse of precisionToBits int step_digits = static_cast((target_p + 20) / 3.32192809488736) + 2; r.truncateToApprox(step_digits); // x^{n-1} を計算 Float r_pow_nm1 = pow(r, n - 1, step_digits); r_pow_nm1.truncateToApprox(step_digits); // a / x^{n-1} Float a_trunc = a; a_trunc.truncateToApprox(step_digits); Float quot = a_trunc / r_pow_nm1; quot.truncateToApprox(step_digits); // ((n-1)*r + quot) / n r = (nm1_float * r + quot) / n_float; } finalizeResult(r, eff_x, precision); return r; } Float cbrt(const Float& x, int precision) { // 特殊値 if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return x; if (x.isZero()) return Float(0); int eff_x = x.effectiveBits(); bool negative = x.isNegative(); Float abs_x = abs(x); Float result = nthRoot_newton(abs_x, 3, eff_x, precision); if (negative) result = -result; return result; } Float cbrt(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return std::move(x); if (x.isZero()) return Float(0); int eff_x = x.effectiveBits(); bool negative = x.isNegative(); Float abs_x = abs(std::move(x)); Float result = nthRoot_newton(abs_x, 3, eff_x, precision); if (negative) result = -result; return result; } Float nthRoot(const Float& x, int n, int precision) { if (n <= 0) return Float::nan(); if (n == 1) return x; if (n == 2) return sqrt(x, precision); // 特殊値 if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) { return (n % 2 == 1) ? Float::negativeInfinity() : Float::nan(); } return Float::positiveInfinity(); } if (x.isZero()) return Float(0); // 負の数: 奇数根のみ有効 if (x.isNegative()) { if (n % 2 == 0) return Float::nan(); Float abs_result = nthRoot(abs(x), n, precision); return -abs_result; } // Newton 精度倍増 return nthRoot_newton(x, n, x.effectiveBits(), precision); } Float nthRoot(Float&& x, int n, int precision) { if (n <= 0) return Float::nan(); if (n == 1) return std::move(x); if (n == 2) return sqrt(std::move(x), precision); // 特殊値 if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) { return (n % 2 == 1) ? Float::negativeInfinity() : Float::nan(); } return Float::positiveInfinity(); } if (x.isZero()) return Float(0); // 負の数: 奇数根のみ有効 if (x.isNegative()) { if (n % 2 == 0) return Float::nan(); Float abs_result = nthRoot(abs(x), n, precision); return -abs_result; } // Newton 精度倍増 return nthRoot_newton(x, n, x.effectiveBits(), precision); } Float recSqrt(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float::positiveInfinity(); if (x.isNegative()) return Float::nan(); if (x.isInfinity()) return Float(0); int eff_x = x.effectiveBits(); int wp = precision + 10; Float X = x; X.truncateToApprox(wp); Float result = Float::one(wp) / sqrt(X, wp); finalizeResult(result, eff_x, precision); return result; } Float recSqrt(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float::positiveInfinity(); if (x.isNegative()) return Float::nan(); if (x.isInfinity()) return Float(0); int eff_x = x.effectiveBits(); int wp = precision + 10; x.truncateToApprox(wp); Float result = Float::one(wp) / sqrt(std::move(x), wp); finalizeResult(result, eff_x, precision); return result; } //============================================================================= // fma / fms //============================================================================= Float fma(const Float& a, const Float& b, const Float& c, int precision) { if (a.isNaN() || b.isNaN() || c.isNaN()) return Float::nan(); int eff = std::min({a.effectiveBits(), b.effectiveBits(), c.effectiveBits()}); int wp = precision + 10; Float A = a; A.truncateToApprox(wp); Float B = b; B.truncateToApprox(wp); Float C = c; C.truncateToApprox(wp); Float product = A * B; product.truncateToApprox(wp); Float result = product + C; finalizeResult(result, eff, precision); return result; } Float fma(Float&& a, Float&& b, Float&& c, int precision) { if (a.isNaN() || b.isNaN() || c.isNaN()) return Float::nan(); int eff = std::min({a.effectiveBits(), b.effectiveBits(), c.effectiveBits()}); int wp = precision + 10; a.truncateToApprox(wp); b.truncateToApprox(wp); c.truncateToApprox(wp); Float product = std::move(a) * std::move(b); product.truncateToApprox(wp); Float result = std::move(product) + std::move(c); finalizeResult(result, eff, precision); return result; } Float fms(const Float& a, const Float& b, const Float& c, int precision) { if (a.isNaN() || b.isNaN() || c.isNaN()) return Float::nan(); int eff = std::min({a.effectiveBits(), b.effectiveBits(), c.effectiveBits()}); int wp = precision + 10; Float A = a; A.truncateToApprox(wp); Float B = b; B.truncateToApprox(wp); Float C = c; C.truncateToApprox(wp); Float product = A * B; product.truncateToApprox(wp); Float result = product - C; finalizeResult(result, eff, precision); return result; } Float fms(Float&& a, Float&& b, Float&& c, int precision) { if (a.isNaN() || b.isNaN() || c.isNaN()) return Float::nan(); int eff = std::min({a.effectiveBits(), b.effectiveBits(), c.effectiveBits()}); int wp = precision + 10; a.truncateToApprox(wp); b.truncateToApprox(wp); c.truncateToApprox(wp); Float product = std::move(a) * std::move(b); product.truncateToApprox(wp); Float result = std::move(product) - std::move(c); finalizeResult(result, eff, precision); return result; } //============================================================================= // 二重積和/差 (fmma / fmms) — MPFR mpfr_fmma / mpfr_fmms 相当 //============================================================================= Float fmma(const Float& a, const Float& b, const Float& c, const Float& d, int precision) { if (a.isNaN() || b.isNaN() || c.isNaN() || d.isNaN()) return Float::nan(); int eff = std::min({a.effectiveBits(), b.effectiveBits(), c.effectiveBits(), d.effectiveBits()}); int wp = precision + 10; // 拡張精度で積を計算し、加算後に丸める (中間丸め誤差を最小化) Float ab = a * b; ab.truncateToApprox(wp); Float cd = c * d; cd.truncateToApprox(wp); Float result = ab + cd; finalizeResult(result, eff, precision); return result; } Float fmms(const Float& a, const Float& b, const Float& c, const Float& d, int precision) { if (a.isNaN() || b.isNaN() || c.isNaN() || d.isNaN()) return Float::nan(); int eff = std::min({a.effectiveBits(), b.effectiveBits(), c.effectiveBits(), d.effectiveBits()}); int wp = precision + 10; Float ab = a * b; ab.truncateToApprox(wp); Float cd = c * d; cd.truncateToApprox(wp); Float result = ab - cd; finalizeResult(result, eff, precision); return result; } //============================================================================= // sinCos //============================================================================= // sinCos: 高精度では cosDoubling + sqrt(1-cos²) で Taylor 1本に削減 static void sinCos_core(Float x, int eff_x, Float& sin_result, Float& cos_result, int precision) { int compute_prec = effectiveComputePrecision(eff_x, precision); int working_precision = compute_prec + 10; bool input_negative = x.isNegative(); x = abs(x); auto reduced = reduceToFirstQuadrant(std::move(x), working_precision); int wp_bits = Float::precisionToBits(working_precision); Float s, c; if (reduced.x.isZero()) { s = Float::zero(); c = Float::one(working_precision); } else if (wp_bits >= BITBURST_THRESHOLD) { // Bit-burst: sin/cos 同時計算、sqrt 不要 auto [sv, cv] = sincos_bitburst(reduced.x, working_precision); s = std::move(sv); c = std::move(cv); } else if (wp_bits >= 1000) { // 高精度: cosDoubling + sqrt(1 - cos²) で Taylor 1 本分を節約 int64_t x_msb = reduced.x.exponent() + static_cast(reduced.x.mantissa().bitLength()); int extra_guard = (x_msb < 0) ? static_cast(-2 * x_msb) : 0; int wp_cos = working_precision + Float::bitsToPrecision(extra_guard); c = cosDoubling(reduced.x, wp_cos); Float one_minus_c2 = Float::one(wp_cos) - c * c; s = sqrt(std::move(one_minus_c2), working_precision); c.truncateToApprox(working_precision); } else { // 低精度: sinCosDoubling (オーバーヘッドが支配的) auto [sv, cv] = sinCosDoubling(reduced.x, working_precision); s = std::move(sv); c = std::move(cv); } if (reduced.sin_negative) s = -s; if (reduced.cos_negative) c = -c; if (input_negative) s = -s; // sin(-x) = -sin(x), cos(-x) = cos(x) finalizeResult(s, eff_x, precision); finalizeResult(c, eff_x, precision); sin_result = std::move(s); cos_result = std::move(c); } void sinCos(const Float& x, Float& sin_result, Float& cos_result, int precision) { if (x.isNaN()) { sin_result = Float::nan(); cos_result = Float::nan(); return; } if (x.isInfinity()) { sin_result = Float::nan(); cos_result = Float::nan(); return; } if (x.isZero()) { sin_result = Float::zero(); cos_result = Float::one(precision); return; } sinCos_core(Float(x), x.effectiveBits(), sin_result, cos_result, precision); } void sinCos(Float&& x, Float& sin_result, Float& cos_result, int precision) { if (x.isNaN()) { sin_result = Float::nan(); cos_result = Float::nan(); return; } if (x.isInfinity()) { sin_result = Float::nan(); cos_result = Float::nan(); return; } if (x.isZero()) { sin_result = Float::zero(); cos_result = Float::one(precision); return; } int eff = x.effectiveBits(); sinCos_core(std::move(x), eff, sin_result, cos_result, precision); } //============================================================================= // sinhCosh — sinh と cosh の同時計算 //============================================================================= void sinhCosh(const Float& x, Float& sinh_result, Float& cosh_result, int precision) { sinh_result = sinh(x, precision); cosh_result = cosh(x, precision); } void sinhCosh(Float&& x, Float& sinh_result, Float& cosh_result, int precision) { sinh_result = sinh(x, precision); cosh_result = cosh(std::move(x), precision); } //============================================================================= // sec / csc / cot — 逆数三角関数 //============================================================================= Float sec(const Float& x, int precision) { // sec(x) = 1 / cos(x) if (x.isNaN()) return Float::nan(); int eff_x = x.effectiveBits(); Float c = cos(x, precision); if (c.isZero()) return Float::nan(); // cos(x)=0 → sec 未定義 Float result = Float(1) / c; finalizeResult(result, eff_x, precision); return result; } Float sec(Float&& x, int precision) { // sec(x) = 1 / cos(x) if (x.isNaN()) return Float::nan(); int eff_x = x.effectiveBits(); Float c = cos(std::move(x), precision); if (c.isZero()) return Float::nan(); // cos(x)=0 → sec 未定義 Float result = Float(1) / c; finalizeResult(result, eff_x, precision); return result; } Float csc(const Float& x, int precision) { // csc(x) = 1 / sin(x) if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float::nan(); // sin(0)=0 → csc 未定義 int eff_x = x.effectiveBits(); Float s = sin(x, precision); if (s.isZero()) return Float::nan(); Float result = Float(1) / s; finalizeResult(result, eff_x, precision); return result; } Float csc(Float&& x, int precision) { // csc(x) = 1 / sin(x) if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float::nan(); // sin(0)=0 → csc 未定義 int eff_x = x.effectiveBits(); Float s = sin(std::move(x), precision); if (s.isZero()) return Float::nan(); Float result = Float(1) / s; finalizeResult(result, eff_x, precision); return result; } Float cot(const Float& x, int precision) { // cot(x) = cos(x) / sin(x) if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float::nan(); // sin(0)=0 → cot 未定義 int eff_x = x.effectiveBits(); Float s = sin(x, precision); if (s.isZero()) return Float::nan(); Float result = cos(x, precision) / s; finalizeResult(result, eff_x, precision); return result; } Float cot(Float&& x, int precision) { // cot(x) = cos(x) / sin(x) if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float::nan(); // sin(0)=0 → cot 未定義 int eff_x = x.effectiveBits(); Float s = sin(x, precision); if (s.isZero()) return Float::nan(); Float result = cos(std::move(x), precision) / s; finalizeResult(result, eff_x, precision); return result; } //============================================================================= // sech / csch / coth — 逆数双曲線関数 //============================================================================= Float sech(const Float& x, int precision) { // sech(x) = 1 / cosh(x) if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float(0); // sech(±∞) = 0 int eff_x = x.effectiveBits(); Float c = cosh(x, precision); Float result = Float(1) / c; // cosh(x) >= 1 なので除算は常に安全 finalizeResult(result, eff_x, precision); return result; } Float sech(Float&& x, int precision) { // sech(x) = 1 / cosh(x) if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float(0); // sech(±∞) = 0 int eff_x = x.effectiveBits(); Float c = cosh(std::move(x), precision); Float result = Float(1) / c; // cosh(x) >= 1 なので除算は常に安全 finalizeResult(result, eff_x, precision); return result; } Float csch(const Float& x, int precision) { // csch(x) = 1 / sinh(x) if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float::nan(); // sinh(0)=0 → csch 未定義 if (x.isInfinity()) return Float(0); // csch(±∞) = 0 int eff_x = x.effectiveBits(); Float s = sinh(x, precision); Float result = Float(1) / s; finalizeResult(result, eff_x, precision); return result; } Float csch(Float&& x, int precision) { // csch(x) = 1 / sinh(x) if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float::nan(); // sinh(0)=0 → csch 未定義 if (x.isInfinity()) return Float(0); // csch(±∞) = 0 int eff_x = x.effectiveBits(); Float s = sinh(std::move(x), precision); Float result = Float(1) / s; finalizeResult(result, eff_x, precision); return result; } Float coth(const Float& x, int precision) { // coth(x) = cosh(x) / sinh(x) if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float::nan(); // sinh(0)=0 → coth 未定義 if (x.isInfinity()) { // coth(+∞) = 1, coth(-∞) = -1 return x.isNegative() ? Float(-1) : Float(1); } int eff_x = x.effectiveBits(); Float result = cosh(x, precision) / sinh(x, precision); finalizeResult(result, eff_x, precision); return result; } Float coth(Float&& x, int precision) { // coth(x) = cosh(x) / sinh(x) if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float::nan(); // sinh(0)=0 → coth 未定義 if (x.isInfinity()) { // coth(+∞) = 1, coth(-∞) = -1 return x.isNegative() ? Float(-1) : Float(1); } int eff_x = x.effectiveBits(); Float s = sinh(x, precision); Float result = cosh(std::move(x), precision) / s; finalizeResult(result, eff_x, precision); return result; } //============================================================================= // factorial — 階乗 (整数引数) //============================================================================= Float factorial(int n, int precision) { if (n < 0) return Float::nan(); if (n <= 1) return Float(1); // 直接積で計算 (小〜中サイズ向け) Float result(1); for (int i = 2; i <= n; i++) { result = result * Float(i); } result.setResultPrecision(precision); return result; } //============================================================================= // sinPi / cosPi / tanPi — πベース三角関数 (精度保証版) //============================================================================= // sinPi(x) = sin(π·x), cosPi(x) = cos(π·x), tanPi(x) = tan(π·x) // // 精度保証の仕組み: // 1. x の符号を除去 (sin は奇関数, cos は偶関数) // 2. x の整数部 n を除去し、小数部 r = x - n ∈ [0, 1) を取得 // → n mod 2 で sin/cos の符号反転を決定 (sin(π(r+n)) = (-1)^n sin(πr)) // 3. r > 0.5 なら r = 1 - r として [0, 0.5] に帰着 // → sin の場合は変換不要 (sin(π(1-r)) = sin(πr)) // → cos の場合は符号反転 (cos(π(1-r)) = -cos(πr)) // 4. r = 0 → sin=0, cos=1 (正確); r = 0.5 → sin=1, cos=0 (正確) // 5. 一般ケース: r ∈ (0, 0.5) → πr ∈ (0, π/2) で sin/cos を計算 // // この方法により: // - 大きな x でも引数還元は整数演算のみ → 還元損失なし // - π の乗算は |r| ≤ 0.5 の範囲に限定 → 丸め誤差最小 // - 整数・半整数引数で正確な値を保証 // sinPi/cosPi 共通の引数還元結果 struct PiReduced { Float r; // [0, 0.5] に還元された小数部 bool negate_sin; // sin の結果を符号反転するか bool negate_cos; // cos の結果を符号反転するか bool r_is_zero; // r == 0 (整数引数) bool r_is_half; // r == 0.5 (半整数引数) }; // x を πベース三角関数用に還元する (x は非負の有限値, 非整数) static PiReduced reducePiArg(Float x) { // x = n + r, n = floor(x), r ∈ [0, 1) Float n_f = floor(x); Float r = x - n_f; // n mod 2 を取得 (符号決定用) // n_f が巨大でも下位1ビットだけ必要 bool n_odd = false; if (!n_f.isZero()) { Int n_int = n_f.toInt(); n_odd = n_int.getBit(0); } // sin(π(r + n)) = (-1)^n · sin(πr) // cos(π(r + n)) = (-1)^n · cos(πr) bool negate_sin = n_odd; bool negate_cos = n_odd; // r を [0, 0.5] に帰着 // 半整数判定: r == 0.5 を正確にチェック Float half(Int(1), -1, false); // 0.5 bool r_is_half = (r == half); bool r_is_zero = r.isZero(); if (!r_is_half && !r_is_zero && r > half) { // r ∈ (0.5, 1): sin(πr) = sin(π(1-r)), cos(πr) = -cos(π(1-r)) r = Float(1) - r; negate_cos = !negate_cos; } return { std::move(r), negate_sin, negate_cos, r_is_zero, r_is_half }; } // sinPi の内部実装 (x は値で受け取り) static Float sinPi_impl(Float x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (x.isZero()) return Float(0); int eff_x = x.effectiveBits(); // sin(-πx) = -sin(πx) bool input_neg = x.isNegative(); if (input_neg) x = -std::move(x); // 整数なら sin(nπ) = 0 (正確) if (x.isInteger()) return Float(0); auto rd = reducePiArg(std::move(x)); // r = 0 → sin(πr) = 0 if (rd.r_is_zero) return Float(0); // r = 0.5 → sin(πr) = sin(π/2) = 1 if (rd.r_is_half) { Float result = Float(1); if (rd.negate_sin != input_neg) result = -result; return result; } // 一般ケース: πr ∈ (0, π/2) → sin(πr) を計算 int wp = precision + 10; Float pi_val = Float::pi(wp); Float arg = pi_val * rd.r; Float result = sin(std::move(arg), precision); if (rd.negate_sin != input_neg) result = -result; finalizeResult(result, eff_x, precision); return result; } // cosPi の内部実装 (x は値で受け取り) static Float cosPi_impl(Float x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); int eff_x = x.effectiveBits(); // cos(-πx) = cos(πx) → 符号を除去 if (x.isNegative()) x = -std::move(x); if (x.isZero()) return Float(1); // 整数なら cos(nπ) = (-1)^n if (x.isInteger()) { Int n = x.toInt(); if (n.getBit(0)) return Float(-1); return Float(1); } auto rd = reducePiArg(std::move(x)); // r = 0 → cos(πr) = 1 if (rd.r_is_zero) { return rd.negate_cos ? Float(-1) : Float(1); } // r = 0.5 → cos(πr) = cos(π/2) = 0 if (rd.r_is_half) return Float(0); // 一般ケース: πr ∈ (0, π/2) → cos(πr) を計算 int wp = precision + 10; Float pi_val = Float::pi(wp); Float arg = pi_val * rd.r; Float result = cos(std::move(arg), precision); if (rd.negate_cos) result = -result; finalizeResult(result, eff_x, precision); return result; } Float sinPi(const Float& x, int precision) { return sinPi_impl(Float(x), precision); } Float sinPi(Float&& x, int precision) { return sinPi_impl(std::move(x), precision); } Float cosPi(const Float& x, int precision) { return cosPi_impl(Float(x), precision); } Float cosPi(Float&& x, int precision) { return cosPi_impl(std::move(x), precision); } Float tanPi(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (x.isInteger()) return Float(0); // tanPi(x) = sinPi(x) / cosPi(x) int wp = precision + 10; Float s = sinPi(x, wp); Float c = cosPi(x, wp); if (c.isZero()) return Float::positiveInfinity(); // 半整数 → ±∞ Float result = s / c; finalizeResult(result, x.effectiveBits(), precision); return result; } Float tanPi(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (x.isInteger()) return Float(0); int eff_x = x.effectiveBits(); int wp = precision + 10; Float s = sinPi(Float(x), wp); Float c = cosPi(std::move(x), wp); if (c.isZero()) return Float::positiveInfinity(); Float result = s / c; finalizeResult(result, eff_x, precision); return result; } //============================================================================= // sinu / cosu / tanu — 任意角度単位三角関数 (IEEE 754-2019) // // sinu(x, u) = sin(2πx/u) 全回転 = u 単位 // 例: u=360 → 度, u=400 → グラジアン // 内部では sinPi(2x/u) に帰着させて精度損失を最小化する。 //============================================================================= // sinu の内部実装 (x は値で受け取り) static Float sinu_impl(Float x, int u, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (u <= 0) return Float::nan(); if (x.isZero()) return Float(0); // sinu(x, u) = sinPi(2x/u) // 先に x mod u で還元してから 2/u を掛ける (大きな x での精度損失回避) Float u_f(u); x = fmod(std::move(x), u_f); // fmod の結果は [-u, u] 範囲 // 特殊値の正確な処理: x_red が u/4, u/2, 3u/4 の倍数かチェック // sinPi に任せるため、2*x_red/u を計算 // 2*x/u: x は最大 u 程度なので精度損失は小さい int wp = precision + 10; Float arg = ldexp(x, 1) / u_f; return sinPi(std::move(arg), precision); } Float sinu(const Float& x, int u, int precision) { return sinu_impl(Float(x), u, precision); } Float sinu(Float&& x, int u, int precision) { return sinu_impl(std::move(x), u, precision); } // cosu の内部実装 static Float cosu_impl(Float x, int u, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (u <= 0) return Float::nan(); if (x.isZero()) return Float(1); Float u_f(u); x = fmod(std::move(x), u_f); Float arg = ldexp(x, 1) / u_f; return cosPi(std::move(arg), precision); } Float cosu(const Float& x, int u, int precision) { return cosu_impl(Float(x), u, precision); } Float cosu(Float&& x, int u, int precision) { return cosu_impl(std::move(x), u, precision); } // tanu の内部実装 static Float tanu_impl(Float x, int u, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::nan(); if (u <= 0) return Float::nan(); if (x.isZero()) return Float(0); Float u_f(u); x = fmod(std::move(x), u_f); Float arg = ldexp(x, 1) / u_f; return tanPi(std::move(arg), precision); } Float tanu(const Float& x, int u, int precision) { return tanu_impl(Float(x), u, precision); } Float tanu(Float&& x, int u, int precision) { return tanu_impl(std::move(x), u, precision); } //============================================================================= // asinPi / acosPi / atanPi / atan2Pi — 逆三角関数 (π 単位) //============================================================================= Float asinPi(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); // 特殊値: asinPi(±1) = ±0.5 (正確) if (!x.isZero()) { double xd = x.toDouble(); if (xd == 1.0) return Float(1, -1, false); // 0.5 if (xd == -1.0) return Float(1, -1, true); // -0.5 } if (x.isZero()) return Float::zero(); int wp = precision + 10; Float result = asin(x, wp) / Float::pi(wp); finalizeResult(result, x.effectiveBits(), precision); return result; } Float asinPi(Float&& x, int precision) { return asinPi(static_cast(x), precision); } Float acosPi(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); // 特殊値: acosPi(1)=0, acosPi(0)=0.5, acosPi(-1)=1 (正確) if (!x.isZero()) { double xd = x.toDouble(); if (xd == 1.0) return Float::zero(); if (xd == -1.0) return Float(1); } if (x.isZero()) return Float(1, -1, false); // 0.5 int wp = precision + 10; Float result = acos(x, wp) / Float::pi(wp); finalizeResult(result, x.effectiveBits(), precision); return result; } Float acosPi(Float&& x, int precision) { return acosPi(static_cast(x), precision); } Float atanPi(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { // atanPi(±∞) = ±0.5 return x.isNegative() ? Float(1, -1, true) : Float(1, -1, false); } if (x.isZero()) return Float::zero(); // 特殊値: atanPi(±1) = ±0.25 { double xd = x.toDouble(); if (xd == 1.0) return Float(1, -2, false); // 0.25 if (xd == -1.0) return Float(1, -2, true); // -0.25 } int wp = precision + 10; Float result = atan(x, wp) / Float::pi(wp); finalizeResult(result, x.effectiveBits(), precision); return result; } Float atanPi(Float&& x, int precision) { return atanPi(static_cast(x), precision); } Float atan2Pi(const Float& y, const Float& x, int precision) { if (y.isNaN() || x.isNaN()) return Float::nan(); // 特殊値 if (y.isZero() && !x.isZero()) { if (x.isNegative()) return Float(1); // atan2Pi(0, -x) = 1 return Float::zero(); // atan2Pi(0, +x) = 0 } if (!y.isZero() && x.isZero()) { // atan2Pi(±y, 0) = ±0.5 return y.isNegative() ? Float(1, -1, true) : Float(1, -1, false); } int eff = std::min(y.effectiveBits(), x.effectiveBits()); int wp = precision + 10; Float result = atan2(y, x, wp) / Float::pi(wp); finalizeResult(result, eff, precision); return result; } Float atan2Pi(Float&& y, Float&& x, int precision) { return atan2Pi(static_cast(y), static_cast(x), precision); } //============================================================================= // nextAbove / nextBelow — 次/前の表現可能な値 //============================================================================= Float nextAbove(const Float& x) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) { // nextAbove(-∞) = most negative finite // 実用的に表現不能なため、そのまま返す return x; } return x; // nextAbove(+∞) = +∞ } if (x.isZero()) { // nextAbove(0) = smallest positive: mantissa=1, exponent=-precision int prec = x.precision() > 0 ? x.precision() : 53; return Float(Int(1), -static_cast(prec), false); } // 正の数: mantissa を 1 増加 // 負の数: mantissa を 1 減少 (絶対値が小さくなる方向) if (!x.isNegative()) { Float result(x.mantissa() + 1, x.exponent(), false); result.setResultPrecision(x.precision()); return result; } else { Int m = x.mantissa(); if (m == Int(1)) { // -1 * 2^e → 0 方向 (特殊処理不要: mantissa-1=0 → ゼロ) return Float(0); } Float result(m - 1, x.exponent(), true); result.setResultPrecision(x.precision()); return result; } } Float nextAbove(Float&& x) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return std::move(x); if (x.isZero()) { int prec = x.precision() > 0 ? x.precision() : 53; return Float(Int(1), -static_cast(prec), false); } int prec = x.precision(); int64_t exp = x.exponent(); if (!x.isNegative()) { Float result(x.mantissa() + 1, exp, false); result.setResultPrecision(prec); return result; } else { Int m = x.mantissa(); if (m == Int(1)) return Float(0); Float result(m - 1, exp, true); result.setResultPrecision(prec); return result; } } Float nextBelow(const Float& x) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (!x.isNegative()) { return x; // nextBelow(+∞) = +∞ (表現限界) } return x; // nextBelow(-∞) = -∞ } if (x.isZero()) { // nextBelow(0) = smallest negative int prec = x.precision() > 0 ? x.precision() : 53; return Float(Int(1), -static_cast(prec), true); } // nextBelow = -nextAbove(-x) と等価 if (x.isNegative()) { // 負の数: 絶対値を増加 Float result(x.mantissa() + 1, x.exponent(), true); result.setResultPrecision(x.precision()); return result; } else { Int m = x.mantissa(); if (m == Int(1)) { return Float(0); } Float result(m - 1, x.exponent(), false); result.setResultPrecision(x.precision()); return result; } } Float nextBelow(Float&& x) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return std::move(x); if (x.isZero()) { int prec = x.precision() > 0 ? x.precision() : 53; return Float(Int(1), -static_cast(prec), true); } int prec = x.precision(); int64_t exp = x.exponent(); if (x.isNegative()) { Float result(x.mantissa() + 1, exp, true); result.setResultPrecision(prec); return result; } else { Int m = x.mantissa(); if (m == Int(1)) return Float(0); Float result(m - 1, exp, false); result.setResultPrecision(prec); return result; } } //============================================================================= // AGM — 算術幾何平均 (Arithmetic-Geometric Mean) //============================================================================= // agm(a, b) = lim_{n→∞} a_n = lim_{n→∞} b_n // a_{n+1} = (a_n + b_n) / 2 // b_{n+1} = √(a_n · b_n) // 二次収束: 有効桁数が反復ごとに倍増 static Float agm_core(Float a_val, Float b_val, int eff, int precision) { int wp = precision + 10; int wp_bits = static_cast(std::ceil(wp * 3.32192809488736)); a_val.truncateToApprox(wp); b_val.truncateToApprox(wp); // 反復回数: 初期比率の「暖機」+ 二次収束の 2 フェーズ。 // AGM(1, ε) で ε = 2^{-N} の場合、暖機 ≈ log2(N)、収束 ≈ log2(p)。 // 安全に 2·log2(wp_bits) + 20 で十分。 int max_iter = static_cast(std::log2(wp_bits)) * 2 + 20; for (int i = 0; i < max_iter; i++) { Float a_new = ldexp(a_val + b_val, -1); a_new.setResultPrecision(wp); Float b_new = sqrt(a_val * b_val, wp); // 値を更新してから収束判定 (break 前に最新値を確保) a_val = std::move(a_new); b_val = std::move(b_new); // 収束判定: |a - b| が十分小さい Float diff = a_val - b_val; if (diff.isZero()) break; int64_t diff_exp = diff.exponent() + static_cast(diff.mantissa().bitLength()); int64_t val_exp = a_val.exponent() + static_cast(a_val.mantissa().bitLength()); if (val_exp - diff_exp > wp_bits) break; } finalizeResult(a_val, eff, precision); return a_val; } Float agm(const Float& a, const Float& b, int precision) { if (a.isNaN() || b.isNaN()) return Float::nan(); if (a.isZero() || b.isZero()) return Float(0); // agm(0, x) = 0 if (a.isInfinity() || b.isInfinity()) return Float::nan(); // 両方正でなければならない if (a.isNegative() || b.isNegative()) return Float::nan(); int eff = std::min(a.effectiveBits(), b.effectiveBits()); return agm_core(Float(a), Float(b), eff, precision); } Float agm(Float&& a, Float&& b, int precision) { if (a.isNaN() || b.isNaN()) return Float::nan(); if (a.isZero() || b.isZero()) return Float(0); // agm(0, x) = 0 if (a.isInfinity() || b.isInfinity()) return Float::nan(); // 両方正でなければならない if (a.isNegative() || b.isNegative()) return Float::nan(); int eff = std::min(a.effectiveBits(), b.effectiveBits()); return agm_core(std::move(a), std::move(b), eff, precision); } //============================================================================= // sum — 高精度総和 //============================================================================= Float sum(std::span values, int precision) { if (values.empty()) return Float(0); // 単純な逐次加算 (多倍長なので桁落ちの心配は少ない) Float result = values[0]; for (size_t i = 1; i < values.size(); i++) { result = result + values[i]; } result.setResultPrecision(precision); return result; } //============================================================================= // dot — 内積 //============================================================================= Float dot(std::span a, std::span b, int precision) { size_t n = std::min(a.size(), b.size()); if (n == 0) return Float(0); Float result = a[0] * b[0]; for (size_t i = 1; i < n; i++) { result = result + a[i] * b[i]; } result.setResultPrecision(precision); return result; } //============================================================================= // erf / erfc — 誤差関数 //============================================================================= // erf(x) = (2/√π) · Σ_{n=0}^{∞} (-1)^n · x^{2n+1} / (n! · (2n+1)) // erfc(x) = 1 - erf(x) static Float erf_core(Float x, int eff_x, int precision) { int wp = precision + 20; // erf(x) = (2/√π) · Σ_{n=0}^{∞} (-1)^n · x^{2n+1} / (n! · (2n+1)) Float x2 = x * x; x2.truncateToApprox(wp); Float neg_x2 = -x2; Float term = x; // 初項: x term.truncateToApprox(wp); Float sum = term; sum.truncateToApprox(wp); Float contribution(0); for (int n = 1; n < 4 * wp; n++) { // term_{n} = term_{n-1} · (-x²) / n FloatOps::mul(term, neg_x2, term); FloatOps::div(term, Float(n), term); FloatOps::div(term, Float(2 * n + 1), contribution); FloatOps::add(sum, contribution, sum); // 収束判定 if (contribution.isZero()) break; int64_t c_bits = contribution.exponent() + static_cast(contribution.mantissa().bitLength()); int64_t s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - c_bits > wp) break; } // (2/√π) · sum Float sqrtpi = sqrt(Float::pi(wp), wp); Float result = ldexp(sum, 1) / sqrtpi; finalizeResult(result, eff_x, precision); return result; } Float erf(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); if (x.isInfinity()) { return x.isNegative() ? Float(-1) : Float(1); } return erf_core(Float(x), x.effectiveBits(), precision); } Float erf(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); if (x.isInfinity()) { return x.isNegative() ? Float(-1) : Float(1); } int eff = x.effectiveBits(); return erf_core(std::move(x), eff, precision); } Float erfc(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(1); if (x.isInfinity()) { return x.isNegative() ? Float(2) : Float(0); } int eff_x = x.effectiveBits(); Float result = Float(1) - erf(x, precision); finalizeResult(result, eff_x, precision); return result; } Float erfc(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(1); if (x.isInfinity()) { return x.isNegative() ? Float(2) : Float(0); } int eff_x = x.effectiveBits(); Float result = Float(1) - erf(std::move(x), precision); finalizeResult(result, eff_x, precision); return result; } // erfcx(x) — スケール付き相補誤差関数 exp(x²)·erfc(x) // x ≤ 2: 直接計算 (ガードビット付き) // x > 2: 連分数展開 (Modified Lentz) // √π · erfcx(x) = 1/(x + 1/2/(x + 2/2/(x + 3/2/(x + ...)))) static Float erfcx_core(Float x, int eff_x, int precision) { int wp = precision + 20; // x < 0: erfcx(x) = 2·exp(x²) - erfcx(-x) if (x.isNegative()) { Float neg_x = -x; Float erfcx_pos = erfcx(neg_x, wp); Float x2 = x * x; x2.truncateToApprox(wp); Float exp_x2 = exp(std::move(x2), wp); Float result = ldexp(exp_x2, 1) - erfcx_pos; finalizeResult(result, eff_x, precision); return result; } double x_approx = x.toDouble(); // x ≤ 2: 直接計算 exp(x²)·erfc(x) // erfc(x) = 1 - erf(x) で x²/ln(2) ビットの桁落ちが発生 → ガードビット追加 if (x_approx <= 2.0) { int extra = static_cast(std::ceil(x_approx * x_approx / std::log(2.0))) + 10; int wp2 = wp + extra; Float erfc_val = erfc(x, wp2); Float x2 = x * x; x2.truncateToApprox(wp); Float exp_x2 = exp(std::move(x2), wp); Float result = exp_x2 * erfc_val; finalizeResult(result, eff_x, precision); return result; } // x > 2: 連分数展開 (Modified Lentz) // g = x + a₁/(x + a₂/(x + ...)), a_n = n/2 // erfcx(x) = 1/(√π · g) x.truncateToApprox(wp); // x > 2 では D, C が 0 になることはないが、防御的にガード Float two(2); Float f = x; Float C = f; Float D(0); for (int n = 1; n < 10 * wp; n++) { // a_n = n/2 Float a_n = Float(n) / two; D = x + a_n * D; D.truncateToApprox(wp); D = Float(1) / D; C = x + a_n / C; C.truncateToApprox(wp); Float delta = C * D; f = f * delta; f.truncateToApprox(wp); // 収束判定: |δ - 1| < 2^{-(wp+5)} Float diff = delta - Float(1); if (diff.isZero()) break; int64_t diff_bits = diff.exponent() + static_cast(diff.mantissa().bitLength()); if (diff_bits < -(wp + 5)) break; } Float sqrtpi = sqrt(Float::pi(wp), wp); Float result = Float(1) / (sqrtpi * f); finalizeResult(result, eff_x, precision); return result; } Float erfcx(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(1); if (x.isInfinity()) { if (x.isNegative()) return Float::positiveInfinity(); return Float(0); } return erfcx_core(Float(x), x.effectiveBits(), precision); } Float erfcx(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(1); if (x.isInfinity()) { if (x.isNegative()) return Float::positiveInfinity(); return Float(0); } int eff = x.effectiveBits(); return erfcx_core(std::move(x), eff, precision); } //============================================================================= // ガンマ関数・関連関数 //============================================================================= // Bernoulli 数 B_{2k} を Akiyama-Tanigawa アルゴリズムで計算 // k=1..max_k → B_2, B_4, ..., B_{2*max_k} static std::vector computeBernoulliNumbers(int max_k, int precision) { int n = 2 * max_k + 1; std::vector a(n + 1, Float(0)); std::vector result(max_k + 1, Float(0)); // result[k] = B_{2k} result[0] = Float(1); // B_0 = 1 for (int m = 0; m <= n; m++) { a[m] = Float(1) / Float(m + 1); for (int j = m; j >= 1; j--) { a[j - 1] = Float(j) * (a[j - 1] - a[j]); a[j - 1].setResultPrecision(precision); } // a[0] = B_m if (m >= 2 && m % 2 == 0) { result[m / 2] = a[0]; } } return result; } // lnGamma(x) — Stirling 級数による対数ガンマ関数 // x > 0 のみ (負の引数は gamma() で反射公式を使う) static Float lnGamma_core(Float x, int eff_x, int precision) { int wp = precision + 20; // 正の整数の場合: lnGamma(n) = ln((n-1)!) if (x.isInteger()) { // 小さい整数の最適化 int n = static_cast(x.toDouble()); if (n <= 20 && n >= 1) { Float fact(1); for (int i = 2; i < n; i++) { fact = fact * Float(i); } if (n <= 2) return Float(0); // lnGamma(1)=lnGamma(2)=0 return log(fact, precision); } } // Stirling 級数: lnΓ(z) = (z-1/2)·ln(z) - z + ln(2π)/2 + Σ B_{2k}/(2k·(2k-1)·z^{2k-1}) // 引数シフト: z が小さい場合 Γ(z) = Γ(z+m)/[z·(z+1)·...·(z+m-1)] を利用 // → lnΓ(z) = lnΓ(z+m) - Σ ln(z+i) // z を十分大きくする (目安: z > wp/2) Float z = std::move(x); z.truncateToApprox(wp); double shift_target = wp * 0.35 + 10; int m = 0; Float prod_log(0); // Σ ln(z+i) の蓄積 double z_approx = z.toDouble(); if (z_approx < shift_target) { m = static_cast(shift_target - z_approx) + 1; // prod_log = Σ_{i=0}^{m-1} ln(z + i) for (int i = 0; i < m; i++) { Float zi = z + Float(i); zi.truncateToApprox(wp); prod_log = prod_log + log(zi, wp); } z = z + Float(m); z.truncateToApprox(wp); } // Stirling 級数の項数: B_{2k} / (2k·(2k-1)·z^{2k-1}) が十分小さくなるまで // Bernoulli 数を計算 int num_terms = static_cast(wp / (2 * std::log2(z.toDouble()))) + 5; if (num_terms < 5) num_terms = 5; if (num_terms > 500) num_terms = 500; auto bernoulli = computeBernoulliNumbers(num_terms, wp); // メイン計算 Float ln_z = log(z, wp); Float half = ldexp(Float(1), -1); // (z - 1/2) · ln(z) - z Float result = (z - half) * ln_z - z; // + ln(2π)/2 Float ln2pi = log(ldexp(Float::pi(wp), 1), wp); result = result + ln2pi * half; // + Stirling 補正項: Σ_{k=1}^{N} B_{2k} / (2k·(2k-1)·z^{2k-1}) Float z_inv = Float(1) / z; z_inv.truncateToApprox(wp); Float z_inv2 = z_inv * z_inv; z_inv2.truncateToApprox(wp); Float z_power = z_inv; // z^{-1}, z^{-3}, z^{-5}, ... for (int k = 1; k <= num_terms; k++) { Float coeff = bernoulli[k] / Float(2 * k * (2 * k - 1)); Float term = coeff * z_power; result = result + term; result.truncateToApprox(wp); // 収束チェック if (k >= 3 && term.isZero()) break; if (k >= 3) { auto term_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto result_bits = result.exponent() + static_cast(result.mantissa().bitLength()); if (result_bits - term_bits > wp + 5) break; } z_power = z_power * z_inv2; z_power.truncateToApprox(wp); } // 引数シフトの補正 result = result - prod_log; finalizeResult(result, eff_x, precision); return result; } Float lnGamma(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float::nan(); return Float::positiveInfinity(); } if (x.isZero() || x.isNegative()) { return Float::nan(); } return lnGamma_core(Float(x), x.effectiveBits(), precision); } Float lnGamma(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float::nan(); return Float::positiveInfinity(); } if (x.isZero() || x.isNegative()) { return Float::nan(); } int eff = x.effectiveBits(); return lnGamma_core(std::move(x), eff, precision); } // gamma(x) — ガンマ関数 Γ(x) Float gamma(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float::nan(); return Float::positiveInfinity(); } // 非正の整数で極 if (x.isZero()) return Float::positiveInfinity(); // Γ(0) = ±∞ if (x.isNegative() && x.isInteger()) return Float::nan(); // Γ(-n) は未定義 int eff_x = x.effectiveBits(); int wp = precision + 15; // 正の小さい整数: Γ(n) = (n-1)! if (x.isInteger() && x.isPositive()) { int n = static_cast(x.toDouble()); if (n <= 25) { Float result(1); for (int i = 2; i < n; i++) { result = result * Float(i); } finalizeResult(result, eff_x, precision); return result; } } // x > 0: Γ(x) = exp(lnΓ(x)) if (x.isPositive()) { Float lng = lnGamma(x, wp); Float result = exp(lng, wp); finalizeResult(result, eff_x, precision); return result; } // x < 0 (非整数): 反射公式 Γ(x)·Γ(1-x) = π/sin(πx) Float one_minus_x = Float(1) - x; Float lng = lnGamma(one_minus_x, wp); Float gamma_1mx = exp(lng, wp); Float sin_pi_x = sinPi(x, wp); // Γ(x) = π / (sin(πx) · Γ(1-x)) Float pi_val = Float::pi(wp); Float result = pi_val / (sin_pi_x * gamma_1mx); finalizeResult(result, eff_x, precision); return result; } Float gamma(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float::nan(); return Float::positiveInfinity(); } // 非正の整数で極 if (x.isZero()) return Float::positiveInfinity(); // Γ(0) = ±∞ if (x.isNegative() && x.isInteger()) return Float::nan(); // Γ(-n) は未定義 int eff_x = x.effectiveBits(); int wp = precision + 15; // 正の小さい整数: Γ(n) = (n-1)! if (x.isInteger() && x.isPositive()) { int n = static_cast(x.toDouble()); if (n <= 25) { Float result(1); for (int i = 2; i < n; i++) { result = result * Float(i); } finalizeResult(result, eff_x, precision); return result; } } // x > 0: Γ(x) = exp(lnΓ(x)) if (x.isPositive()) { Float lng = lnGamma(std::move(x), wp); Float result = exp(std::move(lng), wp); finalizeResult(result, eff_x, precision); return result; } // x < 0 (非整数): 反射公式 Γ(x)·Γ(1-x) = π/sin(πx) Float one_minus_x = Float(1) - x; Float lng = lnGamma(std::move(one_minus_x), wp); Float gamma_1mx = exp(std::move(lng), wp); Float sin_pi_x = sinPi(std::move(x), wp); // Γ(x) = π / (sin(πx) · Γ(1-x)) Float pi_val = Float::pi(wp); Float result = pi_val / (sin_pi_x * gamma_1mx); finalizeResult(result, eff_x, precision); return result; } // beta(a, b) — ベータ関数 B(a,b) = Γ(a)·Γ(b)/Γ(a+b) Float beta(const Float& a, const Float& b, int precision) { if (a.isNaN() || b.isNaN()) return Float::nan(); int eff = std::min(a.effectiveBits(), b.effectiveBits()); int wp = precision + 15; // B(a,b) = exp(lnΓ(a) + lnΓ(b) - lnΓ(a+b)) // ただし a, b が正の場合のみ lnGamma が使える if (a.isPositive() && b.isPositive()) { Float lga = lnGamma(a, wp); Float lgb = lnGamma(b, wp); Float lgab = lnGamma(a + b, wp); Float result = exp(lga + lgb - lgab, wp); finalizeResult(result, eff, precision); return result; } // 一般の場合: Γ(a)·Γ(b)/Γ(a+b) Float ga = gamma(a, wp); Float gb = gamma(b, wp); Float gab = gamma(a + b, wp); if (gab.isZero()) return Float::nan(); Float result = ga * gb / gab; finalizeResult(result, eff, precision); return result; } Float beta(Float&& a, Float&& b, int precision) { if (a.isNaN() || b.isNaN()) return Float::nan(); int eff = std::min(a.effectiveBits(), b.effectiveBits()); int wp = precision + 15; // B(a,b) = exp(lnΓ(a) + lnΓ(b) - lnΓ(a+b)) if (a.isPositive() && b.isPositive()) { Float lga = lnGamma(a, wp); Float lgb = lnGamma(b, wp); Float lgab = lnGamma(a + b, wp); Float result = exp(lga + lgb - lgab, wp); finalizeResult(result, eff, precision); return result; } // 一般の場合: Γ(a)·Γ(b)/Γ(a+b) Float ga = gamma(a, wp); Float gb = gamma(b, wp); Float gab = gamma(a + std::move(b), wp); if (gab.isZero()) return Float::nan(); Float result = ga * gb / gab; finalizeResult(result, eff, precision); return result; } // digamma(x) — ディガンマ関数 ψ(x) = d/dx ln Γ(x) = Γ'(x)/Γ(x) // 漸近展開: ψ(z) ~ ln(z) - 1/(2z) - Σ B_{2k}/(2k·z^{2k}) static Float digamma_core(Float x, int eff_x, int precision) { int wp = precision + 20; // 負の引数: 反射公式 ψ(1-x) - ψ(x) = π·cot(πx) if (x.isNegative()) { Float one_minus_x = Float(1) - x; Float psi_1mx = digamma(one_minus_x, wp); Float pi_val = Float::pi(wp); Float cot_pix = cosPi(x, wp) / sinPi(x, wp); Float result = psi_1mx - pi_val * cot_pix; finalizeResult(result, eff_x, precision); return result; } // 引数シフト: ψ(x+1) = ψ(x) + 1/x → ψ(x) = ψ(x+m) - Σ 1/(x+i) Float z = std::move(x); z.truncateToApprox(wp); double shift_target = wp * 0.35 + 10; int m = 0; Float shift_sum(0); double z_approx = z.toDouble(); if (z_approx < shift_target) { m = static_cast(shift_target - z_approx) + 1; for (int i = 0; i < m; i++) { Float zi = z + Float(i); zi.truncateToApprox(wp); shift_sum = shift_sum + Float(1) / zi; } z = z + Float(m); z.truncateToApprox(wp); } // 漸近展開: ψ(z) ~ ln(z) - 1/(2z) - Σ_{k=1}^{N} B_{2k}/(2k·z^{2k}) int num_terms = static_cast(wp / (2 * std::log2(z.toDouble()))) + 5; if (num_terms < 5) num_terms = 5; if (num_terms > 500) num_terms = 500; auto bernoulli = computeBernoulliNumbers(num_terms, wp); Float result = log(z, wp) - ldexp(Float(1) / z, -1); Float z_inv = Float(1) / z; z_inv.truncateToApprox(wp); Float z_inv2 = z_inv * z_inv; z_inv2.truncateToApprox(wp); Float z_power = z_inv2; // z^{-2}, z^{-4}, ... for (int k = 1; k <= num_terms; k++) { Float coeff = bernoulli[k] / Float(2 * k); Float term = coeff * z_power; result = result - term; result.truncateToApprox(wp); if (k >= 3 && term.isZero()) break; if (k >= 3) { auto term_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto result_bits = result.exponent() + static_cast(result.mantissa().bitLength()); if (result_bits - term_bits > wp + 5) break; } z_power = z_power * z_inv2; z_power.truncateToApprox(wp); } result = result - shift_sum; finalizeResult(result, eff_x, precision); return result; } Float digamma(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float::nan(); return Float::positiveInfinity(); } if (x.isZero()) return Float::nan(); // 非正の整数: 極 if (x.isNegative() && x.isInteger()) return Float::nan(); return digamma_core(Float(x), x.effectiveBits(), precision); } Float digamma(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float::nan(); return Float::positiveInfinity(); } if (x.isZero()) return Float::nan(); // 非正の整数: 極 if (x.isNegative() && x.isInteger()) return Float::nan(); int eff = x.effectiveBits(); return digamma_core(std::move(x), eff, precision); } // trigamma(x) — トリガンマ関数 ψ₁(x) = d²/dx² ln Γ(x) // 漸近展開: ψ₁(z) ~ 1/z + 1/(2z²) + Σ B_{2k}/(z^{2k+1}) static Float trigamma_core(Float x, int eff_x, int precision) { int wp = precision + 20; // 負の引数: 反射公式 ψ₁(1-x) + ψ₁(x) = π²/sin²(πx) if (x.isNegative()) { Float one_minus_x = Float(1) - x; Float psi1_1mx = trigamma(one_minus_x, wp); Float pi_val = Float::pi(wp); Float sin_pix = sinPi(x, wp); Float pi2_over_sin2 = (pi_val * pi_val) / (sin_pix * sin_pix); Float result = pi2_over_sin2 - psi1_1mx; finalizeResult(result, eff_x, precision); return result; } // 引数シフト: ψ₁(x+1) = ψ₁(x) - 1/x² → ψ₁(x) = ψ₁(x+m) + Σ 1/(x+i)² Float z = std::move(x); z.truncateToApprox(wp); double shift_target = wp * 0.35 + 10; int m = 0; Float shift_sum(0); double z_approx = z.toDouble(); if (z_approx < shift_target) { m = static_cast(shift_target - z_approx) + 1; for (int i = 0; i < m; i++) { Float zi = z + Float(i); zi.truncateToApprox(wp); Float zi_inv = Float(1) / zi; shift_sum = shift_sum + zi_inv * zi_inv; } z = z + Float(m); z.truncateToApprox(wp); } // 漸近展開: ψ₁(z) ~ 1/z + 1/(2z²) + Σ_{k=1}^{N} B_{2k}/z^{2k+1} int num_terms = static_cast(wp / (2 * std::log2(z.toDouble()))) + 5; if (num_terms < 5) num_terms = 5; if (num_terms > 500) num_terms = 500; auto bernoulli = computeBernoulliNumbers(num_terms, wp); Float z_inv = Float(1) / z; z_inv.truncateToApprox(wp); Float z_inv2 = z_inv * z_inv; z_inv2.truncateToApprox(wp); Float result = z_inv + ldexp(z_inv2, -1); Float z_power = z_inv2 * z_inv; // z^{-3}, z^{-5}, ... for (int k = 1; k <= num_terms; k++) { Float term = bernoulli[k] * z_power; result = result + term; result.truncateToApprox(wp); if (k >= 3 && term.isZero()) break; if (k >= 3) { auto term_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto result_bits = result.exponent() + static_cast(result.mantissa().bitLength()); if (result_bits - term_bits > wp + 5) break; } z_power = z_power * z_inv2; z_power.truncateToApprox(wp); } result = result + shift_sum; finalizeResult(result, eff_x, precision); return result; } Float trigamma(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float::nan(); return Float(0); // ψ₁(∞) = 0 } if (x.isZero()) return Float::nan(); // 非正の整数: 極 if (x.isNegative() && x.isInteger()) return Float::nan(); return trigamma_core(Float(x), x.effectiveBits(), precision); } Float trigamma(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float::nan(); return Float(0); // ψ₁(∞) = 0 } if (x.isZero()) return Float::nan(); // 非正の整数: 極 if (x.isNegative() && x.isInteger()) return Float::nan(); int eff = x.effectiveBits(); return trigamma_core(std::move(x), eff, precision); } // polygamma(n, x) — ポリガンマ関数 ψ^(n)(x) = d^{n+1}/dx^{n+1} ln Γ(x) // n=0: digamma, n=1: trigamma (既存実装に委譲) // n≥2: 漸近展開 + 引数シフト // ψ^(n)(z) = (-1)^{n+1} [(n-1)!/z^n + n!/(2z^{n+1}) // + Σ_{k=1}^{K} B_{2k} · (2k+n-1)!/((2k)!) / z^{2k+n}] Float polygamma(int n, const Float& x, int precision) { if (n < 0) { throw std::invalid_argument("polygamma: n must be non-negative"); } if (n == 0) return digamma(x, precision); if (n == 1) return trigamma(x, precision); // n >= 2 int eff_x = x.effectiveBits(); if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float::nan(); return Float(0); // ψ^(n)(+∞) = 0 (n ≥ 1) } if (x.isZero()) return Float::nan(); if (x.isNegative() && x.isInteger()) return Float::nan(); int wp = precision + 20; // n! と (n-1)! の計算 Float n_fact(1); for (int i = 2; i <= n; i++) n_fact = n_fact * Float(i); Float n_minus_1_fact = n_fact / Float(n); // 引数シフト: ψ^(n)(z) = ψ^(n)(z+m) + (-1)^{n+1}·n!·Σ 1/(z+i)^{n+1} Float z = x; z.truncateToApprox(wp); Float shift_sum(0); double shift_target = wp * 0.35 + 10; double z_approx = z.toDouble(); if (z_approx < shift_target) { int m = static_cast(std::ceil(shift_target - z_approx)) + 1; for (int i = 0; i < m; i++) { Float zi = z + Float(i); zi.truncateToApprox(wp); // zi^{n+1} を繰り返し乗算で計算 Float zi_pow = zi; for (int j = 1; j <= n; j++) { zi_pow = zi_pow * zi; zi_pow.truncateToApprox(wp); } shift_sum = shift_sum + Float(1) / zi_pow; } z = z + Float(m); z.truncateToApprox(wp); } // 漸近展開 int num_terms = static_cast(wp / (2 * std::log2(z.toDouble()))) + 5; if (num_terms < 5) num_terms = 5; if (num_terms > 500) num_terms = 500; auto bernoulli = computeBernoulliNumbers(num_terms, wp); Float z_inv = Float(1) / z; z_inv.truncateToApprox(wp); Float z_inv2 = z_inv * z_inv; z_inv2.truncateToApprox(wp); // z^{-n} Float z_inv_n(1); for (int i = 0; i < n; i++) { z_inv_n = z_inv_n * z_inv; z_inv_n.truncateToApprox(wp); } // 最初の2項: (n-1)!/z^n + n!/(2z^{n+1}) Float A = n_minus_1_fact * z_inv_n + ldexp(n_fact * z_inv_n * z_inv, -1); A.truncateToApprox(wp); // Σ B_{2k} · R(n,k) / z^{2k+n} // R(n,k) = (2k+n-1)!/(2k)! = Π_{j=1}^{n-1} (2k+j) Float z_power = z_inv_n * z_inv2; // z^{-(n+2)} for (int k = 1; k <= num_terms; k++) { Float R(1); for (int j = 1; j <= n - 1; j++) { R = R * Float(2 * k + j); } Float term = bernoulli[k] * R * z_power; A = A + term; A.truncateToApprox(wp); if (k >= 3 && term.isZero()) break; if (k >= 3) { auto term_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto A_bits = A.exponent() + static_cast(A.mantissa().bitLength()); if (A_bits - term_bits > wp + 5) break; } z_power = z_power * z_inv2; z_power.truncateToApprox(wp); } // 符号: (-1)^{n+1} Float result = (n % 2 == 0) ? -A : A; // シフト補正: (-1)^{n+1} · n! · shift_sum if (!shift_sum.isZero()) { Float correction = n_fact * shift_sum; if (n % 2 == 0) correction = -correction; result = result + correction; } finalizeResult(result, eff_x, precision); return result; } Float polygamma(int n, Float&& x, int precision) { if (n < 0) { throw std::invalid_argument("polygamma: n must be non-negative"); } if (n == 0) return digamma(std::move(x), precision); if (n == 1) return trigamma(std::move(x), precision); // n >= 2 int eff_x = x.effectiveBits(); if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float::nan(); return Float(0); // ψ^(n)(+∞) = 0 (n ≥ 1) } if (x.isZero()) return Float::nan(); if (x.isNegative() && x.isInteger()) return Float::nan(); int wp = precision + 20; // n! と (n-1)! の計算 Float n_fact(1); for (int i = 2; i <= n; i++) n_fact = n_fact * Float(i); Float n_minus_1_fact = n_fact / Float(n); // 引数シフト: ψ^(n)(z) = ψ^(n)(z+m) + (-1)^{n+1}·n!·Σ 1/(z+i)^{n+1} Float z = std::move(x); z.truncateToApprox(wp); Float shift_sum(0); double shift_target = wp * 0.35 + 10; double z_approx = z.toDouble(); if (z_approx < shift_target) { int m = static_cast(std::ceil(shift_target - z_approx)) + 1; for (int i = 0; i < m; i++) { Float zi = z + Float(i); zi.truncateToApprox(wp); // zi^{n+1} を繰り返し乗算で計算 Float zi_pow = zi; for (int j = 1; j <= n; j++) { zi_pow = zi_pow * zi; zi_pow.truncateToApprox(wp); } shift_sum = shift_sum + Float(1) / zi_pow; } z = z + Float(m); z.truncateToApprox(wp); } // 漸近展開 int num_terms = static_cast(wp / (2 * std::log2(z.toDouble()))) + 5; if (num_terms < 5) num_terms = 5; if (num_terms > 500) num_terms = 500; auto bernoulli = computeBernoulliNumbers(num_terms, wp); Float z_inv = Float(1) / z; z_inv.truncateToApprox(wp); Float z_inv2 = z_inv * z_inv; z_inv2.truncateToApprox(wp); // z^{-n} Float z_inv_n(1); for (int i = 0; i < n; i++) { z_inv_n = z_inv_n * z_inv; z_inv_n.truncateToApprox(wp); } // 最初の2項: (n-1)!/z^n + n!/(2z^{n+1}) Float A = n_minus_1_fact * z_inv_n + ldexp(n_fact * z_inv_n * z_inv, -1); A.truncateToApprox(wp); // Σ B_{2k} · R(n,k) / z^{2k+n} // R(n,k) = (2k+n-1)!/(2k)! = Π_{j=1}^{n-1} (2k+j) Float z_power = z_inv_n * z_inv2; // z^{-(n+2)} for (int k = 1; k <= num_terms; k++) { Float R(1); for (int j = 1; j <= n - 1; j++) { R = R * Float(2 * k + j); } Float term = bernoulli[k] * R * z_power; A = A + term; A.truncateToApprox(wp); if (k >= 3 && term.isZero()) break; if (k >= 3) { auto term_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto A_bits = A.exponent() + static_cast(A.mantissa().bitLength()); if (A_bits - term_bits > wp + 5) break; } z_power = z_power * z_inv2; z_power.truncateToApprox(wp); } // 符号: (-1)^{n+1} Float result = (n % 2 == 0) ? -A : A; // シフト補正: (-1)^{n+1} · n! · shift_sum if (!shift_sum.isZero()) { Float correction = n_fact * shift_sum; if (n % 2 == 0) correction = -correction; result = result + correction; } finalizeResult(result, eff_x, precision); return result; } //============================================================================= // 不完全ガンマ関数 //============================================================================= // gammaP — 正規化下側不完全ガンマ関数 P(a,x) = γ(a,x)/Γ(a) // x < a+1: Taylor 級数 // P(a,x) = exp(-x + a·ln(x) - lnΓ(a)) · Σ_{n≥0} x^n / (a·(a+1)···(a+n)) // x ≥ a+1: 1 - Q(a,x) (CF の方が高速) Float gammaP(const Float& a, const Float& x, int precision) { if (a.isNaN() || x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); // P(a,0) = 0 if (x.isNegative()) return Float::nan(); // x ≥ 0 のみ if (!a.isPositive()) return Float::nan(); // a > 0 のみ if (x.isInfinity()) return Float(1); // P(a,+∞) = 1 int eff = std::min(a.effectiveBits(), x.effectiveBits()); int wp = precision + 20; double a_d = a.toDouble(); double x_d = x.toDouble(); if (x_d < a_d + 1.0) { // Taylor 級数 Float a_wp = a; a_wp.truncateToApprox(wp); Float x_wp = x; x_wp.truncateToApprox(wp); Float front = exp(-x_wp + a_wp * log(x_wp, wp) - lnGamma(a_wp, wp), wp); // S = Σ x^n / Π_{k=0}^{n} (a+k) // term_0 = 1/a, term_n = term_{n-1} · x / (a+n) Float term = Float(1) / a_wp; Float sum = term; for (int n = 1; n < 10000; n++) { term = term * x_wp / (a_wp + Float(n)); term.truncateToApprox(wp); sum = sum + term; sum.truncateToApprox(wp); if (n >= 3 && term.isZero()) break; if (n >= 3) { int64_t t_bits = term.exponent() + static_cast(term.mantissa().bitLength()); int64_t s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - t_bits > wp + 5) break; } } Float result = front * sum; finalizeResult(result, eff, precision); return result; } else { // x ≥ a+1: CF 経由で Q を計算し 1 - Q を返す Float q = gammaQ(a, x, wp); Float result = Float(1) - q; finalizeResult(result, eff, precision); return result; } } Float gammaP(Float&& a, Float&& x, int precision) { if (a.isNaN() || x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); // P(a,0) = 0 if (x.isNegative()) return Float::nan(); // x ≥ 0 のみ if (!a.isPositive()) return Float::nan(); // a > 0 のみ if (x.isInfinity()) return Float(1); // P(a,+∞) = 1 int eff = std::min(a.effectiveBits(), x.effectiveBits()); int wp = precision + 20; double a_d = a.toDouble(); double x_d = x.toDouble(); if (x_d < a_d + 1.0) { // Taylor 級数 Float a_wp = std::move(a); a_wp.truncateToApprox(wp); Float x_wp = std::move(x); x_wp.truncateToApprox(wp); Float front = exp(-x_wp + a_wp * log(x_wp, wp) - lnGamma(a_wp, wp), wp); Float term = Float(1) / a_wp; Float sum = term; for (int n = 1; n < 10000; n++) { term = term * x_wp / (a_wp + Float(n)); term.truncateToApprox(wp); sum = sum + term; sum.truncateToApprox(wp); if (n >= 3 && term.isZero()) break; if (n >= 3) { int64_t t_bits = term.exponent() + static_cast(term.mantissa().bitLength()); int64_t s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - t_bits > wp + 5) break; } } Float result = front * sum; finalizeResult(result, eff, precision); return result; } else { Float q = gammaQ(std::move(a), std::move(x), wp); Float result = Float(1) - q; finalizeResult(result, eff, precision); return result; } } // gammaQ — 正規化上側不完全ガンマ関数 Q(a,x) = Γ(a,x)/Γ(a) // x ≥ a+1: Legendre 連分数 (Modified Lentz) // Q(a,x) = exp(-x + a·ln(x) - lnΓ(a)) / f // f = (x+1-a) + K_{n≥1} [-n(n-a) / (x+2n+1-a)] // x < a+1: 1 - P(a,x) (Taylor の方が高速) Float gammaQ(const Float& a, const Float& x, int precision) { if (a.isNaN() || x.isNaN()) return Float::nan(); if (x.isZero()) return Float(1); // Q(a,0) = 1 if (x.isNegative()) return Float::nan(); if (!a.isPositive()) return Float::nan(); if (x.isInfinity()) return Float(0); // Q(a,+∞) = 0 int eff = std::min(a.effectiveBits(), x.effectiveBits()); int wp = precision + 20; double a_d = a.toDouble(); double x_d = x.toDouble(); if (x_d >= a_d + 1.0) { // Legendre CF Float a_wp = a; a_wp.truncateToApprox(wp); Float x_wp = x; x_wp.truncateToApprox(wp); Float front = exp(-x_wp + a_wp * log(x_wp, wp) - lnGamma(a_wp, wp), wp); // Modified Lentz Float tiny(1, -wp * 4); // 2^{-4·wp} (ゼロ回避用) Float b0 = x_wp + Float(1) - a_wp; Float f = b0.isZero() ? tiny : b0; f.truncateToApprox(wp); Float C = f; Float D(0); for (int n = 1; n < 10000; n++) { Float an = Float(-n) * (Float(n) - a_wp); Float bn = x_wp + Float(2 * n + 1) - a_wp; D = bn + an * D; D.truncateToApprox(wp); if (D.isZero()) D = tiny; D = Float(1) / D; D.truncateToApprox(wp); C = bn + an / C; C.truncateToApprox(wp); if (C.isZero()) C = tiny; Float delta = C * D; f = f * delta; f.truncateToApprox(wp); Float diff = delta - Float(1); if (diff.isZero()) break; if (n >= 3) { int64_t diff_bits = diff.exponent() + static_cast(diff.mantissa().bitLength()); if (diff_bits < -(wp + 5)) break; } } Float result = front / f; finalizeResult(result, eff, precision); return result; } else { // x < a+1: Taylor 経由で P を計算し 1 - P を返す Float p = gammaP(a, x, wp); Float result = Float(1) - p; finalizeResult(result, eff, precision); return result; } } Float gammaQ(Float&& a, Float&& x, int precision) { if (a.isNaN() || x.isNaN()) return Float::nan(); if (x.isZero()) return Float(1); // Q(a,0) = 1 if (x.isNegative()) return Float::nan(); if (!a.isPositive()) return Float::nan(); if (x.isInfinity()) return Float(0); // Q(a,+∞) = 0 int eff = std::min(a.effectiveBits(), x.effectiveBits()); int wp = precision + 20; double a_d = a.toDouble(); double x_d = x.toDouble(); if (x_d >= a_d + 1.0) { // Legendre CF Float a_wp = std::move(a); a_wp.truncateToApprox(wp); Float x_wp = std::move(x); x_wp.truncateToApprox(wp); Float front = exp(-x_wp + a_wp * log(x_wp, wp) - lnGamma(a_wp, wp), wp); // Modified Lentz Float tiny(1, -wp * 4); Float b0 = x_wp + Float(1) - a_wp; Float f = b0.isZero() ? tiny : b0; f.truncateToApprox(wp); Float C = f; Float D(0); for (int n = 1; n < 10000; n++) { Float an = Float(-n) * (Float(n) - a_wp); Float bn = x_wp + Float(2 * n + 1) - a_wp; D = bn + an * D; D.truncateToApprox(wp); if (D.isZero()) D = tiny; D = Float(1) / D; D.truncateToApprox(wp); C = bn + an / C; C.truncateToApprox(wp); if (C.isZero()) C = tiny; Float delta = C * D; f = f * delta; f.truncateToApprox(wp); Float diff = delta - Float(1); if (diff.isZero()) break; if (n >= 3) { int64_t diff_bits = diff.exponent() + static_cast(diff.mantissa().bitLength()); if (diff_bits < -(wp + 5)) break; } } Float result = front / f; finalizeResult(result, eff, precision); return result; } else { Float p = gammaP(std::move(a), std::move(x), wp); Float result = Float(1) - p; finalizeResult(result, eff, precision); return result; } } // gammaLower — 下側不完全ガンマ γ(a,x) = P(a,x)·Γ(a) Float gammaLower(const Float& a, const Float& x, int precision) { if (a.isNaN() || x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); if (x.isNegative()) return Float::nan(); if (!a.isPositive()) return Float::nan(); int eff = std::min(a.effectiveBits(), x.effectiveBits()); int wp = precision + 15; Float p = gammaP(a, x, wp); Float g = gamma(a, wp); Float result = p * g; finalizeResult(result, eff, precision); return result; } Float gammaLower(Float&& a, Float&& x, int precision) { if (a.isNaN() || x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); if (x.isNegative()) return Float::nan(); if (!a.isPositive()) return Float::nan(); int eff = std::min(a.effectiveBits(), x.effectiveBits()); int wp = precision + 15; Float p = gammaP(a, x, wp); Float g = gamma(std::move(a), wp); Float result = p * g; finalizeResult(result, eff, precision); return result; } // gammaUpper — 上側不完全ガンマ Γ(a,x) = Q(a,x)·Γ(a) Float gammaUpper(const Float& a, const Float& x, int precision) { if (a.isNaN() || x.isNaN()) return Float::nan(); if (x.isZero()) return gamma(a, precision); // Γ(a,0) = Γ(a) if (x.isNegative()) return Float::nan(); if (!a.isPositive()) return Float::nan(); if (x.isInfinity()) return Float(0); int eff = std::min(a.effectiveBits(), x.effectiveBits()); int wp = precision + 15; Float q = gammaQ(a, x, wp); Float g = gamma(a, wp); Float result = q * g; finalizeResult(result, eff, precision); return result; } Float gammaUpper(Float&& a, Float&& x, int precision) { if (a.isNaN() || x.isNaN()) return Float::nan(); if (x.isZero()) return gamma(std::move(a), precision); // Γ(a,0) = Γ(a) if (x.isNegative()) return Float::nan(); if (!a.isPositive()) return Float::nan(); if (x.isInfinity()) return Float(0); int eff = std::min(a.effectiveBits(), x.effectiveBits()); int wp = precision + 15; Float q = gammaQ(a, x, wp); Float g = gamma(std::move(a), wp); Float result = q * g; finalizeResult(result, eff, precision); return result; } //============================================================================= // 不完全ベータ関数 //============================================================================= // betaRegularized — 正規化不完全ベータ関数 I_x(a,b) // 連分数展開 (Modified Lentz) + 対称性 // I_x(a,b) = front / (1 + d₁/(1 + d₂/(1 + ...))) // d_{2m+1} = -(a+m)(a+b+m)x / ((a+2m)(a+2m+1)) // d_{2m} = m(b-m)x / ((a+2m-1)(a+2m)) // x ≥ (a+1)/(a+b+2) の場合: I_x(a,b) = 1 - I_{1-x}(b,a) で反転 Float betaRegularized(const Float& x, const Float& a, const Float& b, int precision) { if (x.isNaN() || a.isNaN() || b.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); // I_0(a,b) = 0 Float one(1); if (x == one) return Float(1); // I_1(a,b) = 1 if (x.isNegative() || x > one) return Float::nan(); // x ∈ [0,1] if (!a.isPositive() || !b.isPositive()) return Float::nan(); int eff = std::min({x.effectiveBits(), a.effectiveBits(), b.effectiveBits()}); int wp = precision + 20; Float x_wp = x; x_wp.truncateToApprox(wp); Float a_wp = a; a_wp.truncateToApprox(wp); Float b_wp = b; b_wp.truncateToApprox(wp); // 対称性による反転判定: x ≥ (a+1)/(a+b+2) なら反転 double x_d = x.toDouble(); double a_d = a.toDouble(); double b_d = b.toDouble(); bool flip = x_d >= (a_d + 1.0) / (a_d + b_d + 2.0); Float xx, aa, bb; if (flip) { xx = Float(1) - x_wp; xx.truncateToApprox(wp); aa = b_wp; bb = a_wp; } else { xx = x_wp; aa = a_wp; bb = b_wp; } // front = x^a · (1-x)^b / (a · B(a,b)) // = exp(a·ln(x) + b·ln(1-x) + lnΓ(a+b) - lnΓ(a) - lnΓ(b)) / a Float ln_front = aa * log(xx, wp) + bb * log(Float(1) - xx, wp) + lnGamma(aa + bb, wp) - lnGamma(aa, wp) - lnGamma(bb, wp); Float front = exp(ln_front, wp) / aa; front.truncateToApprox(wp); // CF: 1 + d_1/(1 + d_2/(1 + ...)) を Modified Lentz で評価 Float tiny(1, -wp * 4); Float f(1); Float C(1); Float D(0); for (int n = 1; n < 10000; n++) { Float d; if (n % 2 == 1) { // d_{2m+1}: m = (n-1)/2 int m = (n - 1) / 2; Float am = aa + Float(m); Float abm = aa + bb + Float(m); Float denom = (aa + Float(2 * m)) * (aa + Float(2 * m + 1)); d = -(am * abm * xx) / denom; } else { // d_{2m}: m = n/2 int m = n / 2; Float fm(m); Float bm = bb - Float(m); Float denom = (aa + Float(2 * m - 1)) * (aa + Float(2 * m)); d = (fm * bm * xx) / denom; } d.truncateToApprox(wp); D = Float(1) + d * D; D.truncateToApprox(wp); if (D.isZero()) D = tiny; D = Float(1) / D; D.truncateToApprox(wp); C = Float(1) + d / C; C.truncateToApprox(wp); if (C.isZero()) C = tiny; Float delta = C * D; f = f * delta; f.truncateToApprox(wp); if (n >= 3) { Float diff = delta - Float(1); if (diff.isZero()) break; int64_t diff_bits = diff.exponent() + static_cast(diff.mantissa().bitLength()); if (diff_bits < -(wp + 5)) break; } } Float result = front / f; if (flip) { result = Float(1) - result; } finalizeResult(result, eff, precision); return result; } Float betaRegularized(Float&& x, Float&& a, Float&& b, int precision) { if (x.isNaN() || a.isNaN() || b.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); // I_0(a,b) = 0 Float one(1); if (x == one) return Float(1); // I_1(a,b) = 1 if (x.isNegative() || x > one) return Float::nan(); // x ∈ [0,1] if (!a.isPositive() || !b.isPositive()) return Float::nan(); int eff = std::min({x.effectiveBits(), a.effectiveBits(), b.effectiveBits()}); int wp = precision + 20; Float x_wp = std::move(x); x_wp.truncateToApprox(wp); Float a_wp = std::move(a); a_wp.truncateToApprox(wp); Float b_wp = std::move(b); b_wp.truncateToApprox(wp); // 対称性による反転判定: x ≥ (a+1)/(a+b+2) なら反転 double x_d = x_wp.toDouble(); double a_d = a_wp.toDouble(); double b_d = b_wp.toDouble(); bool flip = x_d >= (a_d + 1.0) / (a_d + b_d + 2.0); Float xx, aa, bb; if (flip) { xx = Float(1) - x_wp; xx.truncateToApprox(wp); aa = b_wp; bb = a_wp; } else { xx = x_wp; aa = a_wp; bb = b_wp; } // front = x^a · (1-x)^b / (a · B(a,b)) Float ln_front = aa * log(xx, wp) + bb * log(Float(1) - xx, wp) + lnGamma(aa + bb, wp) - lnGamma(aa, wp) - lnGamma(bb, wp); Float front = exp(ln_front, wp) / aa; front.truncateToApprox(wp); // CF: Modified Lentz Float tiny(1, -wp * 4); Float f(1); Float C(1); Float D(0); for (int n = 1; n < 10000; n++) { Float d; if (n % 2 == 1) { int m = (n - 1) / 2; Float am = aa + Float(m); Float abm = aa + bb + Float(m); Float denom = (aa + Float(2 * m)) * (aa + Float(2 * m + 1)); d = -(am * abm * xx) / denom; } else { int m = n / 2; Float fm(m); Float bm = bb - Float(m); Float denom = (aa + Float(2 * m - 1)) * (aa + Float(2 * m)); d = (fm * bm * xx) / denom; } d.truncateToApprox(wp); D = Float(1) + d * D; D.truncateToApprox(wp); if (D.isZero()) D = tiny; D = Float(1) / D; D.truncateToApprox(wp); C = Float(1) + d / C; C.truncateToApprox(wp); if (C.isZero()) C = tiny; Float delta = C * D; f = f * delta; f.truncateToApprox(wp); if (n >= 3) { Float diff = delta - Float(1); if (diff.isZero()) break; int64_t diff_bits = diff.exponent() + static_cast(diff.mantissa().bitLength()); if (diff_bits < -(wp + 5)) break; } } Float result = front / f; if (flip) { result = Float(1) - result; } finalizeResult(result, eff, precision); return result; } //============================================================================= // 楕円積分 — Carlson 対称形式 //============================================================================= // R_C(x, y) = (1/2)∫₀^∞ dt / [(t+y)√(t+x)] // 重複定理による2次収束反復 static Float carlsonRC_core(Float xw, Float yw, int eff, int precision) { int wp = precision + 20; xw.truncateToApprox(wp); yw.truncateToApprox(wp); for (int iter = 0; iter < 200; iter++) { Float lam = ldexp(sqrt(xw * yw, wp), 1) + yw; xw = ldexp(xw + lam, -2); xw.truncateToApprox(wp); yw = ldexp(yw + lam, -2); yw.truncateToApprox(wp); Float A = (xw + yw + yw) / Float(3); Float s = (yw - A) / A; if (s.isZero()) break; int64_t s_bits = s.exponent() + static_cast(s.mantissa().bitLength()); if (s_bits < -(wp + 5)) break; } Float A = (xw + yw + yw) / Float(3); Float s = (yw - A) / A; // 1 + (3/10)s² + (1/7)s³ + (3/8)s⁴ + (9/22)s⁵ Float poly = Float(1) + s * s * (Float(3) / Float(10) + s * (Float(1) / Float(7) + s * (Float(3) / Float(8) + s * Float(9) / Float(22)))); Float result = poly / sqrt(A, wp); finalizeResult(result, eff, precision); return result; } Float carlsonRC(const Float& x, const Float& y, int precision) { if (x.isNaN() || y.isNaN()) return Float::nan(); if (x.isNegative() || !y.isPositive()) return Float::nan(); int eff = std::min(x.effectiveBits(), y.effectiveBits()); return carlsonRC_core(Float(x), Float(y), eff, precision); } Float carlsonRC(Float&& x, Float&& y, int precision) { if (x.isNaN() || y.isNaN()) return Float::nan(); if (x.isNegative() || !y.isPositive()) return Float::nan(); int eff = std::min(x.effectiveBits(), y.effectiveBits()); return carlsonRC_core(std::move(x), std::move(y), eff, precision); } // R_F(x, y, z) = (1/2)∫₀^∞ dt / √[(t+x)(t+y)(t+z)] // x, y, z ≥ 0, 高々1つが 0 static Float carlsonRF_core(Float xw, Float yw, Float zw, int eff, int precision) { int wp = precision + 20; xw.truncateToApprox(wp); yw.truncateToApprox(wp); zw.truncateToApprox(wp); for (int iter = 0; iter < 200; iter++) { Float sqx = sqrt(xw, wp); Float sqy = sqrt(yw, wp); Float sqz = sqrt(zw, wp); Float lam = sqx * sqy + sqy * sqz + sqz * sqx; xw = ldexp(xw + lam, -2); xw.truncateToApprox(wp); yw = ldexp(yw + lam, -2); yw.truncateToApprox(wp); zw = ldexp(zw + lam, -2); zw.truncateToApprox(wp); Float A = (xw + yw + zw) / Float(3); Float dx = (A - xw) / A; Float dy = (A - yw) / A; Float dz = (A - zw) / A; int64_t dx_b = dx.isZero() ? INT64_MIN : dx.exponent() + static_cast(dx.mantissa().bitLength()); int64_t dy_b = dy.isZero() ? INT64_MIN : dy.exponent() + static_cast(dy.mantissa().bitLength()); int64_t dz_b = dz.isZero() ? INT64_MIN : dz.exponent() + static_cast(dz.mantissa().bitLength()); if (std::max({dx_b, dy_b, dz_b}) < -(wp + 5)) break; } Float A = (xw + yw + zw) / Float(3); Float X = (A - xw) / A; Float Y = (A - yw) / A; Float Z = -(X + Y); Float E2 = X * Y - Z * Z; Float E3 = X * Y * Z; Float poly = Float(1) - E2 / Float(10) + E3 / Float(14) + E2 * E2 / Float(24) - Float(3) * E2 * E3 / Float(44); Float result = poly / sqrt(A, wp); finalizeResult(result, eff, precision); return result; } Float carlsonRF(const Float& x, const Float& y, const Float& z, int precision) { if (x.isNaN() || y.isNaN() || z.isNaN()) return Float::nan(); if (x.isNegative() || y.isNegative() || z.isNegative()) return Float::nan(); int eff = std::min({x.effectiveBits(), y.effectiveBits(), z.effectiveBits()}); return carlsonRF_core(Float(x), Float(y), Float(z), eff, precision); } Float carlsonRF(Float&& x, Float&& y, Float&& z, int precision) { if (x.isNaN() || y.isNaN() || z.isNaN()) return Float::nan(); if (x.isNegative() || y.isNegative() || z.isNegative()) return Float::nan(); int eff = std::min({x.effectiveBits(), y.effectiveBits(), z.effectiveBits()}); return carlsonRF_core(std::move(x), std::move(y), std::move(z), eff, precision); } // R_D(x, y, z) = (3/2)∫₀^∞ dt / [(t+z)^{3/2}√((t+x)(t+y))] // x, y ≥ 0 (高々1つが 0), z > 0 static Float carlsonRD_core(Float xw, Float yw, Float zw, int eff, int precision) { int wp = precision + 20; xw.truncateToApprox(wp); yw.truncateToApprox(wp); zw.truncateToApprox(wp); Float sigma(0); Float fac(1); for (int iter = 0; iter < 200; iter++) { Float sqx = sqrt(xw, wp); Float sqy = sqrt(yw, wp); Float sqz = sqrt(zw, wp); Float lam = sqx * sqy + sqy * sqz + sqz * sqx; sigma = sigma + fac / (sqz * (zw + lam)); sigma.truncateToApprox(wp); fac = ldexp(fac, -2); xw = ldexp(xw + lam, -2); xw.truncateToApprox(wp); yw = ldexp(yw + lam, -2); yw.truncateToApprox(wp); zw = ldexp(zw + lam, -2); zw.truncateToApprox(wp); Float A = (xw + yw + Float(3) * zw) / Float(5); Float dx = (A - xw) / A; Float dy = (A - yw) / A; Float dz = (A - zw) / A; int64_t dx_b = dx.isZero() ? INT64_MIN : dx.exponent() + static_cast(dx.mantissa().bitLength()); int64_t dy_b = dy.isZero() ? INT64_MIN : dy.exponent() + static_cast(dy.mantissa().bitLength()); int64_t dz_b = dz.isZero() ? INT64_MIN : dz.exponent() + static_cast(dz.mantissa().bitLength()); if (std::max({dx_b, dy_b, dz_b}) < -(wp + 5)) break; } Float A = (xw + yw + Float(3) * zw) / Float(5); Float dx = (A - xw) / A; Float dy = (A - yw) / A; Float dz = (A - zw) / A; Float ea = dx * dy; Float eb = dz * dz; Float ec = ea - eb; Float ed = ea - Float(6) * eb; Float ee = ed + ec + ec; // C₁=3/14, C₂=1/6, C₃=9/22, C₄=3/26, C₅=9/88, C₆=9/52 Float poly = Float(1) + ed * (Float(-3) / Float(14) + Float(9) / Float(88) * ed - Float(9) / Float(52) * dz * ee) + dz * (Float(1) / Float(6) * ee + dz * (Float(-9) / Float(22) * ec + dz * Float(3) / Float(26) * ea)); Float result = Float(3) * sigma + fac * poly / (A * sqrt(A, wp)); finalizeResult(result, eff, precision); return result; } Float carlsonRD(const Float& x, const Float& y, const Float& z, int precision) { if (x.isNaN() || y.isNaN() || z.isNaN()) return Float::nan(); if (x.isNegative() || y.isNegative() || !z.isPositive()) return Float::nan(); int eff = std::min({x.effectiveBits(), y.effectiveBits(), z.effectiveBits()}); return carlsonRD_core(Float(x), Float(y), Float(z), eff, precision); } Float carlsonRD(Float&& x, Float&& y, Float&& z, int precision) { if (x.isNaN() || y.isNaN() || z.isNaN()) return Float::nan(); if (x.isNegative() || y.isNegative() || !z.isPositive()) return Float::nan(); int eff = std::min({x.effectiveBits(), y.effectiveBits(), z.effectiveBits()}); return carlsonRD_core(std::move(x), std::move(y), std::move(z), eff, precision); } // R_J(x, y, z, p) = (3/2)∫₀^∞ dt / [(t+p)√((t+x)(t+y)(t+z))] // x, y, z ≥ 0 (高々1つが 0), p > 0 static Float carlsonRJ_core(Float xw, Float yw, Float zw, Float pw, int eff, int precision) { int wp = precision + 20; xw.truncateToApprox(wp); yw.truncateToApprox(wp); zw.truncateToApprox(wp); pw.truncateToApprox(wp); Float sigma(0); Float fac(1); for (int iter = 0; iter < 200; iter++) { Float sqx = sqrt(xw, wp); Float sqy = sqrt(yw, wp); Float sqz = sqrt(zw, wp); Float sqp = sqrt(pw, wp); Float lam = sqx * sqy + sqy * sqz + sqz * sqx; Float alpha = pw * (sqx + sqy + sqz) + sqx * sqy * sqz; alpha = alpha * alpha; alpha.truncateToApprox(wp); Float beta = pw * (pw + lam) * (pw + lam); beta.truncateToApprox(wp); sigma = sigma + fac * carlsonRC(alpha, beta, wp); sigma.truncateToApprox(wp); fac = ldexp(fac, -2); xw = ldexp(xw + lam, -2); xw.truncateToApprox(wp); yw = ldexp(yw + lam, -2); yw.truncateToApprox(wp); zw = ldexp(zw + lam, -2); zw.truncateToApprox(wp); pw = ldexp(pw + lam, -2); pw.truncateToApprox(wp); Float A = (xw + yw + zw + pw + pw) / Float(5); Float dx = (A - xw) / A; Float dy = (A - yw) / A; Float dz = (A - zw) / A; Float dp = (A - pw) / A; int64_t dx_b = dx.isZero() ? INT64_MIN : dx.exponent() + static_cast(dx.mantissa().bitLength()); int64_t dy_b = dy.isZero() ? INT64_MIN : dy.exponent() + static_cast(dy.mantissa().bitLength()); int64_t dz_b = dz.isZero() ? INT64_MIN : dz.exponent() + static_cast(dz.mantissa().bitLength()); int64_t dp_b = dp.isZero() ? INT64_MIN : dp.exponent() + static_cast(dp.mantissa().bitLength()); if (std::max({dx_b, dy_b, dz_b, dp_b}) < -(wp + 5)) break; } Float A = (xw + yw + zw + pw + pw) / Float(5); Float dx = (A - xw) / A; Float dy = (A - yw) / A; Float dz = (A - zw) / A; Float dp = (A - pw) / A; Float ea = dx * (dy + dz) + dy * dz; Float eb = dx * dy * dz; Float ec = dp * dp; Float ed = ea - Float(3) * ec; Float ee = eb + ldexp(dp * (ea - ec), 1); // C₁=3/14, C₂=1/3, C₃=3/22, C₄=3/26 Float poly = Float(1) + ed * (Float(-3) / Float(14) + Float(9) / Float(88) * ed - Float(9) / Float(52) * ee) + eb * (Float(1) / Float(6) + dp * (Float(-6) / Float(22) + dp * Float(3) / Float(26))) + dp * ea * (Float(1) / Float(3) - dp * Float(3) / Float(22)) - Float(1) / Float(3) * dp * ec; Float result = Float(3) * sigma + fac * poly / (A * sqrt(A, wp)); finalizeResult(result, eff, precision); return result; } Float carlsonRJ(const Float& x, const Float& y, const Float& z, const Float& p, int precision) { if (x.isNaN() || y.isNaN() || z.isNaN() || p.isNaN()) return Float::nan(); if (x.isNegative() || y.isNegative() || z.isNegative() || !p.isPositive()) return Float::nan(); int eff = std::min({x.effectiveBits(), y.effectiveBits(), z.effectiveBits(), p.effectiveBits()}); return carlsonRJ_core(Float(x), Float(y), Float(z), Float(p), eff, precision); } Float carlsonRJ(Float&& x, Float&& y, Float&& z, Float&& p, int precision) { if (x.isNaN() || y.isNaN() || z.isNaN() || p.isNaN()) return Float::nan(); if (x.isNegative() || y.isNegative() || z.isNegative() || !p.isPositive()) return Float::nan(); int eff = std::min({x.effectiveBits(), y.effectiveBits(), z.effectiveBits(), p.effectiveBits()}); return carlsonRJ_core(std::move(x), std::move(y), std::move(z), std::move(p), eff, precision); } //============================================================================= // 楕円積分 — Legendre 形式 //============================================================================= // K(k) = R_F(0, 1-k², 1) — 第1種完全楕円積分 Float ellipticK(const Float& k, int precision) { if (k.isNaN()) return Float::nan(); int eff_k = k.effectiveBits(); int wp = precision + 15; Float k2 = k * k; k2.truncateToApprox(wp); if (k2 == Float(1)) return Float::positiveInfinity(); // K(1) = ∞ Float result = carlsonRF(Float(0), Float(1) - k2, Float(1), wp); finalizeResult(result, eff_k, precision); return result; } Float ellipticK(Float&& k, int precision) { if (k.isNaN()) return Float::nan(); int eff_k = k.effectiveBits(); int wp = precision + 15; Float k2 = k * k; k2.truncateToApprox(wp); if (k2 == Float(1)) return Float::positiveInfinity(); Float result = carlsonRF(Float(0), Float(1) - std::move(k2), Float(1), wp); finalizeResult(result, eff_k, precision); return result; } // E(k) = R_F(0, 1-k², 1) - (k²/3)·R_D(0, 1-k², 1) — 第2種完全楕円積分 Float ellipticE(const Float& k, int precision) { if (k.isNaN()) return Float::nan(); { Float k2t = k * k; if (k2t == Float(1)) return Float(1); // E(±1) = 1 } int eff_k = k.effectiveBits(); int wp = precision + 15; Float k2 = k * k; k2.truncateToApprox(wp); Float kp2 = Float(1) - k2; kp2.truncateToApprox(wp); Float rf = carlsonRF(Float(0), kp2, Float(1), wp); Float rd = carlsonRD(Float(0), kp2, Float(1), wp); Float result = rf - k2 * rd / Float(3); finalizeResult(result, eff_k, precision); return result; } Float ellipticE(Float&& k, int precision) { if (k.isNaN()) return Float::nan(); { Float k2t = k * k; if (k2t == Float(1)) return Float(1); } int eff_k = k.effectiveBits(); int wp = precision + 15; Float k2 = k * k; k2.truncateToApprox(wp); Float kp2 = Float(1) - k2; kp2.truncateToApprox(wp); Float rf = carlsonRF(Float(0), kp2, Float(1), wp); Float rd = carlsonRD(Float(0), std::move(kp2), Float(1), wp); Float result = rf - std::move(k2) * rd / Float(3); finalizeResult(result, eff_k, precision); return result; } // Π(n, k) = R_F(0, 1-k², 1) + (n/3)·R_J(0, 1-k², 1, 1-n) — 第3種完全楕円積分 Float ellipticPi(const Float& n, const Float& k, int precision) { if (n.isNaN() || k.isNaN()) return Float::nan(); int eff = std::min(n.effectiveBits(), k.effectiveBits()); int wp = precision + 15; Float k2 = k * k; k2.truncateToApprox(wp); Float kp2 = Float(1) - k2; kp2.truncateToApprox(wp); Float rf = carlsonRF(Float(0), kp2, Float(1), wp); Float rj = carlsonRJ(Float(0), kp2, Float(1), Float(1) - n, wp); Float result = rf + n * rj / Float(3); finalizeResult(result, eff, precision); return result; } Float ellipticPi(Float&& n, Float&& k, int precision) { if (n.isNaN() || k.isNaN()) return Float::nan(); int eff = std::min(n.effectiveBits(), k.effectiveBits()); int wp = precision + 15; Float k2 = k * k; k2.truncateToApprox(wp); Float kp2 = Float(1) - k2; kp2.truncateToApprox(wp); Float rf = carlsonRF(Float(0), kp2, Float(1), wp); Float rj = carlsonRJ(Float(0), std::move(kp2), Float(1), Float(1) - n, wp); Float result = rf + std::move(n) * rj / Float(3); finalizeResult(result, eff, precision); return result; } // F(φ, k) = sin(φ)·R_F(cos²φ, 1-k²sin²φ, 1) — 第1種不完全楕円積分 Float ellipticF(const Float& phi, const Float& k, int precision) { if (phi.isNaN() || k.isNaN()) return Float::nan(); int eff = std::min(phi.effectiveBits(), k.effectiveBits()); int wp = precision + 15; Float s = sin(phi, wp); Float c = cos(phi, wp); Float k2 = k * k; k2.truncateToApprox(wp); Float result = s * carlsonRF(c * c, Float(1) - k2 * s * s, Float(1), wp); finalizeResult(result, eff, precision); return result; } Float ellipticF(Float&& phi, Float&& k, int precision) { if (phi.isNaN() || k.isNaN()) return Float::nan(); int eff = std::min(phi.effectiveBits(), k.effectiveBits()); int wp = precision + 15; Float s = sin(phi, wp); Float c = cos(std::move(phi), wp); Float k2 = k * k; k2.truncateToApprox(wp); Float result = s * carlsonRF(c * c, Float(1) - std::move(k2) * s * s, Float(1), wp); finalizeResult(result, eff, precision); return result; } // E(φ, k) = sin(φ)·R_F(...) - (k²/3)sin³φ·R_D(...) — 第2種不完全楕円積分 Float ellipticE(const Float& phi, const Float& k, int precision) { if (phi.isNaN() || k.isNaN()) return Float::nan(); int eff = std::min(phi.effectiveBits(), k.effectiveBits()); int wp = precision + 15; Float s = sin(phi, wp); Float c = cos(phi, wp); Float k2 = k * k; k2.truncateToApprox(wp); Float c2 = c * c; c2.truncateToApprox(wp); Float s2 = s * s; s2.truncateToApprox(wp); Float w = Float(1) - k2 * s2; w.truncateToApprox(wp); Float rf = carlsonRF(c2, w, Float(1), wp); Float rd = carlsonRD(c2, w, Float(1), wp); Float result = s * rf - k2 * s * s2 * rd / Float(3); finalizeResult(result, eff, precision); return result; } Float ellipticE(Float&& phi, Float&& k, int precision) { if (phi.isNaN() || k.isNaN()) return Float::nan(); int eff = std::min(phi.effectiveBits(), k.effectiveBits()); int wp = precision + 15; Float s = sin(phi, wp); Float c = cos(std::move(phi), wp); Float k2 = k * k; k2.truncateToApprox(wp); Float c2 = c * c; c2.truncateToApprox(wp); Float s2 = s * s; s2.truncateToApprox(wp); Float w = Float(1) - k2 * s2; w.truncateToApprox(wp); Float rf = carlsonRF(c2, w, Float(1), wp); Float rd = carlsonRD(std::move(c2), std::move(w), Float(1), wp); Float result = s * rf - std::move(k2) * s * s2 * rd / Float(3); finalizeResult(result, eff, precision); return result; } // Π(n, φ, k) — 第3種不完全楕円積分 Float ellipticPi(const Float& n, const Float& phi, const Float& k, int precision) { if (n.isNaN() || phi.isNaN() || k.isNaN()) return Float::nan(); int eff = std::min({n.effectiveBits(), phi.effectiveBits(), k.effectiveBits()}); int wp = precision + 15; Float s = sin(phi, wp); Float c = cos(phi, wp); Float k2 = k * k; k2.truncateToApprox(wp); Float s2 = s * s; s2.truncateToApprox(wp); Float c2 = c * c; c2.truncateToApprox(wp); Float w = Float(1) - k2 * s2; w.truncateToApprox(wp); Float rf = carlsonRF(c2, w, Float(1), wp); Float rj = carlsonRJ(c2, w, Float(1), Float(1) - n * s2, wp); Float result = s * rf + n * s * s2 * rj / Float(3); finalizeResult(result, eff, precision); return result; } Float ellipticPi(Float&& n, Float&& phi, Float&& k, int precision) { if (n.isNaN() || phi.isNaN() || k.isNaN()) return Float::nan(); int eff = std::min({n.effectiveBits(), phi.effectiveBits(), k.effectiveBits()}); int wp = precision + 15; Float s = sin(phi, wp); Float c = cos(std::move(phi), wp); Float k2 = k * k; k2.truncateToApprox(wp); Float s2 = s * s; s2.truncateToApprox(wp); Float c2 = c * c; c2.truncateToApprox(wp); Float w = Float(1) - k2 * s2; w.truncateToApprox(wp); Float rf = carlsonRF(c2, w, Float(1), wp); Float rj = carlsonRJ(std::move(c2), std::move(w), Float(1), Float(1) - n * s2, wp); Float result = s * rf + std::move(n) * s * s2 * rj / Float(3); finalizeResult(result, eff, precision); return result; } //============================================================================= // Jacobi 楕円関数 — AGM 下降 Landen 変換 //============================================================================= // 共通ヘルパー: AGM + 逆変換で振幅 am(u, k) = φ₀ を計算 static Float jacobiAmplitude(const Float& u, const Float& k, int wp) { // k = 0: am(u, 0) = u if (k.isZero()) { Float r = u; r.truncateToApprox(wp); return r; } // k = 1: am(u, 1) = gd(u) = 2·arctan(tanh(u/2)) if (k == Float(1)) { Float r = ldexp(atan(tanh(ldexp(u, -1), wp), wp), 1); r.truncateToApprox(wp); return r; } Float kp = sqrt(Float(1) - k * k, wp); // k' = √(1-k²) // AGM 前進反復: a₀=1, b₀=k', c₀=k std::vector a_seq, c_seq; Float a(1), b = kp, c = k; a.truncateToApprox(wp); b.truncateToApprox(wp); c.truncateToApprox(wp); a_seq.push_back(a); c_seq.push_back(c); for (int n = 0; n < 200; n++) { Float a_new = ldexp(a + b, -1); a_new.truncateToApprox(wp); Float c_new = ldexp(a - b, -1); c_new.truncateToApprox(wp); Float b_new = sqrt(a * b, wp); a = a_new; b = b_new; c = c_new; a_seq.push_back(a); c_seq.push_back(c); if (c.isZero()) break; int64_t c_bits = c.exponent() + static_cast(c.mantissa().bitLength()); if (c_bits < -(wp + 5)) break; } int N = static_cast(a_seq.size()) - 1; // φ_N = 2^N · a_N · u Float phi = u * a_seq[N]; for (int i = 0; i < N; i++) { phi = ldexp(phi, 1); } phi.truncateToApprox(wp); // 逆変換: φ_{n-1} = (φ_n + arcsin(c_n/a_n · sin(φ_n))) / 2 for (int n = N; n >= 1; n--) { Float sinphi = sin(phi, wp); Float arg = c_seq[n] * sinphi / a_seq[n]; arg.truncateToApprox(wp); phi = ldexp(phi + asin(arg, wp), -1); phi.truncateToApprox(wp); } return phi; } Float jacobiSn(const Float& u, const Float& k, int precision) { if (u.isNaN() || k.isNaN()) return Float::nan(); if (u.isZero()) return Float(0); int eff = std::min(u.effectiveBits(), k.effectiveBits()); int wp = precision + 20; Float phi = jacobiAmplitude(u, k, wp); Float result = sin(phi, wp); finalizeResult(result, eff, precision); return result; } Float jacobiSn(Float&& u, Float&& k, int precision) { if (u.isNaN() || k.isNaN()) return Float::nan(); if (u.isZero()) return Float(0); int eff = std::min(u.effectiveBits(), k.effectiveBits()); int wp = precision + 20; Float phi = jacobiAmplitude(u, std::move(k), wp); Float result = sin(std::move(phi), wp); finalizeResult(result, eff, precision); return result; } Float jacobiCn(const Float& u, const Float& k, int precision) { if (u.isNaN() || k.isNaN()) return Float::nan(); if (u.isZero()) return Float(1); int eff = std::min(u.effectiveBits(), k.effectiveBits()); int wp = precision + 20; Float phi = jacobiAmplitude(u, k, wp); Float result = cos(phi, wp); finalizeResult(result, eff, precision); return result; } Float jacobiCn(Float&& u, Float&& k, int precision) { if (u.isNaN() || k.isNaN()) return Float::nan(); if (u.isZero()) return Float(1); int eff = std::min(u.effectiveBits(), k.effectiveBits()); int wp = precision + 20; Float phi = jacobiAmplitude(u, std::move(k), wp); Float result = cos(std::move(phi), wp); finalizeResult(result, eff, precision); return result; } Float jacobiDn(const Float& u, const Float& k, int precision) { if (u.isNaN() || k.isNaN()) return Float::nan(); if (u.isZero()) return Float(1); int eff = std::min(u.effectiveBits(), k.effectiveBits()); int wp = precision + 20; Float phi = jacobiAmplitude(u, k, wp); Float sn = sin(phi, wp); Float k2 = k * k; k2.truncateToApprox(wp); Float result = sqrt(Float(1) - k2 * sn * sn, wp); finalizeResult(result, eff, precision); return result; } Float jacobiDn(Float&& u, Float&& k, int precision) { if (u.isNaN() || k.isNaN()) return Float::nan(); if (u.isZero()) return Float(1); int eff = std::min(u.effectiveBits(), k.effectiveBits()); int wp = precision + 20; Float phi = jacobiAmplitude(u, k, wp); Float sn = sin(std::move(phi), wp); Float k2 = k * k; k2.truncateToApprox(wp); Float result = sqrt(Float(1) - std::move(k2) * sn * sn, wp); finalizeResult(result, eff, precision); return result; } //============================================================================= // ゼータ関数 ζ(s) — Borwein アルゴリズム //============================================================================= // Borwein (1995) の加速交代級数: // ζ(s) = [1/(1-2^{1-s})] · [(-1)/(d_n)] · Σ_{k=0}^{n-1} (-1)^k (d_k - d_n) / (k+1)^s // ここで d_k = n · Σ_{i=0}^{k} (n+i-1)! · 4^i / ((n-i)! · (2i)!) Float zeta(const Float& s, int precision) { if (s.isNaN()) return Float::nan(); if (s.isInfinity()) { if (s.isNegative()) return Float::nan(); return Float(1); // ζ(+∞) = 1 } int eff_s = s.effectiveBits(); int wp = precision + 20; // s = 1 は極 Float one(1); if (s == one) return Float::nan(); // 偶数の負の整数: ζ(-2n) = 0 (自明なゼロ) if (s.isNegative() && s.isInteger()) { double sv = s.toDouble(); int si = static_cast(sv); if (si % 2 == 0 && si < 0) return Float(0); } // s < 0: 反射公式 ζ(s) = 2^s · π^{s-1} · sin(πs/2) · Γ(1-s) · ζ(1-s) if (s.isNegative()) { Float one_minus_s = Float(1) - s; Float z_1ms = zeta(one_minus_s, wp); Float g_1ms = gamma(one_minus_s, wp); Float two_pow_s = pow(Float(2), s, wp); Float pi_pow_sm1 = pow(Float::pi(wp), s - Float(1), wp); Float sin_term = sinPi(ldexp(s, -1), wp); Float result = two_pow_s * pi_pow_sm1 * sin_term * g_1ms * z_1ms; finalizeResult(result, eff_s, precision); return result; } // s > 0, s ≠ 1: Euler 加速による Dirichlet eta 関数経由の計算 // η(s) = (1-2^{1-s})·ζ(s) = Σ (-1)^{k-1}/k^s の Euler 変換で計算 // η(s) の部分和: S_K = Σ_{k=1}^{K} (-1)^{k-1}/k^s // Euler 変換: E_n = Σ_{k=0}^{n} C(n,k)/2^n · S_{k+1} // 項数 int n = static_cast(wp * 1.05) + 5; // 部分和を計算しながら Euler 変換 Float eta(0); Float s_copy = s; s_copy.truncateToApprox(wp); // S_k = 部分和, w_k = C(n,k)/2^n (Euler 重み) Float partial_sum(0); Float weighted_sum(0); // w_0 = 1/2^n, w_{k+1} = w_k · (n-k)/(k+1) // 2^n は大きすぎるので、対数スケールで扱うか、直接 Float で計算 Float w = pow(Float(2), Float(-n), wp); // w_0 = 2^{-n} w.truncateToApprox(wp); for (int k = 0; k <= n; k++) { // 部分和更新: S_{k+1} = S_k + (-1)^k / (k+1)^s Float kp1 = Float(k + 1); Float term_k = Float(1) / pow(kp1, s_copy, wp); if (k % 2 == 0) { partial_sum = partial_sum + term_k; } else { partial_sum = partial_sum - term_k; } partial_sum.truncateToApprox(wp); // 重み付き和 weighted_sum = weighted_sum + w * partial_sum; weighted_sum.truncateToApprox(wp); // 重み更新 if (k < n) { w = w * Float(n - k) / Float(k + 1); w.truncateToApprox(wp); } } eta = weighted_sum; // ζ(s) = η(s) / (1 - 2^{1-s}) Float two_pow_1ms = pow(Float(2), Float(1) - s_copy, wp); Float denom = Float(1) - two_pow_1ms; if (denom.isZero()) { // s = 1 の場合 (既にチェック済みだが念のため) return Float::nan(); } Float result = eta / denom; finalizeResult(result, eff_s, precision); return result; } Float zeta(Float&& s, int precision) { return zeta(static_cast(s), precision); } //============================================================================= // Hurwitz ゼータ関数 ζ(s, a) — Euler-Maclaurin 公式 //============================================================================= // ζ(s, a) = Σ_{k=0}^{N-1} (a+k)^{-s} + (a+N)^{1-s}/(s-1) + (a+N)^{-s}/2 // + Σ_{j=1}^{M} B_{2j}/(2j)! · s(s+1)···(s+2j-2) · (a+N)^{-(s+2j-1)} // // Bernoulli 数 B_{2j} は Float 精度で再帰的に計算: // B(2m) = -1/(2m+1) · [1 + (2m+1)·(-1/2) + Σ_{j=1}^{m-1} C(2m+1,2j)·B(2j)] Float hurwitzZeta(const Float& s, const Float& a, int precision) { if (s.isNaN() || a.isNaN()) return Float::nan(); if (a.isZero() || a.isNegative()) return Float::nan(); // a > 0 が必要 int eff = std::min(s.effectiveBits(), a.effectiveBits()); // s = 1 は極 Float one(1); if (s == one) return Float::nan(); // a = 1 → Riemann ζ(s) に委譲 if (a == one) return zeta(s, precision); int wp = precision + 40; Float sw = s; sw.truncateToApprox(wp); Float aw = a; aw.truncateToApprox(wp); // 引数シフト: a+N が十分大きくなるように N を決定 // 目標: a+N ≈ wp/3 (decimal digits 換算で十分な大きさ) int target_aN = wp / 3 + 5; int N = std::max(1, target_aN - static_cast(aw.toDouble())); // 直接和: Σ_{k=0}^{N-1} (a+k)^{-s} Float direct_sum(0); for (int k = 0; k < N; k++) { Float term = pow(aw + Float(k), -sw, wp); term.truncateToApprox(wp); direct_sum = direct_sum + term; direct_sum.truncateToApprox(wp); } Float aN = aw + Float(N); aN.truncateToApprox(wp); // 積分項: (a+N)^{1-s} / (s-1) Float integral_term = pow(aN, Float(1) - sw, wp) / (sw - Float(1)); integral_term.truncateToApprox(wp); // 中点補正: (1/2)·(a+N)^{-s} Float midpoint = ldexp(Float(1) / pow(aN, sw, wp), -1); midpoint.truncateToApprox(wp); Float sum = direct_sum + integral_term + midpoint; sum.truncateToApprox(wp); // Bernoulli 補正項数: M ≈ π·(a+N) で最適だが、収束判定で打ち切る int M = std::min(wp / 5 + 5, target_aN); // Bernoulli 数 B_2, B_4, ..., B_{2M} を Float で計算 std::vector B2j(M); int bp = wp + 50; // ガードビット追加 for (int mi = 1; mi <= M; mi++) { int K = 2 * mi; int K1 = K + 1; Float sum_b(1); // C(K1,0)·B(0) = 1 sum_b = sum_b + ldexp(Float(-K1), -1); // C(K1,1)·B(1) = K1·(-1/2) sum_b.truncateToApprox(bp); Float binom(1); // 二項係数 C(K1, 2j) を逐次計算 for (int j = 1; j < mi; j++) { binom = binom * Float(K1 - 2 * j + 2) / Float(2 * j - 1); binom = binom * Float(K1 - 2 * j + 1) / Float(2 * j); binom.truncateToApprox(bp); sum_b = sum_b + binom * B2j[j - 1]; sum_b.truncateToApprox(bp); } sum_b = sum_b / Float(-K1); sum_b.truncateToApprox(wp); B2j[mi - 1] = sum_b; } // Euler-Maclaurin Bernoulli 補正 Float rising = sw; // 上昇階乗: s, s(s+1)(s+2), ... Float aN_inv = Float(1) / aN; aN_inv.truncateToApprox(wp); Float aN_pow = pow(aN, -sw, wp) * aN_inv; // (a+N)^{-(s+1)} aN_pow.truncateToApprox(wp); for (int j = 0; j < M; j++) { // (2(j+1))! を計算 Float fact(1); for (int i = 1; i <= 2 * (j + 1); i++) { fact = fact * Float(i); } fact.truncateToApprox(wp); Float correction = B2j[j] / fact * rising * aN_pow; correction.truncateToApprox(wp); sum = sum + correction; sum.truncateToApprox(wp); // 収束判定 if (j >= 3 && !correction.isZero()) { auto ct = correction.exponent() + static_cast(correction.mantissa().bitLength()); auto st = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (st - ct > wp + 5) break; // 発散兆候: 補正項が増大し始めたら打ち切り if (j >= 5) { Float prev_corr = B2j[j - 1]; if (!prev_corr.isZero()) { auto pt = prev_corr.exponent() + static_cast(prev_corr.mantissa().bitLength()); if (ct > pt + 5) break; // 項が明確に増大 } } } // 上昇階乗: (s+2j-1)(s+2j) を乗算 rising = rising * (sw + Float(2 * j + 1)) * (sw + Float(2 * (j + 1))); rising.truncateToApprox(wp); // (a+N) 冪: (a+N)^{-2} を乗算 aN_pow = aN_pow * aN_inv * aN_inv; aN_pow.truncateToApprox(wp); } finalizeResult(sum, eff, precision); return sum; } Float hurwitzZeta(Float&& s, Float&& a, int precision) { return hurwitzZeta(static_cast(s), static_cast(a), precision); } //============================================================================= // Dirichlet η 関数: η(s) = (1 - 2^{1-s}) · ζ(s) //============================================================================= Float dirichletEta(const Float& s, int precision) { if (s.isNaN()) return Float::nan(); // η(1) = ln(2) (ζ(1) の極を回避) Float one(1); if (s == one) { return log(Float(2), precision); } int eff_s = s.effectiveBits(); int wp = precision + 20; Float sw = s; sw.truncateToApprox(wp); Float factor = Float(1) - pow(Float(2), Float(1) - sw, wp); factor.truncateToApprox(wp); Float z = zeta(sw, wp); Float result = factor * z; finalizeResult(result, eff_s, precision); return result; } Float dirichletEta(Float&& s, int precision) { if (s.isNaN()) return Float::nan(); Float one(1); if (s == one) { return log(Float(2), precision); } int eff_s = s.effectiveBits(); int wp = precision + 20; s.truncateToApprox(wp); Float factor = Float(1) - pow(Float(2), Float(1) - s, wp); factor.truncateToApprox(wp); Float z = zeta(std::move(s), wp); Float result = std::move(factor) * z; finalizeResult(result, eff_s, precision); return result; } //============================================================================= // 指数積分 Ei(x) と対数積分 li(x) //============================================================================= // Ei(x) = -PV∫_{-x}^{∞} e^{-t}/t dt = γ + ln|x| + Σ_{n=1}^{∞} x^n/(n·n!) // x > 0 の場合の収束級数 // 本体: x を値で受け取る (move 済み前提) static Float expint_core(Float x, int eff_x, int precision) { int wp = precision + 20; x.truncateToApprox(wp); // Ei(x) = γ + ln|x| + Σ_{n=1}^{∞} x^n / (n · n!) Float euler_gamma = Float::euler(wp); Float ln_abs_x = log(abs(x), wp); Float sum(0); Float term = x; // n=1: x^1 / (1·1!) = x sum = term; for (int n = 2; n < 10 * wp; n++) { term = term * x / Float(n); term.truncateToApprox(wp); Float contribution = term / Float(n); sum = sum + contribution; sum.truncateToApprox(wp); // 収束チェック if (n >= 5 && contribution.isZero()) break; if (n >= 5) { auto c_bits = contribution.exponent() + static_cast(contribution.mantissa().bitLength()); auto s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - c_bits > wp + 5) break; } } Float result = euler_gamma + ln_abs_x + sum; finalizeResult(result, eff_x, precision); return result; } Float expint(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float(0); return Float::positiveInfinity(); } if (x.isZero()) return Float::negativeInfinity(); return expint_core(Float(x), x.effectiveBits(), precision); } Float expint(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float(0); return Float::positiveInfinity(); } if (x.isZero()) return Float::negativeInfinity(); int eff = x.effectiveBits(); return expint_core(std::move(x), eff, precision); } // li(x) = Ei(ln(x)) — 対数積分 Float li(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::positiveInfinity(); if (x.isZero() || x.isNegative()) return Float::nan(); // li(1) = -∞ (Ei(0) = -∞) if (x == Float(1)) return Float::negativeInfinity(); int eff_x = x.effectiveBits(); int wp = precision + 10; Float ln_x = log(x, wp); Float result = expint(ln_x, wp); finalizeResult(result, eff_x, precision); return result; } Float li(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float::positiveInfinity(); if (x.isZero() || x.isNegative()) return Float::nan(); if (x == Float(1)) return Float::negativeInfinity(); int eff_x = x.effectiveBits(); int wp = precision + 10; Float ln_x = log(std::move(x), wp); Float result = expint(std::move(ln_x), wp); finalizeResult(result, eff_x, precision); return result; } //============================================================================= // ディロガリズム Li₂(x) = -∫₀ˣ ln(1-t)/t dt = Σ_{n=1}^{∞} x^n/n² //============================================================================= // 本体: x を値で受け取る (move 済み前提) static Float dilog_core(Float x, int eff_x, int precision) { int wp = precision + 20; x.truncateToApprox(wp); // 特殊値 if (x.isZero()) return Float(0); // Li₂(1) = π²/6 if (x == Float(1)) { Float pi = Float::pi(wp); Float result = pi * pi / Float(6); finalizeResult(result, eff_x, precision); return result; } // Li₂(-1) = -π²/12 if (x == Float(-1)) { Float pi = Float::pi(wp); Float result = -(pi * pi) / Float(12); finalizeResult(result, eff_x, precision); return result; } // |x| > 1: 変換公式 Li₂(x) = -Li₂(1/x) - π²/6 + ln²(-x)/2 ... (複素数が必要) // |x| > 1 で実数範囲では注意が必要 // ここでは |x| <= 1 の範囲に限定 (実数範囲での主要な使用域) double x_approx = std::abs(x.toDouble()); // |x| > 0.5: 変換 Li₂(x) = -Li₂(1-x) + π²/6 - ln(x)·ln(1-x) if (x_approx > 0.5 && x_approx <= 1.0) { Float one_minus_x = Float(1) - x; one_minus_x.truncateToApprox(wp); Float li2_1mx = dilog(one_minus_x, wp); Float pi = Float::pi(wp); Float ln_x = log(x, wp); Float ln_1mx = log(one_minus_x, wp); Float result = -li2_1mx + pi * pi / Float(6) - ln_x * ln_1mx; finalizeResult(result, eff_x, precision); return result; } // |x| <= 0.5: 直接 Taylor 級数 Li₂(x) = Σ_{n=1}^{∞} x^n / n² Float sum(0); Float x_power = x; // x^1 for (int n = 1; n < 10 * wp; n++) { Float term = x_power / Float(static_cast(n) * n); sum = sum + term; sum.truncateToApprox(wp); // 収束チェック if (n >= 5 && term.isZero()) break; if (n >= 5) { auto t_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - t_bits > wp + 5) break; } x_power = x_power * x; x_power.truncateToApprox(wp); } finalizeResult(sum, eff_x, precision); return sum; } Float dilog(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); return dilog_core(Float(x), x.effectiveBits(), precision); } Float dilog(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); int eff = x.effectiveBits(); return dilog_core(std::move(x), eff, precision); } //============================================================================= // ベッセル関数 J_n(x), Y_n(x) //============================================================================= // J_n(x) = Σ_{m=0}^{∞} (-1)^m / (m! · (m+n)!) · (x/2)^{2m+n} // Y_n(x) = [J_n(x)·cos(nπ) - J_{-n}(x)] / sin(nπ) (n が整数の場合は極限) Float besselJ(int n, const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float(0); // J_n(±∞) → 0 (振動減衰) int eff_x = x.effectiveBits(); int wp = precision + 20; Float xw = x; xw.truncateToApprox(wp); // J_n(0) = δ_{n,0} if (x.isZero()) { return (n == 0) ? Float(1) : Float(0); } // 負の次数: J_{-n}(x) = (-1)^n · J_n(x) bool negate = false; int abs_n = n; if (n < 0) { abs_n = -n; if (abs_n % 2 != 0) negate = true; } // Taylor 級数: J_n(x) = (x/2)^n · Σ_{m=0}^{∞} (-1)^m · (x/2)^{2m} / (m! · (m+n)!) Float x_half = ldexp(xw, -1); x_half.truncateToApprox(wp); Float x_half_sq = x_half * x_half; x_half_sq.truncateToApprox(wp); Float neg_x_half_sq = -x_half_sq; // (x/2)^n の計算 Float x_half_n(1); for (int i = 0; i < abs_n; i++) { FloatOps::mul(x_half_n, x_half, x_half_n); } x_half_n.truncateToApprox(wp); // 級数: Σ (-1)^m · (x/2)^{2m} / (m! · (m+n)!) // term_0 = 1/n! Float term(1); for (int i = 1; i <= abs_n; i++) { FloatOps::div(term, Float(i), term); } term.truncateToApprox(wp); Float sum = term; for (int m = 1; m < 10 * wp; m++) { // term_m = term_{m-1} · (-(x/2)²) / (m · (m+n)) FloatOps::mul(term, neg_x_half_sq, term); FloatOps::div(term, Float(static_cast(m) * (m + abs_n)), term); term.truncateToApprox(wp); FloatOps::add(sum, term, sum); sum.truncateToApprox(wp); if (m >= 3 && term.isZero()) break; if (m >= 3) { auto t_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - t_bits > wp + 5) break; } } Float result = x_half_n * sum; if (negate) result = -result; finalizeResult(result, eff_x, precision); return result; } Float besselJ(int n, Float&& x, int precision) { return besselJ(n, static_cast(x), precision); } Float besselY(int n, const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float(0); // Y_n(±∞) → 0 if (x.isZero()) return Float::negativeInfinity(); // Y_n(0) = -∞ if (x.isNegative()) return Float::nan(); // Y_n(x<0) は実数でない int eff_x = x.effectiveBits(); int wp = precision + 30; Float xw = x; xw.truncateToApprox(wp); int abs_n = (n >= 0) ? n : -n; // 正確な公式 (Abramowitz & Stegun 9.1.11): // Y_n(x) = (2/π)·J_n(x)·(γ + ln(x/2)) // - (1/π)·Σ_{k=0}^{n-1} (n-k-1)!/k! · (x/2)^{2k-n} // - (1/π)·Σ_{k=0}^{∞} (-1)^k·(ψ(k+1)+ψ(k+n+1))/(k!·(k+n)!) · (x/2)^{2k+n} // ψ(m+1) = -γ + H_m (H_m = Σ 1/j) // → 合わせると: // Y_n(x) = (2/π)·J_n(x)·ln(x/2) // - (1/π)·(x/2)^{-n}·Σ_{k=0}^{n-1} (n-k-1)!/k! · (x²/4)^k // - (1/π)·(x/2)^n·Σ_{k=0}^{∞} (-1)^k·(H_k+H_{k+n})/(k!·(k+n)!)·(x²/4)^k Float pi_val = Float::pi(wp); Float euler_gamma = Float::euler(wp); Float two_over_pi = Float(2) / pi_val; Float one_over_pi = Float(1) / pi_val; Float x_half = ldexp(xw, -1); x_half.truncateToApprox(wp); Float ln_x_half = log(x_half, wp); Float x_half_sq = x_half * x_half; x_half_sq.truncateToApprox(wp); Float jn = besselJ(abs_n, xw, wp); // Part A: (2/π) · J_n(x) · (γ + ln(x/2)) [DLMF 10.8.1] Float result = two_over_pi * jn * (euler_gamma + ln_x_half); result.truncateToApprox(wp); // Part B: -(1/π)·(x/2)^{-n}·Σ_{k=0}^{n-1} (n-k-1)!/k! · (x²/4)^k if (abs_n > 0) { Float x_half_neg_n(1); for (int i = 0; i < abs_n; i++) { FloatOps::div(x_half_neg_n, x_half, x_half_neg_n); } x_half_neg_n.truncateToApprox(wp); // k=0 の項: (n-1)!/0! = (n-1)! Float bk_coeff(1); for (int i = 1; i < abs_n; i++) { FloatOps::mul(bk_coeff, Float(i), bk_coeff); } bk_coeff.truncateToApprox(wp); Float partB = bk_coeff; for (int k = 1; k < abs_n; k++) { // 漸化式: coeff_k = coeff_{k-1} · (x²/4) / (k · (n-k)) FloatOps::mul(bk_coeff, x_half_sq, bk_coeff); FloatOps::div(bk_coeff, Float(static_cast(k) * (abs_n - k)), bk_coeff); bk_coeff.truncateToApprox(wp); FloatOps::add(partB, bk_coeff, partB); partB.truncateToApprox(wp); } result = result - one_over_pi * x_half_neg_n * partB; result.truncateToApprox(wp); } // Part C: -(1/π)·(x/2)^n·Σ_{k=0}^{∞} (-1)^k·(H_k+H_{k+n})/(k!·(k+n)!)·(x²/4)^k Float x_half_n(1); for (int i = 0; i < abs_n; i++) { FloatOps::mul(x_half_n, x_half, x_half_n); } x_half_n.truncateToApprox(wp); Float H_k(0); // H_0 = 0 Float H_kn(0); // H_n = Σ_{j=1}^{n} 1/j Float h_tmp(0); for (int j = 1; j <= abs_n; j++) { FloatOps::div(Float(1), Float(j), h_tmp); FloatOps::add(H_kn, h_tmp, H_kn); } H_kn.truncateToApprox(wp); // k=0: coeff = 1/(0!·n!) = 1/n!, 調和数和 = H_0 + H_n = H_n Float ck_coeff(1); // 1/(k!·(k+n)!) — 漸化式で管理、符号 (-1)^k 込み for (int i = 1; i <= abs_n; i++) { FloatOps::div(ck_coeff, Float(i), ck_coeff); } ck_coeff.truncateToApprox(wp); Float neg_x_half_sq = -x_half_sq; Float partC = ck_coeff * (H_k + H_kn); partC.truncateToApprox(wp); Float term(0); for (int k = 1; k < 10 * wp; k++) { FloatOps::div(Float(1), Float(k), h_tmp); FloatOps::add(H_k, h_tmp, H_k); FloatOps::div(Float(1), Float(k + abs_n), h_tmp); FloatOps::add(H_kn, h_tmp, H_kn); // 漸化式: ck_coeff_{k} = ck_coeff_{k-1} · (-(x²/4)) / (k · (k+n)) FloatOps::mul(ck_coeff, neg_x_half_sq, ck_coeff); FloatOps::div(ck_coeff, Float(static_cast(k) * (k + abs_n)), ck_coeff); ck_coeff.truncateToApprox(wp); FloatOps::mul(ck_coeff, H_k + H_kn, term); term.truncateToApprox(wp); FloatOps::add(partC, term, partC); partC.truncateToApprox(wp); if (k >= 5 && term.isZero()) break; if (k >= 5) { auto t_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto s_bits = partC.exponent() + static_cast(partC.mantissa().bitLength()); if (s_bits - t_bits > wp + 5) break; } } result = result - one_over_pi * x_half_n * partC; // 負の次数: Y_{-n}(x) = (-1)^n · Y_n(x) if (n < 0 && abs_n % 2 != 0) result = -result; finalizeResult(result, eff_x, precision); return result; } Float besselY(int n, Float&& x, int precision) { return besselY(n, static_cast(x), precision); } //============================================================================= // 変形ベッセル関数 I_n(x) (Modified Bessel function of the first kind) //============================================================================= // I_n(x) = Σ_{m=0}^{∞} (x/2)^{2m+n} / (m! · (m+n)!) // J_n と同じ級数だが (-1)^m の交代符号がない(全項正) Float besselI(int n, const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) { return (std::abs(n) % 2 == 0) ? Float::positiveInfinity() : Float::negativeInfinity(); } return Float::positiveInfinity(); // I_n(+∞) = +∞ } int eff_x = x.effectiveBits(); int wp = precision + 20; Float xw = x; xw.truncateToApprox(wp); // I_n(0) = δ_{n,0} if (x.isZero()) { return (n == 0) ? Float(1) : Float(0); } // 負の次数: I_{-n}(x) = I_n(x) (整数次) int abs_n = (n >= 0) ? n : -n; // I_n(-x) = (-1)^n · I_n(x) bool negate = false; if (xw.isNegative()) { xw = -xw; if (abs_n % 2 != 0) negate = true; } // Taylor 級数: I_n(x) = (x/2)^n · Σ_{m=0}^{∞} (x/2)^{2m} / (m! · (m+n)!) Float x_half = ldexp(xw, -1); x_half.truncateToApprox(wp); Float x_half_sq = x_half * x_half; x_half_sq.truncateToApprox(wp); // (x/2)^n Float x_half_n(1); for (int i = 0; i < abs_n; i++) { FloatOps::mul(x_half_n, x_half, x_half_n); } x_half_n.truncateToApprox(wp); // term_0 = 1/n! Float term(1); for (int i = 1; i <= abs_n; i++) { FloatOps::div(term, Float(i), term); } term.truncateToApprox(wp); Float sum = term; for (int m = 1; m < 10 * wp; m++) { // term_m = term_{m-1} · (x/2)² / (m · (m+n)) — 符号反転なし FloatOps::mul(term, x_half_sq, term); FloatOps::div(term, Float(static_cast(m) * (m + abs_n)), term); term.truncateToApprox(wp); FloatOps::add(sum, term, sum); sum.truncateToApprox(wp); if (m >= 3 && term.isZero()) break; if (m >= 3) { auto t_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - t_bits > wp + 5) break; } } Float result = x_half_n * sum; if (negate) result = -result; finalizeResult(result, eff_x, precision); return result; } Float besselI(int n, Float&& x, int precision) { return besselI(n, static_cast(x), precision); } //============================================================================= // 変形ベッセル関数 K_n(x) (Modified Bessel function of the second kind) //============================================================================= // A&S 9.6.11 / DLMF 10.31.1 (整数次): // K_n(x) = (1/2)(x/2)^{-n} · Σ_{k=0}^{n-1} (-1)^k · (n-k-1)!/k! · (x²/4)^k // + (-1)^{n+1} · ln(x/2) · I_n(x) // + (-1)^n · (1/2)(x/2)^n · Σ_{k=0}^{∞} [ψ(k+1)+ψ(k+n+1)] / (k!·(k+n)!) · (x²/4)^k // ψ(m+1) = -γ + H_m Float besselK(int n, const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float::nan(); return Float(0); // K_n(+∞) = 0 (指数減衰) } if (x.isZero()) return Float::positiveInfinity(); // K_n(0) = +∞ if (x.isNegative()) return Float::nan(); // K_n(x<0) は実数でない int eff_x = x.effectiveBits(); int wp = precision + 30; Float xw = x; xw.truncateToApprox(wp); // K_{-n}(x) = K_n(x) (整数次) int abs_n = (n >= 0) ? n : -n; Float euler_gamma = Float::euler(wp); Float x_half = ldexp(xw, -1); x_half.truncateToApprox(wp); Float ln_x_half = log(x_half, wp); Float x_half_sq = x_half * x_half; x_half_sq.truncateToApprox(wp); Float in = besselI(abs_n, xw, wp); // Part A: (-1)^{n+1} · I_n(x) · ln(x/2) Float partA = in * ln_x_half; if (abs_n % 2 == 0) partA = -partA; // (-1)^{n+1} partA.truncateToApprox(wp); // Part B: (1/2)·(x/2)^{-n} · Σ_{k=0}^{n-1} (-1)^k · (n-k-1)!/k! · (x²/4)^k Float partB(0); if (abs_n > 0) { Float x_half_neg_n(1); for (int i = 0; i < abs_n; i++) { FloatOps::div(x_half_neg_n, x_half, x_half_neg_n); } x_half_neg_n.truncateToApprox(wp); // k=0: (n-1)!/0! = (n-1)! Float bk_coeff(1); Float neg_x_half_sq_k = -x_half_sq; for (int i = 1; i < abs_n; i++) { FloatOps::mul(bk_coeff, Float(i), bk_coeff); } bk_coeff.truncateToApprox(wp); Float sumB = bk_coeff; for (int k = 1; k < abs_n; k++) { // 漸化式: coeff_k = coeff_{k-1} · (-(x²/4)) / (k · (n-k)) FloatOps::mul(bk_coeff, neg_x_half_sq_k, bk_coeff); FloatOps::div(bk_coeff, Float(static_cast(k) * (abs_n - k)), bk_coeff); bk_coeff.truncateToApprox(wp); FloatOps::add(sumB, bk_coeff, sumB); sumB.truncateToApprox(wp); } partB = ldexp(x_half_neg_n * sumB, -1); partB.truncateToApprox(wp); } // Part C: (-1)^n · (1/2)·(x/2)^n · Σ_{k=0}^{∞} [ψ(k+1)+ψ(k+n+1)] / (k!·(k+n)!) · (x²/4)^k // ψ(k+1) = -γ + H_k, ψ(k+n+1) = -γ + H_{k+n} // ψ(k+1) + ψ(k+n+1) = -2γ + H_k + H_{k+n} Float x_half_n(1); for (int i = 0; i < abs_n; i++) { FloatOps::mul(x_half_n, x_half, x_half_n); } x_half_n.truncateToApprox(wp); Float H_k(0); // H_0 = 0 Float H_kn(0); // H_n = Σ_{j=1}^{n} 1/j Float h_tmp(0); for (int j = 1; j <= abs_n; j++) { FloatOps::div(Float(1), Float(j), h_tmp); FloatOps::add(H_kn, h_tmp, H_kn); } H_kn.truncateToApprox(wp); // k=0: 1/(0!·n!) = 1/n! Float ck_coeff(1); for (int i = 1; i <= abs_n; i++) { FloatOps::div(ck_coeff, Float(i), ck_coeff); } ck_coeff.truncateToApprox(wp); Float two_gamma = ldexp(euler_gamma, 1); Float partC = ck_coeff * (H_k + H_kn - two_gamma); partC.truncateToApprox(wp); Float term(0); for (int k = 1; k < 10 * wp; k++) { FloatOps::div(Float(1), Float(k), h_tmp); FloatOps::add(H_k, h_tmp, H_k); FloatOps::div(Float(1), Float(k + abs_n), h_tmp); FloatOps::add(H_kn, h_tmp, H_kn); // 漸化式: ck_coeff_{k} = ck_coeff_{k-1} · (x²/4) / (k · (k+n)) FloatOps::mul(ck_coeff, x_half_sq, ck_coeff); FloatOps::div(ck_coeff, Float(static_cast(k) * (k + abs_n)), ck_coeff); ck_coeff.truncateToApprox(wp); FloatOps::mul(ck_coeff, H_k + H_kn - two_gamma, term); term.truncateToApprox(wp); FloatOps::add(partC, term, partC); partC.truncateToApprox(wp); if (k >= 5 && term.isZero()) break; if (k >= 5) { auto t_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto s_bits = partC.exponent() + static_cast(partC.mantissa().bitLength()); if (s_bits - t_bits > wp + 5) break; } } Float sign_n = (abs_n % 2 == 0) ? Float(1) : Float(-1); partC = sign_n * ldexp(x_half_n * partC, -1); partC.truncateToApprox(wp); Float result = partA + partB + partC; finalizeResult(result, eff_x, precision); return result; } Float besselK(int n, Float&& x, int precision) { return besselK(n, static_cast(x), precision); } //============================================================================= // 球ベッセル関数 j_n(x) (Spherical Bessel function of the first kind) //============================================================================= // j_0(x) = sin(x)/x // j_1(x) = sin(x)/x² - cos(x)/x // j_{k+1}(x) = (2k+1)/x · j_k(x) - j_{k-1}(x) (前進漸化式) Float sphericalBesselJ(int n, const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (n < 0) return Float::nan(); int eff_x = x.effectiveBits(); int wp = precision + 20 + 2 * n; Float xw = x; xw.truncateToApprox(wp); // j_n(0): j_0(0) = 1, j_n(0) = 0 (n > 0) if (x.isZero()) { return (n == 0) ? Float(1) : Float(0); } // x → ±∞: j_n(x) → 0 (振動減衰) if (x.isInfinity()) return Float(0); Float sin_x, cos_x; sinCos(xw, sin_x, cos_x, wp); // j_0(x) = sin(x)/x Float j0 = sin_x / xw; j0.truncateToApprox(wp); if (n == 0) { finalizeResult(j0, eff_x, precision); return j0; } // j_1(x) = sin(x)/x² - cos(x)/x Float j1 = sin_x / (xw * xw) - cos_x / xw; j1.truncateToApprox(wp); if (n == 1) { finalizeResult(j1, eff_x, precision); return j1; } // 前進漸化式: j_{k+1} = (2k+1)/x · j_k - j_{k-1} Float j_prev = j0; Float j_curr = j1; for (int k = 1; k < n; k++) { Float j_next = Float(2 * k + 1) / xw * j_curr - j_prev; j_next.truncateToApprox(wp); j_prev = j_curr; j_curr = j_next; } finalizeResult(j_curr, eff_x, precision); return j_curr; } Float sphericalBesselJ(int n, Float&& x, int precision) { return sphericalBesselJ(n, static_cast(x), precision); } //============================================================================= // 球ベッセル関数 y_n(x) (Spherical Bessel function of the second kind) //============================================================================= // y_0(x) = -cos(x)/x // y_1(x) = -cos(x)/x² - sin(x)/x // y_{k+1}(x) = (2k+1)/x · y_k(x) - y_{k-1}(x) (前進漸化式) Float sphericalBesselY(int n, const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (n < 0) return Float::nan(); if (x.isZero()) return Float::negativeInfinity(); // y_n(0) = -∞ if (x.isNegative()) return Float::nan(); if (x.isInfinity()) return Float(0); // y_n(+∞) → 0 int eff_x = x.effectiveBits(); int wp = precision + 20 + 2 * n; Float xw = x; xw.truncateToApprox(wp); Float sin_x, cos_x; sinCos(xw, sin_x, cos_x, wp); // y_0(x) = -cos(x)/x Float y0 = -cos_x / xw; y0.truncateToApprox(wp); if (n == 0) { finalizeResult(y0, eff_x, precision); return y0; } // y_1(x) = -cos(x)/x² - sin(x)/x Float y1 = -cos_x / (xw * xw) - sin_x / xw; y1.truncateToApprox(wp); if (n == 1) { finalizeResult(y1, eff_x, precision); return y1; } // 前進漸化式: y_{k+1} = (2k+1)/x · y_k - y_{k-1} Float y_prev = y0; Float y_curr = y1; for (int k = 1; k < n; k++) { Float y_next = Float(2 * k + 1) / xw * y_curr - y_prev; y_next.truncateToApprox(wp); y_prev = y_curr; y_curr = y_next; } finalizeResult(y_curr, eff_x, precision); return y_curr; } Float sphericalBesselY(int n, Float&& x, int precision) { return sphericalBesselY(n, static_cast(x), precision); } //============================================================================= // エアリー関数 Ai(x), Bi(x) //============================================================================= // Ai(x) = c1·f(x) - c2·g(x) // Bi(x) = √3·[c1·f(x) + c2·g(x)] // f(x) = Σ_{k=0}^{∞} 3^k · x^{3k} / (3k)! · Γ(k+1/3)/(Γ(1/3)) ← 漸化式で // 実用的には Taylor 級数で直接計算: // Ai(x) = a·Σ_{k=0}^{∞} c_k·x^k where c_0 = 1, c_1 = x, c_{k+3} = c_k / ((k+2)(k+3))·x³ // Maclaurin 級数: // Ai(x) = Ai(0)·f(x) + Ai'(0)·g(x) // f(x) = 1 + x³/6 + x⁶/180 + ... = Σ (3k)! の逆数的な項 // g(x) = x + x⁴/12 + x⁷/504 + ... // Ai(0) = 1/(3^{2/3}·Γ(2/3)), Ai'(0) = -1/(3^{1/3}·Γ(1/3)) Float airyAi(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float(0); // Ai(±∞) → 0 int eff_x = x.effectiveBits(); int wp = precision + 30; Float xw = x; xw.truncateToApprox(wp); // Ai(0) = 1 / (3^{2/3} · Γ(2/3)) // Ai'(0) = -1 / (3^{1/3} · Γ(1/3)) Float three = Float(3); Float ai0 = Float(1) / (pow(three, Float(2) / three, wp) * gamma(Float(2) / three, wp)); Float aip0 = -Float(1) / (pow(three, Float(1) / three, wp) * gamma(Float(1) / three, wp)); ai0.truncateToApprox(wp); aip0.truncateToApprox(wp); if (x.isZero()) { finalizeResult(ai0, eff_x, precision); return ai0; } // f(x) = Σ_{k=0}^{∞} a_k, where a_0 = 1, a_{k+1} = a_k · x³ / ((3k+2)(3k+3)) // g(x) = Σ_{k=0}^{∞} b_k, where b_0 = x, b_{k+1} = b_k · x³ / ((3k+3)(3k+4)) Float x3 = xw * xw * xw; x3.truncateToApprox(wp); Float f_sum(1); // a_0 = 1 Float g_sum = xw; // b_0 = x Float f_term(1); Float g_term = xw; for (int k = 0; k < 10 * wp; k++) { FloatOps::mul(f_term, x3, f_term); FloatOps::div(f_term, Float(static_cast(3 * k + 2) * (3 * k + 3)), f_term); f_term.truncateToApprox(wp); FloatOps::add(f_sum, f_term, f_sum); f_sum.truncateToApprox(wp); FloatOps::mul(g_term, x3, g_term); FloatOps::div(g_term, Float(static_cast(3 * k + 3) * (3 * k + 4)), g_term); g_term.truncateToApprox(wp); FloatOps::add(g_sum, g_term, g_sum); g_sum.truncateToApprox(wp); if (k >= 3) { bool f_conv = f_term.isZero(); bool g_conv = g_term.isZero(); if (!f_conv) { auto ft = f_term.exponent() + static_cast(f_term.mantissa().bitLength()); auto fs = f_sum.exponent() + static_cast(f_sum.mantissa().bitLength()); f_conv = (fs - ft > wp + 5); } if (!g_conv) { auto gt = g_term.exponent() + static_cast(g_term.mantissa().bitLength()); auto gs = g_sum.exponent() + static_cast(g_sum.mantissa().bitLength()); g_conv = (gs - gt > wp + 5); } if (f_conv && g_conv) break; } } Float result = ai0 * f_sum + aip0 * g_sum; finalizeResult(result, eff_x, precision); return result; } Float airyAi(Float&& x, int precision) { return airyAi(static_cast(x), precision); } Float airyBi(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float(0); // Bi(-∞) → 0 return Float::positiveInfinity(); // Bi(+∞) → +∞ } int eff_x = x.effectiveBits(); int wp = precision + 30; Float xw = x; xw.truncateToApprox(wp); // Bi(0) = 1 / (3^{1/6} · Γ(2/3)) // Bi'(0) = 3^{1/6} / Γ(1/3) Float three = Float(3); Float bi0 = Float(1) / (pow(three, Float(1) / Float(6), wp) * gamma(Float(2) / three, wp)); Float bip0 = pow(three, Float(1) / Float(6), wp) / gamma(Float(1) / three, wp); bi0.truncateToApprox(wp); bip0.truncateToApprox(wp); if (x.isZero()) { finalizeResult(bi0, eff_x, precision); return bi0; } // 同じ f(x), g(x) 級数 Float x3 = xw * xw * xw; x3.truncateToApprox(wp); Float f_sum(1); Float g_sum = xw; Float f_term(1); Float g_term = xw; for (int k = 0; k < 10 * wp; k++) { FloatOps::mul(f_term, x3, f_term); FloatOps::div(f_term, Float(static_cast(3 * k + 2) * (3 * k + 3)), f_term); f_term.truncateToApprox(wp); FloatOps::add(f_sum, f_term, f_sum); f_sum.truncateToApprox(wp); FloatOps::mul(g_term, x3, g_term); FloatOps::div(g_term, Float(static_cast(3 * k + 3) * (3 * k + 4)), g_term); g_term.truncateToApprox(wp); FloatOps::add(g_sum, g_term, g_sum); g_sum.truncateToApprox(wp); if (k >= 3) { bool f_conv = f_term.isZero(); bool g_conv = g_term.isZero(); if (!f_conv) { auto ft = f_term.exponent() + static_cast(f_term.mantissa().bitLength()); auto fs = f_sum.exponent() + static_cast(f_sum.mantissa().bitLength()); f_conv = (fs - ft > wp + 5); } if (!g_conv) { auto gt = g_term.exponent() + static_cast(g_term.mantissa().bitLength()); auto gs = g_sum.exponent() + static_cast(g_sum.mantissa().bitLength()); g_conv = (gs - gt > wp + 5); } if (f_conv && g_conv) break; } } Float result = bi0 * f_sum + bip0 * g_sum; finalizeResult(result, eff_x, precision); return result; } Float airyBi(Float&& x, int precision) { return airyBi(static_cast(x), precision); } //============================================================================= // エアリー関数の導関数 Ai'(x), Bi'(x) //============================================================================= // Maclaurin 級数の項微分: // f(x) = Σ c_k·x^{3k}, f'(x) = Σ_{k≥1} 3k·c_k·x^{3k-1} // g(x) = Σ d_k·x^{3k+1}, g'(x) = Σ_{k≥0} (3k+1)·d_k·x^{3k} // // ループ内で f_term, g_term を更新した後: // fp_contrib = 3(k+1) · f_term / x // gp_contrib = (3k+4) · g_term / x // // Ai'(x) = Ai(0)·f'(x) + Ai'(0)·g'(x) // Bi'(x) = Bi(0)·f'(x) + Bi'(0)·g'(x) Float airyAiPrime(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) return Float(0); // Ai'(±∞) → 0 int eff_x = x.effectiveBits(); int wp = precision + 30; Float xw = x; xw.truncateToApprox(wp); Float three = Float(3); Float ai0 = Float(1) / (pow(three, Float(2) / three, wp) * gamma(Float(2) / three, wp)); Float aip0 = -Float(1) / (pow(three, Float(1) / three, wp) * gamma(Float(1) / three, wp)); ai0.truncateToApprox(wp); aip0.truncateToApprox(wp); // Ai'(0) = aip0 (f'(0) = 0, g'(0) = 1) if (x.isZero()) { finalizeResult(aip0, eff_x, precision); return aip0; } Float x3 = xw * xw * xw; x3.truncateToApprox(wp); Float inv_x = Float(1) / xw; inv_x.truncateToApprox(wp); Float f_term(1); Float g_term = xw; Float fp_sum(0); // f'(0) = 0 Float gp_sum(1); // g'(0) = 1 Float fp_contrib(0), gp_contrib(0); for (int k = 0; k < 10 * wp; k++) { FloatOps::mul(f_term, x3, f_term); FloatOps::div(f_term, Float(static_cast(3 * k + 2) * (3 * k + 3)), f_term); f_term.truncateToApprox(wp); FloatOps::mul(g_term, x3, g_term); FloatOps::div(g_term, Float(static_cast(3 * k + 3) * (3 * k + 4)), g_term); g_term.truncateToApprox(wp); FloatOps::mul(f_term, inv_x, fp_contrib); FloatOps::mul(fp_contrib, Float(3 * (k + 1)), fp_contrib); fp_contrib.truncateToApprox(wp); FloatOps::mul(g_term, inv_x, gp_contrib); FloatOps::mul(gp_contrib, Float(3 * k + 4), gp_contrib); gp_contrib.truncateToApprox(wp); FloatOps::add(fp_sum, fp_contrib, fp_sum); fp_sum.truncateToApprox(wp); FloatOps::add(gp_sum, gp_contrib, gp_sum); gp_sum.truncateToApprox(wp); if (k >= 3) { bool fp_conv = fp_contrib.isZero(); bool gp_conv = gp_contrib.isZero(); if (!fp_conv) { auto ft = fp_contrib.exponent() + static_cast(fp_contrib.mantissa().bitLength()); auto fs = fp_sum.exponent() + static_cast(fp_sum.mantissa().bitLength()); fp_conv = (fs - ft > wp + 5); } if (!gp_conv) { auto gt = gp_contrib.exponent() + static_cast(gp_contrib.mantissa().bitLength()); auto gs = gp_sum.exponent() + static_cast(gp_sum.mantissa().bitLength()); gp_conv = (gs - gt > wp + 5); } if (fp_conv && gp_conv) break; } } Float result = ai0 * fp_sum + aip0 * gp_sum; finalizeResult(result, eff_x, precision); return result; } Float airyAiPrime(Float&& x, int precision) { return airyAiPrime(static_cast(x), precision); } Float airyBiPrime(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isInfinity()) { if (x.isNegative()) return Float(0); // Bi'(-∞) → 0 return Float::positiveInfinity(); // Bi'(+∞) → +∞ } int eff_x = x.effectiveBits(); int wp = precision + 30; Float xw = x; xw.truncateToApprox(wp); Float three = Float(3); Float bi0 = Float(1) / (pow(three, Float(1) / Float(6), wp) * gamma(Float(2) / three, wp)); Float bip0 = pow(three, Float(1) / Float(6), wp) / gamma(Float(1) / three, wp); bi0.truncateToApprox(wp); bip0.truncateToApprox(wp); // Bi'(0) = bip0 if (x.isZero()) { finalizeResult(bip0, eff_x, precision); return bip0; } Float x3 = xw * xw * xw; x3.truncateToApprox(wp); Float inv_x = Float(1) / xw; inv_x.truncateToApprox(wp); Float f_term(1); Float g_term = xw; Float fp_sum(0); Float gp_sum(1); Float fp_contrib(0), gp_contrib(0); for (int k = 0; k < 10 * wp; k++) { FloatOps::mul(f_term, x3, f_term); FloatOps::div(f_term, Float(static_cast(3 * k + 2) * (3 * k + 3)), f_term); f_term.truncateToApprox(wp); FloatOps::mul(g_term, x3, g_term); FloatOps::div(g_term, Float(static_cast(3 * k + 3) * (3 * k + 4)), g_term); g_term.truncateToApprox(wp); FloatOps::mul(f_term, inv_x, fp_contrib); FloatOps::mul(fp_contrib, Float(3 * (k + 1)), fp_contrib); fp_contrib.truncateToApprox(wp); FloatOps::mul(g_term, inv_x, gp_contrib); FloatOps::mul(gp_contrib, Float(3 * k + 4), gp_contrib); gp_contrib.truncateToApprox(wp); FloatOps::add(fp_sum, fp_contrib, fp_sum); fp_sum.truncateToApprox(wp); FloatOps::add(gp_sum, gp_contrib, gp_sum); gp_sum.truncateToApprox(wp); if (k >= 3) { bool fp_conv = fp_contrib.isZero(); bool gp_conv = gp_contrib.isZero(); if (!fp_conv) { auto ft = fp_contrib.exponent() + static_cast(fp_contrib.mantissa().bitLength()); auto fs = fp_sum.exponent() + static_cast(fp_sum.mantissa().bitLength()); fp_conv = (fs - ft > wp + 5); } if (!gp_conv) { auto gt = gp_contrib.exponent() + static_cast(gp_contrib.mantissa().bitLength()); auto gs = gp_sum.exponent() + static_cast(gp_sum.mantissa().bitLength()); gp_conv = (gs - gt > wp + 5); } if (fp_conv && gp_conv) break; } } Float result = bi0 * fp_sum + bip0 * gp_sum; finalizeResult(result, eff_x, precision); return result; } Float airyBiPrime(Float&& x, int precision) { return airyBiPrime(static_cast(x), precision); } //============================================================================= // 合流極限関数 ₀F₁(; b; z) //============================================================================= // ₀F₁(; b; z) = Σ_{k=0}^∞ z^k / ((b)_k · k!) // term_{k+1} / term_k = z / ((b+k) · (k+1)) // 全 z で収束 Float hyperg0F1(const Float& b, const Float& z, int precision) { if (b.isNaN() || z.isNaN()) return Float::nan(); if (z.isZero()) return Float(1); // b が非正整数 → 極 if (!b.isPositive() && b.isInteger()) return Float::nan(); int eff = std::min(b.effectiveBits(), z.effectiveBits()); int wp = precision + 20; Float bw = b; bw.truncateToApprox(wp); Float zw = z; zw.truncateToApprox(wp); Float term(1); Float sum(1); Float tmp(0); for (int k = 0; k < 10 * wp; k++) { FloatOps::mul(term, zw, term); FloatOps::add(bw, Float(k), tmp); FloatOps::mul(tmp, Float(k + 1), tmp); FloatOps::div(term, tmp, term); term.truncateToApprox(wp); FloatOps::add(sum, term, sum); sum.truncateToApprox(wp); if (k >= 3 && term.isZero()) break; if (k >= 3) { auto t_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - t_bits > wp + 5) break; } } finalizeResult(sum, eff, precision); return sum; } Float hyperg0F1(Float&& b, Float&& z, int precision) { return hyperg0F1(static_cast(b), static_cast(z), precision); } //============================================================================= // 合流型超幾何関数 ₁F₁(a; b; z) (Kummer M) //============================================================================= // M(a, b, z) = Σ_{k=0}^∞ (a)_k z^k / ((b)_k · k!) // term_{k+1} / term_k = (a+k) · z / ((b+k) · (k+1)) // // Kummer 変換 (z < 0 時): M(a,b,z) = e^z · M(b-a, b, -z) // 交代級数の相殺を回避し収束を加速 Float confHyperg(const Float& a, const Float& b, const Float& z, int precision) { if (a.isNaN() || b.isNaN() || z.isNaN()) return Float::nan(); if (z.isZero()) return Float(1); // b が非正整数 → 極 if (!b.isPositive() && b.isInteger()) return Float::nan(); // a = 0 → 1 if (a.isZero()) return Float(1); int eff = std::min({a.effectiveBits(), b.effectiveBits(), z.effectiveBits()}); int wp = precision + 30; Float aw = a; aw.truncateToApprox(wp); Float bw = b; bw.truncateToApprox(wp); Float zw = z; zw.truncateToApprox(wp); // a = b → e^z if (aw == bw) { Float result = exp(zw, wp); finalizeResult(result, eff, precision); return result; } // Kummer 変換: z < 0 の場合、M(a,b,z) = e^z · M(b-a, b, -z) // -z > 0 なので再帰呼び出しは Kummer パスに入らない if (zw.isNegative()) { Float ez = exp(zw, wp); Float inner = confHyperg(bw - aw, bw, -zw, wp); inner.truncateToApprox(wp); Float result = ez * inner; finalizeResult(result, eff, precision); return result; } // a が非正整数 → 多項式(有限和) bool is_poly = false; int poly_terms = 0; if (aw.isNegative() && aw.isInteger()) { is_poly = true; poly_terms = -static_cast(aw.toDouble()) + 1; } // Taylor 級数 Float term(1); Float sum(1); int max_iter = is_poly ? poly_terms : 10 * wp; for (int k = 0; k < max_iter; k++) { term = term * (aw + Float(k)) * zw / ((bw + Float(k)) * Float(k + 1)); term.truncateToApprox(wp); sum = sum + term; sum.truncateToApprox(wp); if (!is_poly && k >= 3 && term.isZero()) break; if (!is_poly && k >= 3) { auto t_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - t_bits > wp + 5) break; } } finalizeResult(sum, eff, precision); return sum; } Float confHyperg(Float&& a, Float&& b, Float&& z, int precision) { return confHyperg(static_cast(a), static_cast(b), static_cast(z), precision); } //============================================================================= // Gauss 超幾何関数 ₂F₁(a, b; c; z) //============================================================================= // ₂F₁(a, b; c; z) = Σ_{k=0}^∞ (a)_k (b)_k z^k / ((c)_k · k!) // |z| < 1 で収束。領域戦略: // |z| < 0.5 → 直接 Taylor // z < -0.5 → Pfaff 変換: (1-z)^{-a} · ₂F₁(a, c-b; c; z/(z-1)) // 0.5 ≤ z < 1 → DLMF 15.8.1 接続公式 (1-z で評価) // 多項式ケース → 常に直接 Taylor Float hyperg(const Float& a, const Float& b, const Float& c, const Float& z, int precision) { if (a.isNaN() || b.isNaN() || c.isNaN() || z.isNaN()) return Float::nan(); if (z.isZero()) return Float(1); if (a.isZero() || b.isZero()) return Float(1); int eff = std::min({a.effectiveBits(), b.effectiveBits(), c.effectiveBits(), z.effectiveBits()}); int wp = precision + 40; Float aw = a; aw.truncateToApprox(wp); Float bw = b; bw.truncateToApprox(wp); Float cw = c; cw.truncateToApprox(wp); Float zw = z; zw.truncateToApprox(wp); // c が非正整数 → a or b が先に終端しない限り極 if (!cw.isPositive() && cw.isInteger()) { int ci = static_cast(cw.toDouble()); bool a_ok = aw.isNegative() && aw.isInteger() && static_cast(aw.toDouble()) >= ci; bool b_ok = bw.isNegative() && bw.isInteger() && static_cast(bw.toDouble()) >= ci; if (!a_ok && !b_ok) return Float::nan(); } // 多項式ケースの検出 bool is_poly = false; int poly_terms = 0; if (aw.isNegative() && aw.isInteger()) { is_poly = true; poly_terms = -static_cast(aw.toDouble()) + 1; } else if (bw.isNegative() && bw.isInteger()) { is_poly = true; poly_terms = -static_cast(bw.toDouble()) + 1; } double z_val = zw.toDouble(); double z_abs = std::abs(z_val); // |z| >= 1 で非多項式 → 発散 if (!is_poly && z_abs >= 1.0) return Float::nan(); // --- Pfaff 変換: z < -0.5 --- // ₂F₁(a,b;c;z) = (1-z)^{-a} · ₂F₁(a, c-b; c; z/(z-1)) // z/(z-1) ∈ (0, 1/3] なので Taylor 高速収束 if (!is_poly && z_val < -0.5) { Float one_minus_z = Float(1) - zw; one_minus_z.truncateToApprox(wp); Float z_mapped = zw / (zw - Float(1)); z_mapped.truncateToApprox(wp); Float prefix = pow(one_minus_z, -aw, wp); prefix.truncateToApprox(wp); Float inner = hyperg(aw, cw - bw, cw, z_mapped, wp); inner.truncateToApprox(wp); Float result = prefix * inner; finalizeResult(result, eff, precision); return result; } // --- DLMF 15.8.1 接続公式: 0.5 ≤ z < 1 --- // ₂F₁(a,b;c;z) = Γ(c)Γ(c-a-b) / [Γ(c-a)Γ(c-b)] · ₂F₁(a,b;a+b-c+1;1-z) // + (1-z)^{c-a-b} · Γ(c)Γ(a+b-c) / [Γ(a)Γ(b)] · ₂F₁(c-a,c-b;c-a-b+1;1-z) // 1-z ∈ (0, 0.5] なので両方の ₂F₁ は Taylor で収束 if (!is_poly && z_val >= 0.5) { Float w = Float(1) - zw; // w ∈ (0, 0.5] w.truncateToApprox(wp); Float cab = cw - aw - bw; cab.truncateToApprox(wp); // c-a-b が整数に近い場合は直接 Taylor(精度低下のため) double cab_val = cab.toDouble(); double cab_frac = cab_val - std::floor(cab_val); if (cab_frac < 0.01 || cab_frac > 0.99) { // 整数近傍: 接続公式は不安定なので多めのガードビットで直接 Taylor wp = precision + 80; aw.truncateToApprox(wp); bw.truncateToApprox(wp); cw.truncateToApprox(wp); zw = z; zw.truncateToApprox(wp); Float term(1); Float sum(1); for (int k = 0; k < 10 * wp; k++) { term = term * (aw + Float(k)) * (bw + Float(k)) * zw / ((cw + Float(k)) * Float(k + 1)); term.truncateToApprox(wp); sum = sum + term; sum.truncateToApprox(wp); if (k >= 3 && term.isZero()) break; if (k >= 3) { auto t_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - t_bits > wp + 5) break; } } finalizeResult(sum, eff, precision); return sum; } // 接続公式の2項を計算 Float gc = gamma(cw, wp); Float g_cab = gamma(cab, wp); Float g_neg_cab = gamma(-cab, wp); // Γ(a+b-c) = Γ(-cab) ... ではなく Γ(-(c-a-b)) に注意 // 第1項: Γ(c)·Γ(c-a-b) / [Γ(c-a)·Γ(c-b)] · ₂F₁(a, b; a+b-c+1; 1-z) Float coeff1 = gc * g_cab / (gamma(cw - aw, wp) * gamma(cw - bw, wp)); coeff1.truncateToApprox(wp); Float f1 = hyperg(aw, bw, aw + bw - cw + Float(1), w, wp); f1.truncateToApprox(wp); // 第2項: (1-z)^{c-a-b} · Γ(c)·Γ(a+b-c) / [Γ(a)·Γ(b)] · ₂F₁(c-a, c-b; c-a-b+1; 1-z) Float ab_minus_c = -(cab); // a+b-c Float coeff2 = gc * gamma(ab_minus_c, wp) / (gamma(aw, wp) * gamma(bw, wp)); coeff2.truncateToApprox(wp); Float f2 = hyperg(cw - aw, cw - bw, cab + Float(1), w, wp); f2.truncateToApprox(wp); Float w_power = pow(w, cab, wp); w_power.truncateToApprox(wp); Float result = coeff1 * f1 + coeff2 * w_power * f2; finalizeResult(result, eff, precision); return result; } // --- 直接 Taylor 級数: |z| < 0.5 or 多項式 --- Float term(1); Float sum(1); Float tmp(0); int max_iter = is_poly ? poly_terms : 10 * wp; for (int k = 0; k < max_iter; k++) { // term *= (a+k)*(b+k)*z / ((c+k)*(k+1)) Float fk(k); FloatOps::add(aw, fk, tmp); FloatOps::mul(term, tmp, term); FloatOps::add(bw, fk, tmp); FloatOps::mul(term, tmp, term); FloatOps::mul(term, zw, term); FloatOps::add(cw, fk, tmp); FloatOps::mul(tmp, Float(k + 1), tmp); FloatOps::div(term, tmp, term); term.truncateToApprox(wp); FloatOps::add(sum, term, sum); sum.truncateToApprox(wp); if (!is_poly && k >= 3 && term.isZero()) break; if (!is_poly && k >= 3) { auto t_bits = term.exponent() + static_cast(term.mantissa().bitLength()); auto s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - t_bits > wp + 5) break; } } finalizeResult(sum, eff, precision); return sum; } Float hyperg(Float&& a, Float&& b, Float&& c, Float&& z, int precision) { return hyperg(static_cast(a), static_cast(b), static_cast(c), static_cast(z), precision); } //============================================================================= // Legendre 多項式 P_n(x) //============================================================================= // Bonnet 漸化式: (n+1)P_{n+1} = (2n+1)·x·P_n - n·P_{n-1} Float legendreP(int n, const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (n < 0) return Float::nan(); if (n == 0) return Float(1); int eff_x = x.effectiveBits(); if (n == 1) { Float r = x; finalizeResult(r, eff_x, precision); return r; } int wp = precision + 10; Float xw = x; xw.truncateToApprox(wp); Float p_prev(1); // P_0 Float p_curr = xw; // P_1 for (int k = 1; k < n; k++) { Float p_next = (Float(2 * k + 1) * xw * p_curr - Float(k) * p_prev) / Float(k + 1); p_next.truncateToApprox(wp); p_prev = p_curr; p_curr = p_next; } finalizeResult(p_curr, eff_x, precision); return p_curr; } Float legendreP(int n, Float&& x, int precision) { return legendreP(n, static_cast(x), precision); } //============================================================================= // 陪 Legendre 関数 P_n^m(x) //============================================================================= // Condon-Shortley 位相因子なし // P_m^m = (2m-1)!! · (1-x²)^{m/2} // P_{m+1}^m = x·(2m+1)·P_m^m // (k-m+1)P_{k+1}^m = (2k+1)·x·P_k^m - (k+m)·P_{k-1}^m Float assocLegendreP(int n, int m, const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (n < 0 || m < 0) return Float::nan(); if (m > n) return Float(0); if (m == 0) return legendreP(n, x, precision); int eff_x = x.effectiveBits(); int wp = precision + 15; Float xw = x; xw.truncateToApprox(wp); // (1 - x²)^{1/2} Float sin2 = Float(1) - xw * xw; sin2.truncateToApprox(wp); if (sin2.isNegative()) sin2 = Float(0); Float sin_factor = sqrt(sin2, wp); // P_m^m = (2m-1)!! · (1-x²)^{m/2} Float pmm(1); for (int i = 1; i <= m; i++) { pmm = pmm * Float(2 * i - 1) * sin_factor; pmm.truncateToApprox(wp); } if (n == m) { finalizeResult(pmm, eff_x, precision); return pmm; } // P_{m+1}^m = x·(2m+1)·P_m^m Float pm1m = xw * Float(2 * m + 1) * pmm; pm1m.truncateToApprox(wp); if (n == m + 1) { finalizeResult(pm1m, eff_x, precision); return pm1m; } // 漸化式 Float p_prev = pmm; Float p_curr = pm1m; for (int k = m + 1; k < n; k++) { Float p_next = (Float(2 * k + 1) * xw * p_curr - Float(k + m) * p_prev) / Float(k - m + 1); p_next.truncateToApprox(wp); p_prev = p_curr; p_curr = p_next; } finalizeResult(p_curr, eff_x, precision); return p_curr; } Float assocLegendreP(int n, int m, Float&& x, int precision) { return assocLegendreP(n, m, static_cast(x), precision); } //============================================================================= // Hermite 多項式 H_n(x) — 物理学者版 //============================================================================= // H_0(x) = 1, H_1(x) = 2x // H_{n+1}(x) = 2x·H_n(x) - 2n·H_{n-1}(x) Float hermite(int n, const Float& x, int precision) { if (n < 0) return Float::nan(); if (n == 0) return Float::one(precision); int wp = precision + 10; Float two(2); Float h_prev = Float::one(wp); // H_0 Float h_curr = two * x; // H_1 h_curr.truncateToApprox(wp); for (int k = 1; k < n; ++k) { Float h_next = two * x * h_curr - two * Float(k) * h_prev; h_next.truncateToApprox(wp); h_prev = std::move(h_curr); h_curr = std::move(h_next); } finalizeResult(h_curr, x.effectiveBits(), precision); return h_curr; } Float hermite(int n, Float&& x, int precision) { return hermite(n, static_cast(x), precision); } //============================================================================= // Laguerre 多項式 L_n(x) //============================================================================= // L_0(x) = 1, L_1(x) = 1 - x // (n+1)·L_{n+1}(x) = (2n+1-x)·L_n(x) - n·L_{n-1}(x) Float laguerre(int n, const Float& x, int precision) { if (n < 0) return Float::nan(); if (n == 0) return Float::one(precision); int wp = precision + 10; Float l_prev = Float::one(wp); // L_0 Float l_curr = Float::one(wp) - x; // L_1 l_curr.truncateToApprox(wp); for (int k = 1; k < n; ++k) { Float l_next = (Float(2 * k + 1) - x) * l_curr - Float(k) * l_prev; l_next = l_next / Float(k + 1); l_next.truncateToApprox(wp); l_prev = std::move(l_curr); l_curr = std::move(l_next); } finalizeResult(l_curr, x.effectiveBits(), precision); return l_curr; } Float laguerre(int n, Float&& x, int precision) { return laguerre(n, static_cast(x), precision); } //============================================================================= // 陪 Laguerre 多項式 L_n^m(x) //============================================================================= // L_0^m(x) = 1, L_1^m(x) = 1 + m - x // (n+1)·L_{n+1}^m(x) = (2n+1+m-x)·L_n^m(x) - (n+m)·L_{n-1}^m(x) Float assocLaguerre(int n, int m, const Float& x, int precision) { if (n < 0) return Float::nan(); if (n == 0) return Float::one(precision); int wp = precision + 10; Float l_prev = Float::one(wp); // L_0^m Float l_curr = Float(1 + m) - x; // L_1^m l_curr.truncateToApprox(wp); for (int k = 1; k < n; ++k) { Float l_next = (Float(2 * k + 1 + m) - x) * l_curr - Float(k + m) * l_prev; l_next = l_next / Float(k + 1); l_next.truncateToApprox(wp); l_prev = std::move(l_curr); l_curr = std::move(l_next); } finalizeResult(l_curr, x.effectiveBits(), precision); return l_curr; } Float assocLaguerre(int n, int m, Float&& x, int precision) { return assocLaguerre(n, m, static_cast(x), precision); } //============================================================================= // Lambert W₀(x) — 主枝 //============================================================================= // W(x)·e^{W(x)} = x, W₀ ≥ -1 // Halley 反復: 3次収束 // 本体: x を値で受け取る (move 済み前提) static Float lambertW0_core(Float x, int eff_x, int precision) { int wp = precision + 20; x.truncateToApprox(wp); // x < -1/e → 定義域外 Float neg_inv_e = Float(-1) / exp(Float(1), wp); neg_inv_e.truncateToApprox(wp); if (x < neg_inv_e) return Float::nan(); // double で初期推定 double xd = x.toDouble(); double wd; if (xd < -0.3) { double p = std::sqrt(2.0 * (2.718281828459045 * xd + 1.0)); wd = -1.0 + p - p * p / 3.0 + 11.0 * p * p * p / 72.0; } else if (xd <= 3.0) { if (xd <= 0.5) { wd = xd * (1.0 - xd); } else { double lnx1 = std::log(xd + 1.0); wd = 0.665 * (1.0 + 0.0195 * lnx1) * lnx1 + 0.04; } } else { double lnx = std::log(xd); wd = lnx - std::log(lnx); } Float w(wd); w.truncateToApprox(wp); // Halley 反復 for (int i = 0; i < 10 * (wp / 50 + 1); i++) { Float ew = exp(w, wp); Float wew = w * ew; wew.truncateToApprox(wp); Float f = wew - x; f.truncateToApprox(wp); if (f.isZero()) break; // 収束チェック auto f_bits = f.exponent() + static_cast(f.mantissa().bitLength()); auto x_bits = x.isZero() ? int64_t(0) : x.exponent() + static_cast(x.mantissa().bitLength()); if (x_bits - f_bits > wp + 5) break; Float wp1 = w + Float(1); wp1.truncateToApprox(wp); Float denom = ew * wp1 - (w + Float(2)) * f / ldexp(wp1, 1); denom.truncateToApprox(wp); if (denom.isZero()) break; w = w - f / denom; w.truncateToApprox(wp); // Halley 収束により w の有効精度は各反復で向上する。 // exp(w,wp) の effective_bits_ 最適化が正しく動作するため eff を更新。 w.setResultPrecision(wp); } finalizeResult(w, eff_x, precision); return w; } Float lambertW0(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); if (x.isInfinity() && x.isPositive()) return Float::positiveInfinity(); return lambertW0_core(Float(x), x.effectiveBits(), precision); } Float lambertW0(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); if (x.isInfinity() && x.isPositive()) return Float::positiveInfinity(); int eff = x.effectiveBits(); return lambertW0_core(std::move(x), eff, precision); } //============================================================================= // Lambert W₋₁(x) — 副枝 //============================================================================= // -1/e ≤ x < 0 で定義。W₋₁ ≤ -1。 // 本体: x を値で受け取る (move 済み前提) static Float lambertWm1_core(Float x, int eff_x, int precision) { int wp = precision + 20; x.truncateToApprox(wp); // x < -1/e or x >= 0 → 定義域外 Float neg_inv_e = Float(-1) / exp(Float(1), wp); neg_inv_e.truncateToApprox(wp); if (x < neg_inv_e || !x.isNegative()) return Float::nan(); // double で初期推定 double xd = x.toDouble(); double wd; if (xd > -0.1) { double p = std::sqrt(2.0 * (2.718281828459045 * xd + 1.0)); wd = -1.0 - p - p * p / 3.0 - 11.0 * p * p * p / 72.0; } else { double lnmx = std::log(-xd); wd = lnmx - std::log(-lnmx); } Float w(wd); w.truncateToApprox(wp); // Halley 反復 for (int i = 0; i < 10 * (wp / 50 + 1); i++) { Float ew = exp(w, wp); Float wew = w * ew; wew.truncateToApprox(wp); Float f = wew - x; f.truncateToApprox(wp); if (f.isZero()) break; auto f_bits = f.exponent() + static_cast(f.mantissa().bitLength()); auto x_bits = x.exponent() + static_cast(x.mantissa().bitLength()); if (x_bits - f_bits > wp + 5) break; Float wp1 = w + Float(1); wp1.truncateToApprox(wp); Float denom = ew * wp1 - (w + Float(2)) * f / ldexp(wp1, 1); denom.truncateToApprox(wp); if (denom.isZero()) break; w = w - f / denom; w.truncateToApprox(wp); w.setResultPrecision(wp); } finalizeResult(w, eff_x, precision); return w; } Float lambertWm1(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); return lambertWm1_core(Float(x), x.effectiveBits(), precision); } Float lambertWm1(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); int eff = x.effectiveBits(); return lambertWm1_core(std::move(x), eff, precision); } //============================================================================= // 一般化指数積分 E_n(x) = ∫₁^∞ e^{-xt}/t^n dt //============================================================================= // E_1(x) = -Ei(-x) (x > 0) // 漸化式: k·E_{k+1}(x) = e^{-x} - x·E_k(x) // 連分数: 大きい x 向け // 本体: x を値で受け取る (move 済み前提) static Float expintN_core(int n, Float x, int eff_x, int precision) { int wp = precision + 20; x.truncateToApprox(wp); // E_0(x) = e^{-x}/x if (n == 0) { Float result = exp(-x, wp) / x; finalizeResult(result, eff_x, precision); return result; } // E_1(x) = -Ei(-x) → 既存 expint を利用 Float e1; { // E_1(x) = -γ - ln(x) + Σ_{k=1}^∞ (-1)^{k+1} x^k/(k·k!) Float euler_gamma = Float::euler(wp); Float ln_x = log(x, wp); Float sum = -euler_gamma - ln_x; Float term(1); for (int k = 1; k < 10 * wp; k++) { term = term * (-x) / Float(k); term.truncateToApprox(wp); Float contribution = -term / Float(k); sum = sum + contribution; sum.truncateToApprox(wp); if (k >= 5 && contribution.isZero()) break; if (k >= 5) { auto c_bits = contribution.exponent() + static_cast(contribution.mantissa().bitLength()); auto s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - c_bits > wp + 5) break; } } e1 = sum; } if (n == 1) { finalizeResult(e1, eff_x, precision); return e1; } // 上昇漸化式: k·E_{k+1}(x) = e^{-x} - x·E_k(x) Float emx = exp(-x, wp); Float en = e1; for (int k = 1; k < n; k++) { en = (emx - x * en) / Float(k); en.truncateToApprox(wp); } finalizeResult(en, eff_x, precision); return en; } Float expintN(int n, const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (n < 0) return Float::nan(); if (x.isNegative()) return Float::nan(); if (x.isZero()) { if (n <= 1) return Float::positiveInfinity(); return Float(1) / Float(n - 1); } return expintN_core(n, Float(x), x.effectiveBits(), precision); } Float expintN(int n, Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (n < 0) return Float::nan(); if (x.isNegative()) return Float::nan(); if (x.isZero()) { if (n <= 1) return Float::positiveInfinity(); return Float(1) / Float(n - 1); } int eff = x.effectiveBits(); return expintN_core(n, std::move(x), eff, precision); } //============================================================================= // 正弦積分 Si(x) = ∫₀ˣ sin(t)/t dt //============================================================================= // Si(x) = Σ_{k=0}^∞ (-1)^k · x^{2k+1} / ((2k+1)·(2k+1)!) // 奇関数: Si(-x) = -Si(x) // 本体: x を値で受け取る (move 済み前提) static Float sinIntegral_core(Float x, int eff_x, int precision) { int wp = precision + 20; x.truncateToApprox(wp); // 奇関数 bool negate = x.isNegative(); if (negate) x = -x; Float x2 = x * x; x2.truncateToApprox(wp); Float neg_x2 = -x2; // term tracks x^{2k+1} / (2k+1)! // 実際: Si(x) = Σ (-1)^k x^{2k+1} / ((2k+1)(2k+1)!) // term = (-1)^k x^{2k+1} / (2k+1)!, contribution = term / (2k+1) // term_{k+1} = term_k · (-x²) / ((2k+2)(2k+3)) Float term = x; // k=0: x^1 / 1! Float sum = x; // k=0: x / 1 Float contribution(0); for (int k = 0; k < 10 * wp; k++) { FloatOps::mul(term, neg_x2, term); FloatOps::div(term, Float(static_cast(2 * k + 2) * (2 * k + 3)), term); term.truncateToApprox(wp); FloatOps::div(term, Float(2 * k + 3), contribution); FloatOps::add(sum, contribution, sum); sum.truncateToApprox(wp); if (k >= 3 && contribution.isZero()) break; if (k >= 3) { auto c_bits = contribution.exponent() + static_cast(contribution.mantissa().bitLength()); auto s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - c_bits > wp + 5) break; } } if (negate) sum = -sum; finalizeResult(sum, eff_x, precision); return sum; } Float sinIntegral(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); return sinIntegral_core(Float(x), x.effectiveBits(), precision); } Float sinIntegral(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (x.isZero()) return Float(0); int eff = x.effectiveBits(); return sinIntegral_core(std::move(x), eff, precision); } //============================================================================= // 余弦積分 Ci(x) = γ + ln|x| + ∫₀ˣ (cos(t)-1)/t dt //============================================================================= // Ci(x) = γ + ln(x) + Σ_{k=1}^∞ (-1)^k · x^{2k} / (2k·(2k)!) // x > 0 のみ // 本体: x を値で受け取る (move 済み前提) static Float cosIntegral_core(Float x, int eff_x, int precision) { int wp = precision + 20; x.truncateToApprox(wp); Float euler_gamma = Float::euler(wp); Float ln_x = log(x, wp); Float sum = euler_gamma + ln_x; sum.truncateToApprox(wp); Float x2 = x * x; x2.truncateToApprox(wp); Float neg_x2 = -x2; // term tracks (-1)^k · x^{2k} / (2k)! // term_{k} = term_{k-1} · (-x²) / ((2k-1)·2k) // contribution = term / (2k) Float term(1); // k=0 初期値 Float contribution(0); for (int k = 1; k < 10 * wp; k++) { FloatOps::mul(term, neg_x2, term); FloatOps::div(term, Float(static_cast(2 * k - 1) * (2 * k)), term); term.truncateToApprox(wp); FloatOps::div(term, Float(2 * k), contribution); FloatOps::add(sum, contribution, sum); sum.truncateToApprox(wp); if (k >= 3 && contribution.isZero()) break; if (k >= 3) { auto c_bits = contribution.exponent() + static_cast(contribution.mantissa().bitLength()); auto s_bits = sum.exponent() + static_cast(sum.mantissa().bitLength()); if (s_bits - c_bits > wp + 5) break; } } finalizeResult(sum, eff_x, precision); return sum; } Float cosIntegral(const Float& x, int precision) { if (x.isNaN()) return Float::nan(); if (!x.isPositive()) { if (x.isZero()) return Float::negativeInfinity(); return Float::nan(); } return cosIntegral_core(Float(x), x.effectiveBits(), precision); } Float cosIntegral(Float&& x, int precision) { if (x.isNaN()) return Float::nan(); if (!x.isPositive()) { if (x.isZero()) return Float::negativeInfinity(); return Float::nan(); } int eff = x.effectiveBits(); return cosIntegral_core(std::move(x), eff, precision); } //============================================================================= // 乱数生成 //============================================================================= // [0, 1) の一様分布乱数を precision ビット精度で生成 Float randomFloat(int precision) { static thread_local std::mt19937_64 rng(std::random_device{}()); // precision ビットの乱数仮数を生成 int num_words = (precision + 63) / 64; std::vector words(num_words); for (int i = 0; i < num_words; i++) { words[i] = rng(); } // 余分なビットをマスク int extra_bits = num_words * 64 - precision; if (extra_bits > 0 && num_words > 0) { words[num_words - 1] &= (uint64_t(-1) >> extra_bits); } // Int に変換して 2^{-precision} を掛ける → [0, 1) の値 Int mantissa = Int::fromRawWords( std::span(words.data(), words.size()), +1); if (mantissa.isZero()) return Float(0); // Float(mantissa, exponent): value = mantissa * 2^exponent // mantissa は precision ビット以下の整数なので、 // exponent = -precision で [0, 1) になる Float result(mantissa, -static_cast(precision)); result.setResultPrecision(precision); return result; } // [lo, hi) の一様分布乱数 Float randomFloat(const Float& lo, const Float& hi, int precision) { Float r = randomFloat(precision); Float result = lo + r * (hi - lo); result.setResultPrecision(precision); return result; } Float randomFloat(Float&& lo, Float&& hi, int precision) { Float r = randomFloat(precision); Float range = std::move(hi) - lo; Float result = std::move(lo) + r * std::move(range); result.setResultPrecision(precision); return result; } //============================================================================= // normalRandom — 正規分布乱数 N(0,1), Box-Muller 法 //============================================================================= Float normalRandom(int precision) { // Box-Muller 法: u1, u2 ∈ (0, 1) から // z = sqrt(-2·ln(u1)) · cos(2π·u2) int wp = precision + 10; // u1 を (0, 1) で生成 (0 回避: log(0) = -∞) Float u1; do { u1 = randomFloat(wp); } while (u1.isZero()); Float u2 = randomFloat(wp); // r = sqrt(-2 * ln(u1)) Float neg2ln = Float(-2) * log(u1, wp); Float r = sqrt(std::move(neg2ln), wp); // theta = 2π * u2 Float theta = ldexp(Float::pi(wp), 1) * u2; Float result = r * cos(theta, wp); result.setResultPrecision(precision); return result; } //============================================================================= // exponentialRandom — 指数分布乱数 Exp(1), 逆変換法 //============================================================================= Float exponentialRandom(int precision) { // 逆変換法: -ln(u), u ∈ (0, 1) int wp = precision + 10; Float u; do { u = randomFloat(wp); } while (u.isZero()); Float result = -log(u, wp); result.setResultPrecision(precision); return result; } //============================================================================= // canRound — 丸め方向判定 (MPFR mpfr_can_round 互換) //============================================================================= bool canRound(const Float& x, int err_bits, RoundingMode rnd1, RoundingMode rnd2, int target_prec) { // 特殊値は常に丸め可能 if (x.isZero() || x.isNaN() || x.isInfinity()) return true; // 目標ビット数を算出 int target_bits = Float::precisionToBits(target_prec); // ガードビット数 = 正確ビット数 - 目標ビット数 int guard_bits = err_bits - target_bits; if (guard_bits <= 0) return false; // 精度不足 // 内部の canRoundCorrectly と同等のロジック int bit_length = static_cast(x.mantissa().bitLength()); if (bit_length <= target_bits) return true; // 丸め不要 // 判定に使えるガードビット数 (計算誤差を考慮し margin=2) constexpr int MARGIN = 2; int usable = guard_bits - MARGIN; if (usable < 1) return false; // ガードビット領域のスキャン // shift = bit_length - target_bits: 丸め位置より下のビット数 int shift = bit_length - target_bits; const uint64_t* data = x.mantissa().data(); // usable 個のガードビットを読み取る // 位置: bit (shift - 1) が最上位ガードビット int hi_pos = shift - 1; int lo_pos = shift - usable; if (lo_pos < 0) lo_pos = 0; bool all_zero = true; bool all_one = true; size_t lo_word = static_cast(lo_pos) / 64; size_t hi_word = static_cast(hi_pos) / 64; for (size_t w = lo_word; w <= hi_word; ++w) { uint64_t word = data[w]; unsigned lo_bit = (w == lo_word) ? static_cast(lo_pos % 64) : 0; unsigned hi_bit = (w == hi_word) ? static_cast(hi_pos % 64) : 63; unsigned width = hi_bit - lo_bit + 1; uint64_t mask; if (width >= 64) { mask = ~uint64_t(0); } else { mask = ((uint64_t(1) << width) - 1) << lo_bit; } uint64_t bits = word & mask; if (bits != 0) all_zero = false; if (bits != mask) all_one = false; if (!all_zero && !all_one) return true; } // 全 0 or 全 1 → 丸め境界に近すぎて方向不明 return false; } //============================================================================= // Float バイナリシリアライズ //============================================================================= std::vector exportBinary(const Float& value) { // tag: 0x00=zero, 0x01=normal, 0x02=infinity, 0x03=nan if (value.isNaN()) { return {0x03}; } if (value.isInfinity()) { uint8_t sign = value.isNegative() ? 0xFF : 0x00; return {0x02, sign}; } if (value.isZero()) { return {0x00}; } // Normal value // [tag:1][sign:1][exponent:8 LE][eff:4 LE][req:4 LE][mantissa_binary...] uint8_t sign = value.isNegative() ? 0xFF : 0x00; int64_t exp = value.exponent(); int eff = value.effectiveBits(); int req = value.requestedBits(); // mantissa をシリアライズ auto mant_bin = IntIOUtils::exportBinary(value.mantissa()); std::vector result; result.reserve(1 + 1 + 8 + 4 + 4 + mant_bin.size()); result.push_back(0x01); // tag result.push_back(sign); // exponent LE for (int i = 0; i < 8; ++i) result.push_back(static_cast((static_cast(exp) >> (i * 8)) & 0xFF)); // effective_bits LE for (int i = 0; i < 4; ++i) result.push_back(static_cast((static_cast(eff) >> (i * 8)) & 0xFF)); // requested_bits LE for (int i = 0; i < 4; ++i) result.push_back(static_cast((static_cast(req) >> (i * 8)) & 0xFF)); // mantissa binary result.insert(result.end(), mant_bin.begin(), mant_bin.end()); return result; } Float importBinaryFloat(std::span data) { if (data.empty()) { throw std::invalid_argument("importBinaryFloat: empty data"); } uint8_t tag = data[0]; if (tag == 0x00) return Float::zero(); if (tag == 0x03) return Float::nan(); if (tag == 0x02) { if (data.size() < 2) throw std::invalid_argument("importBinaryFloat: truncated infinity"); return data[1] == 0xFF ? Float::negativeInfinity() : Float::positiveInfinity(); } if (tag != 0x01) throw std::invalid_argument("importBinaryFloat: unknown tag"); // Normal: [tag:1][sign:1][exp:8][eff:4][req:4][mantissa...] if (data.size() < 18) throw std::invalid_argument("importBinaryFloat: truncated normal value"); bool neg = (data[1] == 0xFF); int64_t exp = 0; for (int i = 0; i < 8; ++i) exp |= static_cast(data[2 + i]) << (i * 8); int eff = 0; for (int i = 0; i < 4; ++i) eff |= static_cast(data[10 + i]) << (i * 8); int req = 0; for (int i = 0; i < 4; ++i) req |= static_cast(data[14 + i]) << (i * 8); // mantissa auto mant_span = data.subspan(18); Int mantissa = IntIOUtils::importBinary(mant_span); Float result(std::move(mantissa), exp, neg); result.effective_bits_ = eff; result.requested_bits_ = req; return result; } } // namespace calx