はじめに
$e^{iπ} = -1$
は、「世界一美しい数式」といわれているオイラーの等式です。
確かに美しいというか無駄がないという感じですが、これをプログラムで出力してみたいと思いました。
ただ、それだけです。
とはいえ、一見簡単そうな数式ですが、プログラムで表現するのは少しだけ手間です。
- $e$や$π$といった無理数を扱う必要がある。
- 複素数を扱う必要がある。
- さらに、複素数のべき乗を扱う必要がある。
実行環境
オンラインでコンパイル可能な以下のいずれかで実行確認しました。
Paiza.io
TIO
D言語の場合
- $e$は
E
、$π$は、PI
という定数定義がstd.math
で用意されている。 - 複素数は、
complex
テンプレートが用意されている。 - べき乗は
^^
演算子を利用できる。
import std;
void main()
{
auto x = complex(cast(real)0.0, PI);
auto e = complex(E, cast(real)0.0);
auto ans = e ^^ x;
writeln(ans);
writefln("%.10f %.10f", ans.re, ans.im);
}
-1-5.01656e-20i
-1.0000000000 -0.0000000000
writeln(ans)
ように、演算結果をそのまま出力すると、-1-5.01656e-20i
と誤差が発生します。
他のプログラミング言語でもそうですが、無理数を浮動小数で扱う以上、このようになります。
ちなみに、D言語の場合、倍精度(double
)よりも精度が高いreal
型を利用できるので、虚数部の誤差が5.01656e-20
と、より少なくなっています。
double
型を利用すると、他のプログラミング言語と同様-1+1.22465e-16i
となります。
誤差を丸めて表示するためにwritefln("%.10f %.10f", ans.re, ans.im)
と小数点以下10桁表示にしました。
その場合、実数部が-1.0000000000
、虚数部が-0.0000000000
と表示されました。
以下、他のプログラミング言語の実装例も紹介します。
C言語の場合
-
$e$や$π$は、C言語の標準仕様では定数定義されていない。
コンパイラによりM_E
やM_PI
として定義されている場合がある。 -
複素数は、
double complex
が用意されている。 -
複素数のべき乗は
cpow
関数を利用できる。
#include <stdio.h>
#include <math.h>
#include <complex.h>
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#ifndef M_E
#define M_E 2.71828182845904523536
#endif
int main(void){
double complex x1, z;
x1 = M_PI * I;
z = cpow(M_E, x1);
printf("%.20f %.20f\n", creal(z), cimag(z));
printf("%.10f %.10f\n", creal(z), cimag(z));
}
-1.00000000000000000000 0.00000000000000012246
-1.0000000000 0.0000000000
double complex
型を複素数形式でそのまま表示できないようです。
C++の場合
- $e$や$π$は、
<cmath>
でM_E
やM_PI
として定義。 - 複素数は、
complex
テンプレートが用意されている。 - べき乗は
pow
関数を利用できる。
#include <iostream>
#include <cmath>
#include <complex>
using std::complex;
int main(void){
auto x1 = complex<double>(0.0, M_PI);
auto e = complex<double>(M_E, 0.0);
auto z = pow(M_E, x1);
std::cout << z << std::endl;
printf("%.10f %.10f\n", real(z), imag(z));
}
(-1,1.22465e-16)
-1.0000000000 0.0000000000
C#の場合
- $e$や$π$は、
Math.E
やMath.PI
として定義。 - 複素数は、
Complex
クラスが用意されている。 - 複素数のべき乗は
Complex.Pow
関数を利用できる。
using System;
using System.Numerics;
public class Example
{
public static void Main()
{
Complex x = new Complex(0.0, Math.PI);
Complex ans = Complex.Pow(Math.E, x);
Console.WriteLine(ans);
Console.WriteLine("{0:f10} {1:f10}", ans.Real, ans.Imaginary);
}
}
(-1, 1.22464679914735E-16)
-1.0000000000 0.0000000000
Goの場合
- $e$や$π$は、
math.E
やmath.Pi
として定義。 - 複素数は、
complex
型が用意されている。 - 複素数のべき乗は
cmplx.Pow
関数を利用できる。
package main
import (
"fmt"
"math"
"math/cmplx"
)
func main() {
x1 := complex(0, math.Pi)
ans := cmplx.Pow(math.E, x1)
fmt.Println(ans)
fmt.Printf("%.10f %.10f", real(ans), imag(ans))
}
(-1+1.2246467991473515e-16i)
-1.0000000000 0.0000000000
Perlの場合
- $e$や$π$は、定数定義されていない。
- 複素数は、
1.0 + 1.0 * i
といった形で利用できる。 - べき乗は
**
演算子を利用できる。
use Math::Complex;
my $pi = 3.14159265358979323846;
my $e = exp(1.0);
my $x = 0.0 + $pi * i;
my $ans = $e ** $x;
print $ans, "\n";
printf "%.10f %.10f\n", Re($ans), Im($ans);
-1+1.22464679914735e-16i
-1.0000000000 0.0000000000
Pythonの場合
- $e$や$π$は、
math.e
やmath.pi
として定義。 - 複素数は、
complex
型が用意されている。 - べき乗は
pow
関数を利用できる。
import math
x = complex(0, math.pi)
ans = pow(math.e, x)
print(ans)
print("%.10f %.10f" % (ans.real, ans.imag))
(-1+1.2246467991473532e-16j)
-1.0000000000 0.0000000000
Rubyの場合
- $e$や$π$は、
Math::E
やMath::PI
として定義。 - 複素数は、
Complex
型が用意されている。 - べき乗は
**
演算子を利用できる。
x = Complex(0.0, Math::PI)
ans = Math::E ** x
puts(ans)
printf("%.10f %.10f\n", ans.real, ans.imag)
-1.0+0.0i
-1.0000000000 0.0000000000
Rubyでは、演算結果をそのまま出力しても誤差が表示されませんでした。
Haskellの場合
- $e$は、定数定義されていない。$π$は、
pi
として定義。 - 複素数は、
1.0 :+ 1.0
といった形で利用できる。 - べき乗は
**
演算子を利用できる。
import Data.Complex
import Text.Printf
main = do
let x = 0.0 :+ pi
let e = exp 1.0
let ans = e ** x
let re = realPart(ans)
let im = imagPart(ans)
print ans
printf "%.10f %.10f\n" (re::Double) (im::Double)
(-1.0) :+ 1.2246467991473532e-16
-1.0000000000 0.0000000000
Juliaの場合
- $e$や$π$は、
ℯ
やπ
として定義。 - 複素数は、
Complex
型が用意されている。- 虚数単位
im
も定数として用意されている。
- 虚数単位
- べき乗は
^
演算子を利用できる。
using Printf
x = π * im # == complex(0.0, π)
ans = ℯ ^ x
println(ans)
@printf("%.10f %.10f\n", real(ans), imag(ans))
-1.0 + 1.2246467991473532e-16im
-1.0000000000 0.0000000000