C#を利用したアプリケーション開発において、数値計算は避けて通れない要素の一つです。

その中でも「平方根(ルート)」の計算は、グラフィックス処理、物理シミュレーション、データ統計、さらには機械学習のロジックなど、非常に幅広い場面で活用されます。

かつてのC#ではMath.Sqrtを用いるのが唯一の標準的な方法でしたが、現在の.NET環境では、パフォーマンスや型の柔軟性を考慮した複数の最適なアプローチが存在します。

本記事では、初心者の方からパフォーマンスを追求するエンジニアまで役立つ、C#における平方根計算の決定版を紹介します。

基本的な平方根の計算:Math.Sqrt と MathF.Sqrt

C#で最も一般的かつ標準的な平方根の計算方法は、System.Mathクラスが提供する静的メソッドを利用することです。

Math.Sqrt (double型) の利用

Math.Sqrtは、引数としてdouble(倍精度浮動小数点数)を受け取り、その平方根を返します。

これは.NETの初期から存在する最も信頼性の高いメソッドです。

C#
using System;

class Program
{
    static void Main()
    {
        double value = 25.0;
        // Math.Sqrtを使用して平方根を計算
        double result = Math.Sqrt(value);

        Console.WriteLine($"値: {value}");
        Console.WriteLine($"平方根: {result}");
    }
}
実行結果
値: 25
平方根: 5

MathF.Sqrt (float型) の利用

ゲーム開発(Unityなど)やリアルタイム処理では、メモリ消費量や計算速度の観点からfloat(単精度浮動小数点数)が多用されます。

かつては(float)Math.Sqrt(value)のようにキャストが必要でしたが、現在のC#ではfloat型に特化したMathF.Sqrtを利用するのがベストプラクティスです。

C#
float distanceSquared = 10.0f;
// float型のまま計算できるため、キャストのオーバーヘッドがない
float distance = MathF.Sqrt(distanceSquared);

MathFを利用することで、コードの可読性が向上するだけでなく、ランタイム側で最適化されたCPU命令が直接呼び出されるため、計算効率が向上します。

高精度な計算が必要な場合:decimal型での平方根

金融系のシステムなど、誤差が許されない計算ではdecimal型が使用されます。

しかし、驚くべきことに.NETの標準ライブラリにはdecimalを受け取るSqrtメソッドが存在しません。

Math.Sqrt((double)decimalValue)のようにキャストして計算することも可能ですが、これではdoubleに変換した時点で精度が落ちてしまいます。

decimalの精度を維持したまま平方根を求めるには、一般的にニュートン法を用いたカスタム実装が行われます。

ニュートン法による実装例

以下は、decimal型で高精度な平方根を求めるための実装コードです。

C#
using System;

public static class DecimalMath
{
    public static decimal Sqrt(decimal x, decimal epsilon = 0.0m)
    {
        if (x < 0) throw new OverflowException("負の数の平方根は計算できません。");
        if (x == 0) return 0;

        decimal current = (decimal)Math.Sqrt((double)x);
        decimal previous;

        do
        {
            previous = current;
            if (previous == 0) return 0;
            // ニュートン法の公式: x_{n+1} = (x_n + x / x_n) / 2
            current = (previous + x / previous) / 2;
        }
        while (Math.Abs(previous - current) > epsilon && previous != current);

        return current;
    }
}

// 使用例
decimal dValue = 2.0m;
decimal dResult = DecimalMath.Sqrt(dValue);
Console.WriteLine($"decimalでの √2: {dResult}");
実行結果
decimalでの √2: 1.4142135623730950488016887242

このように、用途に応じて「速度のdouble/float」か「精度のdecimal」かを適切に選択することが重要です。

最新のC#における「ジェネリック数学」を活用した実装

C# 11以降、.NETにはジェネリック数学(Generic Math)という画期的な機能が導入されました。

これにより、特定の型(doubleやintなど)に依存せず、数値型であれば何でも受け取れる汎用的な計算メソッドを記述できるようになりました。

IRootFunctions<TSelf> インターフェースの活用

最新のC#では、IRootFunctions<TSelf>インターフェースを実装している型であれば、共通のロジックで平方根を計算できます。

C#
using System;
using System.Numerics;

