利用者:Ef3/Hypot2
表示
< 利用者:Ef3
#include <stdio.h> #include <math.h> #include <stdint.h> #include <float.h> #define SPLIT (0x1.0p27 + 1) // 16進浮動小数点リテラルを使用 // sq関数: 高精度の平方計算 static void sq(double x, double *hi, double *lo) { double xc = x * SPLIT; double xh = x - xc + xc; double xl = x - xh; *hi = x * x; *lo = xh * xh - *hi + 2.0 * xh * xl + xl * xl; } // hypot関数: 高精度の2つの値のユークリッド距離 double hypot_custom(double x, double y) { // x と y を符号なし64ビット整数に変換 uint64_t ux = *(uint64_t*)&x, uy = *(uint64_t*)&y; // 絶対値を取得 ux &= ~(1ULL << 63); // 符号ビットをクリア uy &= ~(1ULL << 63); // 符号ビットをクリア // |x| >= |y| を確認 if (ux < uy) { uint64_t tmp = ux; ux = uy; uy = tmp; } // 指数部を取り出す int ex = ux >> 52; int ey = uy >> 52; // 浮動小数点数に戻す x = *(double*)&ux; y = *(double*)&uy; // 特殊ケース処理 if (ey == 0x7FF) { return y; // y が NaN または Inf の場合 } if (ex == 0x7FF || uy == 0.0) { return x; // x が NaN または Inf または y が 0 の場合 } if (ex - ey > 64) { return x + y; // 大きな差がある場合、単純に x + y を返す } // 精密な平方根計算の準備 double z = 1.0; if (ex > 0x3FF + 510) { z = 0x1.0p700; // 2^700 x *= 0x1.0p-700; // x と y をスケーリング y *= 0x1.0p-700; } else if (ey < 0x3FF - 450) { z = 0x1.0p-700; // 2^-700 x *= 0x1.0p700; // x と y をスケーリング y *= 0x1.0p700; } // 高精度の平方計算 double hx, lx, hy, ly; sq(x, &hx, &lx); sq(y, &hy, &ly); // 最終的な平方根の計算 return z * sqrt(lx + ly + hx + hy); } int main() { printf("Hypotenuse: %1$g(%1$a) %2$g(%2$a)\n", hypot_custom(3.0, 4.0), hypot(3,4)); printf("Hypotenuse: %1$g(%1$a) %2$g(%2$a)\n", hypot_custom(1e300, 1e300), hypot(1e300, 1e300)); printf("Hypotenuse: %1$g(%1$a) %2$g(%2$a)\n", hypot_custom(1e-300, 1e-300), hypot(1e-300, 1e-300)); printf("Hypotenuse: %1$g(%1$a) %2$g(%2$a)\n", hypot_custom(1e-300, 1e300), hypot(1e-300, 1e300)); printf("Hypotenuse: %1$g(%1$a) %2$g(%2$a)\n", hypot_custom(1e300, 1e-300), hypot(1e300, 1e-300)); return 0; }
const SPLIT = Math.pow(2, 27) + 1; // FLT_EVAL_METHODに基づくSPLITの選択 function sq(x) { const xc = x * SPLIT; const xh = x - xc + xc; const xl = x - xh; const hi = x * x; const lo = xh * xh - hi + 2 * xh * xl + xl * xl; return { hi, lo }; } function hypot(x, y) { // 絶対値を取得 x = Math.abs(x); y = Math.abs(y); // |x| >= |y| となるように並べ替え if (x < y) { [x, y] = [y, x]; } const ex = Math.floor(Math.log2(x)); const ey = Math.floor(Math.log2(y)); // 特殊ケース処理 if (!isFinite(y)) return y; if (!isFinite(x) || y === 0) return x; if (ex - ey > 64) return x + (y * y) / (2 * x); // 精密な平方根計算の準備 let z = 1; if (ex > 510) { z = Math.pow(2, 700); x *= Math.pow(2, -700); y *= Math.pow(2, -700); } else if (ey < -450) { z = Math.pow(2, -700); x *= Math.pow(2, 700); y *= Math.pow(2, 700); } const { hi: hx, lo: lx } = sq(x); const { hi: hy, lo: ly } = sq(y); return z * Math.sqrt(ly + lx + hy + hx); } // テスト console.log(hypot(3, 4)); // 5.0 console.log(hypot(1e-300, 1e-300)); console.log(hypot(1e300, 1e300)); // テストコード function compareResults(x, y) { const h1 = hypot(x, y); const h2 = Math.sqrt(x * x + y * y); const h3 = Math.hypot(x, y); console.log(`\nTest case: x=${x.toPrecision(17)}, y=${y.toPrecision(17)}`); console.log(`Custom hypot: ${h1.toPrecision(17)}`); console.log(`Simple hypot: ${h2.toPrecision(17)}`); console.log(`Standard hypot: ${h3.toPrecision(17)}`); // NaNチェック if (Number.isNaN(h1) || Number.isNaN(h2) || Number.isNaN(h3)) { console.log("NaN検出!"); console.log(`Custom IsNaN: ${Number.isNaN(h1)}`); console.log(`Simple IsNaN: ${Number.isNaN(h2)}`); console.log(`Standard IsNaN: ${Number.isNaN(h3)}`); return; } // 結果比較 if (h1 !== h2 || h1 !== h3) { console.log("差異検出!"); if (!Number.isFinite(h1) || !Number.isFinite(h2)) { console.log("Custom vs Simple: 少なくとも1つが無限大"); } else { const relErr12 = Math.abs(h1 - h2) / Math.max(Math.abs(h1), Math.abs(h2)); console.log(`Custom vs Simple 相対誤差: ${relErr12.toPrecision(17)}`); } if (!Number.isFinite(h1) || !Number.isFinite(h3)) { console.log("Custom vs Standard: 少なくとも1つが無限大"); } else { const relErr13 = Math.abs(h1 - h3) / Math.max(Math.abs(h1), Math.abs(h3)); console.log(`Custom vs Standard 相対誤差: ${relErr13.toPrecision(17)}`); } } } // テストの実行 console.log("=== 通常のケース ==="); compareResults(3, 4); compareResults(1e-8, 1e-8); console.log("\n=== 極端な値のケース ==="); compareResults(1e300, 1e300); compareResults(1e-300, 1e-300); console.log("\n=== 非常に異なる大きさの値 ==="); compareResults(1e308, 1e-308); compareResults(1e50, 1e-50); console.log("\n=== 特殊な値 ==="); compareResults(Infinity, 1.0); compareResults(NaN, 1.0); compareResults(0.0, 0.0); console.log("\n=== オーバーフローが起こりやすいケース ==="); compareResults(1e308, 1e308); compareResults(Number.MAX_VALUE/2, Number.MAX_VALUE/2); console.log("\n=== アンダーフローが起こりやすいケース ==="); compareResults(Number.MIN_VALUE, Number.MIN_VALUE); compareResults(1e-308, 1e-308);
const SPLIT: f64 = (1u64 << 27) as f64 + 1.0; // SPLIT の定義 fn sq(x: f64) -> (f64, f64) { let xc = x * SPLIT; let xh = x - xc + xc; let xl = x - xh; let hi = x * x; let lo = xh * xh - hi + 2.0 * xh * xl + xl * xl; (hi, lo) } fn hypot(x: f64, y: f64) -> f64 { // 絶対値を取得 let mut x = x.abs(); let mut y = y.abs(); // |x| >= |y| となるように並べ替え if x < y { std::mem::swap(&mut x, &mut y); } let ex = x.to_bits() >> 52 & 0x7FF; // x の指数部 let ey = y.to_bits() >> 52 & 0x7FF; // y の指数部 // 特殊ケース処理 if ey == 0x7FF { return y; // y が NaN または inf の場合 } if ex == 0x7FF || y == 0.0 { return x; // x が inf または y が 0 の場合 } if ex as i32 - ey as i32 > 64 { return x + y * y / (2.0 * x); // |x| >> |y| の場合 } // 精密な平方根計算の準備 let mut z = 1.0; if ex > 1023 + 510 { z = 2f64.powi(700); x *= 2f64.powi(-700); y *= 2f64.powi(-700); } else if ey < 1023 - 450 { z = 2f64.powi(-700); x *= 2f64.powi(700); y *= 2f64.powi(700); } let (hx, lx) = sq(x); let (hy, ly) = sq(y); z * (lx + ly + hx + hy).sqrt() } fn main() { let x = 3.0; let y = 4.0; println!("{}", hypot(x, y)); // 5.0 }
#include <iostream> #include <cmath> #include <cstdint> #include <cstring> // 追加: memcpyを使うために必要 // FLT_EVAL_METHOD に基づく SPLIT の定義 #if FLT_EVAL_METHOD > 1 && LDBL_MANT_DIG == 64 #define SPLIT (0x1p32 + 1) #else #define SPLIT (0x1p27 + 1) #endif // sq関数 void sq(double x, double& hi, double& lo) { double xc = x * SPLIT; double xh = x - xc + xc; double xl = x - xh; hi = x * x; lo = xh * xh - hi + 2.0 * xh * xl + xl * xl; } // hypot関数 double hypot(double x, double y) { uint64_t ux, uy; std::memcpy(&ux, &x, sizeof(x)); // 修正: memcpyはstd::ではなくグローバル名前空間にある std::memcpy(&uy, &y, sizeof(y)); // 修正: 同様 // 絶対値を取得 ux &= ~((uint64_t)1 << 63); uy &= ~((uint64_t)1 << 63); if (ux < uy) { std::swap(ux, uy); } int ex = (ux >> 52) & 0x7FF; int ey = (uy >> 52) & 0x7FF; x = *reinterpret_cast<double*>(&ux); y = *reinterpret_cast<double*>(&uy); // 特殊ケース処理 if (ey == 0x7FF) { return y; } if (ex == 0x7FF || y == 0.0) { return x; } if (ex - ey > 64) { return x + y; } // 精密な平方根計算の準備 double z = 1.0; if (ex > 1023 + 510) { z = std::pow(2.0, 700); x *= std::pow(2.0, -700); y *= std::pow(2.0, -700); } else if (ey < 1023 - 450) { z = std::pow(2.0, -700); x *= std::pow(2.0, 700); y *= std::pow(2.0, 700); } double hx, lx, hy, ly; sq(x, hx, lx); sq(y, hy, ly); return z * std::sqrt(lx + ly + hx + hy); } int main() { double x = 3.0; double y = 4.0; std::cout << hypot(x, y) << std::endl; // 5.0 return 0; }
import struct import math # FLT_EVAL_METHOD に基づく SPLIT の定義 SPLIT = (2 ** 27 + 1) # それに相当する値を定義 # sq関数 def sq(x): xc = x * SPLIT xh = x - xc + xc xl = x - xh hi = x * x lo = xh * xh - hi + 2.0 * xh * xl + xl * xl return hi, lo # hypot関数 def hypot(x, y): # x と y のビットパターンを整数に変換する ux = struct.unpack('>Q', struct.pack('>d', x))[0] uy = struct.unpack('>Q', struct.pack('>d', y))[0] # 絶対値を取得 ux &= ~((1 << 63)) # 符号ビットをクリア uy &= ~((1 << 63)) # 同様 # |x| >= |y| を確認 if ux < uy: ux, uy = uy, ux # 指数部の取得 ex = (ux >> 52) & 0x7FF ey = (uy >> 52) & 0x7FF x = struct.unpack('>d', struct.pack('>Q', ux))[0] y = struct.unpack('>d', struct.pack('>Q', uy))[0] # 特殊ケース処理 if ey == 0x7FF: return y if ex == 0x7FF or y == 0.0: return x if ex - ey > 64: return x + y # 精密な平方根計算の準備 z = 1.0 if ex > 1023 + 510: z = 2 ** 700 x *= 2 ** -700 y *= 2 ** -700 elif ey < 1023 - 450: z = 2 ** -700 x *= 2 ** 700 y *= 2 ** 700 # 平方根の計算 hx, lx = sq(x) hy, ly = sq(y) return z * math.sqrt(lx + ly + hx + hy) # 実行例 x = 3.0 y = 4.0 print(hypot(x, y)) # 結果: 5.0