質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

1回答

5220閲覧

C++ - std::setprecision() の仕様について

tiitoi

総合スコア21956

GCC

GCCはGNU Compiler Collectionの略です。LinuxのC言語コンパイラのデファクトスタンダードであり、数多くの他言語やプラットフォームサポートもします。

Visual Studio

Microsoft Visual StudioはMicrosoftによる統合開発環境(IDE)です。多種多様なプログラミング言語に対応しています。

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

1グッド

0クリップ

投稿2020/07/26 15:09

編集2020/07/27 02:02

double 型は10進で最大16桁程度の有効桁数しかないと理解していますが、std::setprecision(50) のようにそれより大きい桁数を設定しても、なんらかの値が出力されます。
例えば、以下のコードで std::log(2) の値を出力すると、15桁程度までは真の値と合っていて、それ以降も間違ってはいますが、値が出力されます。

そこで質問なのですが、

  • 16桁以降の値はどこから来たのでしょうか?
  • std::setprecision(53) より大きい値を設定しても53桁までしか表示されないようですが、そういう仕様でしょうか?
  • もしくは大きい桁数を設定した場合の挙動は動作未定義なのでしょうか?

参考

環境

  • C++14
  • Visual Studio 2019 / GCC 11 (Wandboxで確認)
  • 上記2つのコンパイラで出力結果が一致することを確認

コード

text

10.693147180559945 28622676398299518041312694549560546 出力 20.693147180559945 30941723212145817656807550013436026 正解

正解のソース: log(2) 50digits - Wolfram|Alpha

cpp

1#include <cmath> 2#include <iomanip> 3#include <iostream> 4 5int main() 6{ 7 std::cout << std::log(2.) << std::endl; 8 // 0.693147 9 10 std::cout << std::setprecision(16) << std::log(2.) << std::endl; 11 // 0.6931471805599453 12 13 std::cout << std::setprecision(52) << std::log(2.) << std::endl; 14 // 0.6931471805599452862267639829951804131269454956054688 15 16 std::cout << std::setprecision(53) << std::log(2.) << std::endl; 17 // 0.69314718055994528622676398299518041312694549560546875 18 19 std::cout << std::setprecision(54) << std::log(2.) << std::endl; 20 // 0.69314718055994528622676398299518041312694549560546875 21 22 // 正解 0.69314718055994530941723212145817656807550013436026 23 24 return 0; 25}

追記

std の実装はまだ確認してませんが、yohhoy さん、SHOMI さんのコメントで値がどこから出てきたのか謎が解けました。
ご回答ありがとうございます。

double a = std::log(2.) の浮動小数点表記

  • bias = 1023
  • 仮数部 0x162E42FEFA39EF * (2 ** -52)
  • 指数部 0x3FE
  • 浮動小数点数: 0x162E42FEFA39EF * (2 ** -52) * (2**0x3FE - 1023) = 6243314768165359 / 9007199254740992

6243314768165359 / 9007199254740992 = 0.69314718055994528622676398299518041312694549560546875

yohhoy👍を押しています

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

C++標準仕様は、浮動小数点数に関してほとんど何も規定しません。double型が IEEE 754(ISO/IEC 60559) 仕様に準拠しないことさえも許容しています。

一般的には double型 == IEEE754 binary64 が採用されますので、この前提で以下回答を記述しています。


16桁以降の値はどこから来たのでしょうか?

おそらく、double内部表現(基数=2)から10進表現へ変換するロジックを、16桁目以降も続けて適用した結果と考えられます。
double型の値は10進表現だと有効数字15桁しか持たないため、16桁目以降の数値は信頼できません。

std::setprecision(53) より大きい値を設定しても53桁までしか表示されないようですが、そういう仕様でしょうか?

仕様上は「処理系定義」です。
おそらく、double基数2で表現できる最大桁数53で出力を打ち切るロジックになっているのでしょう。

もしくは大きい桁数を設定した場合の挙動は動作未定義なのでしょうか?

仕様上は「処理系定義」です。

投稿2020/07/26 16:08

yohhoy

総合スコア6191

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

SHOMI

2020/07/26 18:24 編集

>10進表現へ変換するロジックを、16桁目以降も続けて適用した結果と考えられます。 ですね。 std::log(2.)の結果のdouble内部表現は0x3fe62e42fefa39efなので、 指数部0x3fe、指数部バイアスを掛けて-1 仮数部0x62e42fefa39ef、頭に1を補って0x162e42fefa39ef=6243314768165359 よって 6243314768165359*2^(-52-1) =6243314768165359/9007199254740992 =0.69314718055994528622676398299518041312694549560546875
tiitoi

2020/07/27 02:03

To: yohhoy さん、SHOMI さん 処理系定義となっていたのですね。 値がどこから出てきたのかについても疑問が解消しました。 ご回答いただきありがとうございます!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問