class GenericCalculator
{
    // Tが数値かつ平方根計算をサポートしていることを制約に指定
    public static T CalculateSqrt<T>(T value) where T : IRootFunctions<T>
    {
        return T.Sqrt(value);
    }
}

class Program
{
    static void Main()
    {
        double d = GenericCalculator.CalculateSqrt(25.0);
        float f = GenericCalculator.CalculateSqrt(16.0f);
        
        Console.WriteLine($"Double: {d}, Float: {f}");
    }
}

この手法の最大のメリットは、コードの重複を排除できる点にあります。

将来的に新しい数値型(例えば新しい128ビット浮動小数点数型など)が登場したとしても、このメソッドはそのまま動作します。

手法推奨される型特徴
Math.Sqrtdouble最も一般的、高い汎用性
MathF.Sqrtfloatパフォーマンス重視、ゲーム開発向け
ニュートン法decimal高精度な金融計算向け
ジェネリック数学T (汎用)再利用性の高いライブラリ設計向け

パフォーマンスを意識した高度な手法

大量のデータ(配列など)に対して一括で平方根を計算する場合、ループ内でMath.Sqrtを何度も呼び出すのは効率的ではありません。

このようなケースでは、SIMD(Single Instruction, Multiple Data)を活用した並列計算が有効です。

System.Numerics.Vector による一括計算

.NETのSystem.Numerics名前空間には、CPUのSIMD命令を直接活用するためのクラスが用意されています。

C#
using System;
using System.Numerics;

public void CalculateBulk(float[] data)
{
    int vectorSize = Vector<float>.Count;
    int i = 0;

    // SIMD命令を使用して一括計算
    for (; i <= data.Length - vectorSize; i += vectorSize)
    {
        var vector = new Vector<float>(data, i);
        var result = Vector.SquareRoot(vector);
        result.CopyTo(data, i);
    }

    // 残りの要素を個別に計算
    for (; i < data.Length; i++)
    {
        data[i] = MathF.Sqrt(data[i]);
    }
}

このコードでは、CPUが1回の命令で複数の数値(例えば8つのfloat値)に対して同時に平方根を計算するため、単純なループと比較して数倍から十数倍の高速化が期待できます。

大量のセンサーデータ処理や画像フィルター処理を実装する際には必須のテクニックです。

計算時の注意点とエラーハンドリング

平方根の計算には、数学的な制約に伴う注意点がいくつかあります。

これらを無視すると、実行時に予期しない値が返されたり、バグの原因となったりします。

負の数と非数 (NaN)

実数の範囲では、負の数の平方根を求めることはできません。

C#において、負の数をMath.Sqrtに渡すと、double.NaN(Not a Number:非数)が返されます。

C#
double negativeInput = -1.0;
double result = Math.Sqrt(negativeInput);

if (double.IsNaN(result))
{
    Console.WriteLine("エラー:負の数の平方根は実数の範囲で定義されていません。");
}

例外はスローされないため、戻り値がNaNであるかどうかをチェックする習慣をつけることが重要です。

また、無限大(double.PositiveInfinity)を渡した場合は無限大が返されます。

精度による比較の注意

平方根の計算結果は多くの場合、無限小数を伴う近似値となります。

そのため、if (Math.Sqrt(4) == 2.0) のような直接的な比較は安全ですが、Math.Sqrt(2) * Math.Sqrt(2) == 2.0 は計算誤差によってfalseになる可能性があります。

浮動小数点数を比較する際は、常に微小な差(エプシロン)を許容するアルゴリズムを採用してください。

まとめ

C#で平方根を計算する方法は、単なるMath.Sqrtの利用に留まらず、現代の.NETでは用途に応じた多様な選択肢が用意されています。

  • 一般的な開発では、標準的な Math.Sqrt
  • パフォーマンスが要求されるゲーム等では、軽量な MathF.Sqrt
  • 金融などの高精度計算では、decimal 型とニュートン法の組み合わせ。
  • 抽象度の高いライブラリ作成では、ジェネリック数学
  • 大量のデータ処理では、SIMDによるベクトル演算。

これらの手法を適切に使い分けることで、効率的で堅牢なアプリケーションを構築することができます。

2026年現在のC#開発においては、特に型安全とパフォーマンスを両立させるジェネリック数学の活用が、モダンなコードを書くための重要なステップとなるでしょう。

計算対象のデータの性質と、求められる精度・速度を鑑みて、最適なメソッドを選択してください。