PHPで浮動小数点数を扱う際、「この値はNaN(Not a Number)なのか、それとも有効な数値なのか?」を確認したい場面があります。そんなときに便利なのがis_nan関数です。
この記事では、PHPのis_nan関数の基本的な使い方から実践的な活用方法まで、初心者にも分かりやすく解説します。
is_nan関数とは?
is_nan関数は、指定された値がNaN(Not a Number)かどうかを判定するPHPの組み込み関数です。NaNは、数学的に定義されていない演算の結果として生成される特別な浮動小数点値です。
基本構文
is_nan(float $value): bool
- 引数:
$value
– 判定したい浮動小数点数 - 戻り値: NaNの場合は
true
、そうでなければfalse
NaN(Not a Number)とは?
NaNは浮動小数点演算において、数学的に定義されていない結果を表現するための特別な値です。以下のような演算で生成されます:
- 0を0で割る
- 無限大から無限大を引く
- 負の数の平方根を計算する
- 不正な演算結果
NaNの特徴
<?php
// NaNの生成例
$nan1 = sqrt(-1); // 負の数の平方根
$nan2 = acos(2); // 定義域外のアークコサイン
$nan3 = log(-1); // 負の数の自然対数
$nan4 = 0 / 0; // 不定形
// NaNの特別な性質
var_dump($nan1); // float(NAN)
var_dump($nan1 == $nan1); // bool(false) - NaNは自分自身とも等しくない
var_dump($nan1 === $nan1); // bool(false)
?>
is_nan関数の基本的な使い方
基本例
<?php
// 通常の数値
$normal = 3.14;
var_dump(is_nan($normal)); // bool(false)
// NaN値
$nan = sqrt(-1);
var_dump(is_nan($nan)); // bool(true)
// 無限大
$infinity = INF;
var_dump(is_nan($infinity)); // bool(false) - 無限大はNaNではない
// 整数
$integer = 42;
var_dump(is_nan($integer)); // bool(false)
// 文字列
$string = "hello";
var_dump(is_nan($string)); // bool(false)
?>
NaNを生成する様々な演算
<?php
function testNanOperations() {
$operations = [
'sqrt(-1)' => sqrt(-1),
'acos(2)' => acos(2),
'log(-1)' => log(-1),
'0/0' => 0/0,
'INF - INF' => INF - INF,
'INF / INF' => INF / INF,
'pow(-1, 0.5)' => pow(-1, 0.5)
];
foreach ($operations as $operation => $result) {
echo "{$operation}: " . var_export($result, true) .
" - NaN? " . (is_nan($result) ? "Yes" : "No") . "\n";
}
}
testNanOperations();
?>
実践的な活用例
1. 数学的計算の結果検証
<?php
function safeSquareRoot($number) {
if (!is_numeric($number)) {
throw new InvalidArgumentException("数値を入力してください");
}
$result = sqrt($number);
if (is_nan($result)) {
throw new DomainException("負の数の平方根は計算できません");
}
return $result;
}
// 使用例
try {
echo safeSquareRoot(9); // 3
echo safeSquareRoot(0); // 0
echo safeSquareRoot(-1); // 例外: 負の数の平方根は計算できません
} catch (Exception $e) {
echo "エラー: " . $e->getMessage();
}
?>
2. 統計計算での異常値検出
<?php
function calculateStatistics($numbers) {
if (empty($numbers)) {
throw new InvalidArgumentException("空の配列です");
}
$sum = array_sum($numbers);
$count = count($numbers);
$mean = $sum / $count;
// 分散の計算
$squaredDifferences = array_map(function($x) use ($mean) {
return pow($x - $mean, 2);
}, $numbers);
$variance = array_sum($squaredDifferences) / $count;
$standardDeviation = sqrt($variance);
// NaNをチェック
$results = [
'mean' => $mean,
'variance' => $variance,
'standard_deviation' => $standardDeviation
];
foreach ($results as $key => $value) {
if (is_nan($value)) {
throw new RuntimeException("計算結果に異常値が含まれています: {$key}");
}
}
return $results;
}
// 使用例
try {
$data = [1, 2, 3, 4, 5];
$stats = calculateStatistics($data);
print_r($stats);
} catch (Exception $e) {
echo "エラー: " . $e->getMessage();
}
?>
3. データクリーニング処理
<?php
function cleanNumericData($data) {
$cleaned = [];
$errors = [];
foreach ($data as $index => $value) {
// 数値に変換
$numericValue = (float)$value;
// NaNをチェック
if (is_nan($numericValue)) {
$errors[] = "インデックス {$index}: 無効な数値 '{$value}'";
continue;
}
// 無限大をチェック
if (is_infinite($numericValue)) {
$errors[] = "インデックス {$index}: 無限大の値 '{$value}'";
continue;
}
$cleaned[] = $numericValue;
}
return [
'cleaned_data' => $cleaned,
'errors' => $errors
];
}
// 使用例
$rawData = [1, 2.5, "3", "abc", sqrt(-1), INF, "4.5"];
$result = cleanNumericData($rawData);
echo "クリーンなデータ: " . implode(", ", $result['cleaned_data']) . "\n";
echo "エラー:\n";
foreach ($result['errors'] as $error) {
echo "- {$error}\n";
}
?>
4. 金融計算での安全性確保
<?php
class FinancialCalculator {
public function calculateCompoundInterest($principal, $rate, $time, $compounds) {
// 入力値の検証
if (!is_numeric($principal) || !is_numeric($rate) ||
!is_numeric($time) || !is_numeric($compounds)) {
throw new InvalidArgumentException("すべての引数は数値である必要があります");
}
if ($principal <= 0 || $rate < 0 || $time < 0 || $compounds <= 0) {
throw new InvalidArgumentException("引数は適切な範囲内である必要があります");
}
// 複利計算
$result = $principal * pow((1 + $rate / $compounds), $compounds * $time);
// NaNやInfinityをチェック
if (is_nan($result)) {
throw new RuntimeException("計算結果が無効です (NaN)");
}
if (is_infinite($result)) {
throw new RuntimeException("計算結果が無限大です");
}
return round($result, 2);
}
public function calculateLoanPayment($principal, $rate, $periods) {
if ($rate == 0) {
return $principal / $periods;
}
$monthlyRate = $rate / 12;
$payment = $principal * ($monthlyRate * pow(1 + $monthlyRate, $periods)) /
(pow(1 + $monthlyRate, $periods) - 1);
if (is_nan($payment)) {
throw new RuntimeException("ローン支払額の計算に失敗しました");
}
return round($payment, 2);
}
}
// 使用例
$calculator = new FinancialCalculator();
try {
$compound = $calculator->calculateCompoundInterest(1000, 0.05, 10, 12);
echo "複利計算結果: {$compound}円\n";
$payment = $calculator->calculateLoanPayment(300000, 0.035, 360);
echo "月々の支払額: {$payment}円\n";
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
?>
is_nan関数と関連する関数
is_finite、is_infinite関数との組み合わせ
<?php
function analyzeFloatValue($value) {
echo "値: " . var_export($value, true) . "\n";
echo "is_nan: " . (is_nan($value) ? "true" : "false") . "\n";
echo "is_finite: " . (is_finite($value) ? "true" : "false") . "\n";
echo "is_infinite: " . (is_infinite($value) ? "true" : "false") . "\n";
echo "---\n";
}
// テストケース
analyzeFloatValue(3.14); // 通常の数値
analyzeFloatValue(INF); // 正の無限大
analyzeFloatValue(-INF); // 負の無限大
analyzeFloatValue(sqrt(-1)); // NaN
analyzeFloatValue(0); // ゼロ
?>
is_numeric関数との違い
<?php
function compareNumericChecks($value) {
echo "値: " . var_export($value, true) . "\n";
echo "is_numeric: " . (is_numeric($value) ? "true" : "false") . "\n";
echo "is_nan: " . (is_nan($value) ? "true" : "false") . "\n";
echo "---\n";
}
compareNumericChecks(42); // is_numeric: true, is_nan: false
compareNumericChecks("42"); // is_numeric: true, is_nan: false
compareNumericChecks(3.14); // is_numeric: true, is_nan: false
compareNumericChecks(sqrt(-1)); // is_numeric: false, is_nan: true
compareNumericChecks("hello"); // is_numeric: false, is_nan: false
?>
高度な使用例
1. 配列の数値データ検証
<?php
function validateNumericArray($array) {
$valid = [];
$invalid = [];
foreach ($array as $key => $value) {
$floatValue = (float)$value;
if (is_nan($floatValue)) {
$invalid[$key] = "NaN: {$value}";
} elseif (is_infinite($floatValue)) {
$invalid[$key] = "Infinite: {$value}";
} elseif (!is_numeric($value)) {
$invalid[$key] = "Non-numeric: {$value}";
} else {
$valid[$key] = $floatValue;
}
}
return [
'valid' => $valid,
'invalid' => $invalid
];
}
// 使用例
$data = [1, 2.5, "3", "abc", sqrt(-1), INF, null, "4.5"];
$result = validateNumericArray($data);
echo "有効なデータ:\n";
print_r($result['valid']);
echo "\n無効なデータ:\n";
print_r($result['invalid']);
?>
2. 科学計算での精度チェック
<?php
class ScientificCalculator {
public function quadraticFormula($a, $b, $c) {
if ($a == 0) {
throw new InvalidArgumentException("二次方程式のaは0にできません");
}
$discriminant = $b * $b - 4 * $a * $c;
if ($discriminant < 0) {
// 複素数解の場合
$realPart = -$b / (2 * $a);
$imaginaryPart = sqrt(-$discriminant) / (2 * $a);
return [
'type' => 'complex',
'solutions' => [
['real' => $realPart, 'imaginary' => $imaginaryPart],
['real' => $realPart, 'imaginary' => -$imaginaryPart]
]
];
}
$sqrtDiscriminant = sqrt($discriminant);
$solution1 = (-$b + $sqrtDiscriminant) / (2 * $a);
$solution2 = (-$b - $sqrtDiscriminant) / (2 * $a);
// NaNチェック
if (is_nan($solution1) || is_nan($solution2)) {
throw new RuntimeException("計算結果にNaNが含まれています");
}
return [
'type' => 'real',
'solutions' => [$solution1, $solution2]
];
}
public function calculateSeries($n, $formula) {
$sum = 0;
for ($i = 1; $i <= $n; $i++) {
$term = $formula($i);
if (is_nan($term)) {
throw new RuntimeException("項 {$i} でNaNが発生しました");
}
if (is_infinite($term)) {
throw new RuntimeException("項 {$i} で無限大が発生しました");
}
$sum += $term;
}
return $sum;
}
}
// 使用例
$calc = new ScientificCalculator();
try {
// 二次方程式: x^2 - 5x + 6 = 0
$result = $calc->quadraticFormula(1, -5, 6);
print_r($result);
// 調和級数の部分和
$harmonicSum = $calc->calculateSeries(100, function($n) {
return 1 / $n;
});
echo "調和級数の部分和: {$harmonicSum}\n";
} catch (Exception $e) {
echo "エラー: " . $e->getMessage() . "\n";
}
?>
デバッグとトラブルシューティング
1. NaNの発生源を特定する
<?php
function debugNanIssue($operations) {
foreach ($operations as $name => $operation) {
try {
$result = $operation();
if (is_nan($result)) {
echo "⚠️ NaN発生: {$name}\n";
echo "結果: " . var_export($result, true) . "\n";
} else {
echo "✅ 正常: {$name} = {$result}\n";
}
} catch (Exception $e) {
echo "❌ エラー: {$name} - " . $e->getMessage() . "\n";
}
echo "---\n";
}
}
// デバッグ用の演算
$operations = [
'sqrt(-4)' => function() { return sqrt(-4); },
'log(0)' => function() { return log(0); },
'acos(3)' => function() { return acos(3); },
'atan2(0, 0)' => function() { return atan2(0, 0); },
'0/0' => function() { return 0/0; },
'INF - INF' => function() { return INF - INF; }
];
debugNanIssue($operations);
?>
2. NaN値の処理方法
<?php
function handleNanValues($data) {
$processed = [];
foreach ($data as $value) {
if (is_nan($value)) {
// NaNの処理方法を選択
$processed[] = 0; // デフォルト値に置換
// または
// continue; // スキップ
// throw new Exception("NaN detected"); // エラーとして扱う
} else {
$processed[] = $value;
}
}
return $processed;
}
// 使用例
$data = [1, 2, sqrt(-1), 4, log(-1), 6];
$clean = handleNanValues($data);
echo "処理後のデータ: " . implode(", ", $clean) . "\n";
?>
まとめ
is_nan関数は、PHPで浮動小数点演算の結果を安全に処理するために不可欠な関数です。主な特徴をまとめると:
- 目的: NaN(Not a Number)値を検出する
- 戻り値: boolean型(true/false)
- 適用場面: 数学的計算、統計処理、データクリーニング、金融計算など
- 注意点: NaNは自分自身とも等しくない特殊な値
- 関連関数: is_finite、is_infinite、is_numeric との組み合わせが有効
科学計算や統計処理、金融アプリケーションを開発する際は、is_nan関数を活用して計算結果の妥当性を確認することが重要です。特に、ユーザー入力に基づく計算や、複雑な数学的演算を行う場合には、予期しないNaN値の発生を防ぐために積極的に使用することをお勧めします。
実際の開発では、is_finite()
、is_infinite()
、is_numeric()
などの関数と組み合わせて、包括的な数値検証システムを構築することが効果的です。