解決策

解決策

ナノ秒精度で日付を取得します。

$ start=$(date '+%s.%N')

...そして印刷してください:

$ echo ${start}
1662664850.030126174

今まではそんなに良くなった。しかし、ランダムに大きな精度でprintfを実行すると、どのような結果が出るのか見てみましょう。

1662664850.0301261739805340766906738281250000000000000000000000000

Q1. dateコマンドは実際に開始変数にその分の情報を入力しますか、それとも数字がゴミですか?

これが質問の2番目の部分です。私がしばらく数学をしたいとしましょう。 「終了」タイムスタンプを作成します。

$ end=$(date '+%s.%N')
$ echo ${end}
1662665413.471669572
$ printf "%.55f\n" ${end}
1662665413.4716695720562711358070373535156250000000000000000000000

ここで、bcを使用して期待される結果を得る。

$ echo $(bc <<< ${end}-${start})
563.441543398

しかし、PythonやPerlを使用したときに得られた結果を見てください。

$ echo $(python -c "print(${end} - ${start})")
563.441543579
$ echo $(perl -e "print(${end} - ${start})")
563.441543579102

ある時点で、この数字は次のように表示されます。

紀元前563.441543398話  
Python 563.441543579話  
真珠 563.441543579話102  

Q2.数値は多様ですが、丸めによる結果は予想されたものとは異なります。何を提供しますか?

システム情報:
Linux 3.10.0-1160.71.1.el7.x86_64#1 SMP 2022年6月15日水曜日08:55:08 UTC

コマンド情報:
日付(GNU coreutils)8.22
bc 1.06.95
Python 2.7.5
Perl 5、バージョン16、Subversion 3(v5.16.3)x86_64-linux-thread-multi用に構築

ベストアンサー1

他の人が言ったように、浮動小数点数のほとんどの10進表現は、コンピュータが計算に使用するバイナリ形式では正確に表現できません。

あなたが見るのは、printf %.55f元の数字の2進近似を10進数で表したものです。

printf(可能な)組み込みコマンドはバイナリ表現に変換を使用し、それを関数1662665413.471669572(またはシリーズの他の関数、たとえば)に渡してフォーマットします(に変更)。long doublestrtold()printf()snprintf()%.55f%.55Lf

表現は、long doubleシステムごと、Cコンパイラごと、Cコンパイラごと、CPUアーキテクチャによって異なります。 amd64ハードウェアでGNU cc / shellでコンパイルされたGNU / Linuxシステムの場合printf、long doubleは現在のタイムスタンプをナノ秒精度で保存するのに十分な精度を持ちます。

しかし、ほとんどのawk実装、Perl、ほとんどのシェル(bashではなく浮動小数点演算をサポートする場合)を含む多くの(ほとんどではありませんが)ツールと言語は長いdoubleの代わりにdoubleを使用し、倍精度数字は常に53桁です精度だけがあります。だから、精度は小数点以下15桁を超えることはできません。1.

これが正確なタイムスタンプを扱うほとんどのものが実際にタイムスタンプを2つの整数、つまり秒とマイクロ秒(struct timevalシステムコールから返された値gettimeofday())またはナノ秒(struct timespecシステムコールで返された値clock_gettime())で表す理由の1つです。

これがGNUが浮動小数点バリアントの代わりにおよびをdate持ち、zshが浮動小数点の代わりに配列を持つ理由です。%s%N%s$epochtime

これらの高精度タイムスタンプで計算を実行するには、言語/ツールがlong doubleを使用しないか、またはCPU浮動小数点演算(たとえばbc

ほとんどのシェルは整数演算に64ビット整数を使用します。longこれは最大9223372036854775807までの数字を受け入れることができます。

たとえば、2つのタイムスタンプ(各タイムスタンプを(秒、ナノ秒)タプルとして表示)の差を計算するには、ナノ秒(sec2 - sec1) * 100000000 + nsec2 - nsec1単位で差を取得できます。

たとえば、zshでは次のようになります。

zmodload zsh/datetime
start=($epochtime)
uname
end=($epochtime)
print running uname took about $((
  (end[1] - start[1]) * 1_000_000_000 + end[2] - start[2] )) nanoseconds.

ここに与えられた:

Linux
running uname took about 2621008 nanoseconds.

埋め込まれていないコマンド(含む)を実行するのに少なくとも数千ナノ秒かかるシェルでは、そのような高精度を持つことは言葉ではないとdate主張できます。

図:

zmodload zsh/datetime
start=$EPOCHREALTIME
uname
end=$EPOCHREALTIME
printf 'running uname took about %.5g seconds\n' $(( end - start ))

$EPOCHREALTIME秒とナノ秒(ゼロパディング)整数を中間値に連結して構築されているため、完全な精度がありますが、.計算を実行するために変換すると一部の精度が失われます。これはおそらく十分です。double

ksh93のように、ここでは次のことをお勧めします。

typeset -F SECONDS=0
uname
printf "running uname took %g seconds\n" $SECONDS

$SECONDS、シェルが起動してから節約された時間はフローティング(およびストップウォッチにリセット)に設定でき、この場合はマイクロ秒精度を得ることができます。)


1たとえば、a of sの代わりにsをprintf使用しても同じ数字が提供されるという保証はありません(たとえば、and ofまたはを使用してみてください)。doublelong doubleprintf %.16g <a-16-digit-number>9.999999999999919printfzshgawkperl

おすすめ記事