意外に知らない %.2g の意味

C++20 で追加される(はずの) std::format のリファレンス実装 fmtlib/fmt を触ってて気づいたメモ。

浮動小数の小数点以下を2桁に抑えたい場合、例えば以下のように書くと思う。

#include <iostream>
#include <format>
#include <cstdio>

int main() {
    double d = 3.1415926535;

    ::printf("printf = %.2f", d); // "3.14"
    std::cout << std::format("format = {:.2}", d); // "3.14" ?

    return 0;
}

だが、これは以下の出力になる。

printf = 3.14
format = 3.1

実は std::format() で型指定をしない場合、浮動小数型では {:.2g} として扱われる。
この gprintf() にもあるオプションなので、指定してみたら出力が一致した。

double d = 3.1415926535;
::printf("printf = %.2g", d); // "3.1"
std::cout << std::format("format = {:.2}", d); // "3.1"

オプションの意味

このように直感に反する結果になってしまったが、そもそも g とはどんなオプションなのか。

古い仕様書だけど、ISO/IEC 9899:1999 が見つかったので参照してみる。
ISO/IEC 9899:1999 - Programming languages

g,G
A double argument representing a floating-point number is converted in style f or e (or in style F or E in the case of a G conversion specifier), depending on the value converted and the precision.

ということで、g は入力の精度によって f (小数点表示) と e (指数表示) を自動切り替えするオプションであった。
さて、問題の精度指定 %.2g については、その上に定義が見つかる。

An optional precision that gives the minimum number of digits to appear for the d, i, o, u, x, and X conversions, the number of digits to appear after the decimal-point character for a, A, e, E, f, and F conversions, the maximum number of significant digits for the g and G conversions, or the maximum number of bytes to be written for s conversions.

要点だけ書くと

  • a, A, e, E, f では「小数点以下の桁数」
  • g, G では「最大有効桁数」

と定義されていた。

まとめ

つまり、%.2g は 「浮動小数型の出力を自動で切り替えるけど、有効桁数を2桁にする」という意味だった。
自動切り替えが不要ならば、おとなしく fe を指定しておこう。

double d = 3.1415926535;

// 小数点以下が2桁になる
::printf("printf = %.2f", d); // "3.14"
std::cout << std::format("format = {:.2f}", d); // "3.14"

// 有効桁数が2桁になる
::printf("printf = %.2g", d); // "3.1"
std::cout << std::format("format = {:.2g}", d); // "3.1"