jq 1.7の新機能ー数値精度

[JSON](https://datatracker.ietf.org/doc/html/rfc8259)処理のコマンドラインツールである[`jq`](https://stedolan.github.io/jq/)が、5年ぶりにアップデートしました。現在の最新は1.7.1（2023年12月リリース）です。1.6からいろいろと改定や新機能の追加があって、ますます楽しくなっています。

全容は[Github上のリリースノート](https://github.com/jqlang/jq/releases)を見ていただくとして、今日は改良された数値精度を試してみます。


### これまでは

`jq`は、[IEEE 754](https://ja.wikipedia.org/wiki/IEEE_754)の倍精度形式（binary64）で数値を表現します。この仕様では、仮数部（A × 10<sup>B</sup>のAの部分）は10進数でだいたい16桁くらいまで、指数部（Bの部分）は308くらいまで正確に表現できます。それ以上では、丸め誤差が発生します。

たとえば、次で用いている円周率のπは小数点以下が20桁で定義されていますが（Cの`math.h`から取ってきました）、バージョン1.6では切り捨てられてしまいます。

```bash
$ jq-1.6 --version
jq-1.6                                      # バージョン1.6を使っています

$ echo 3.14159265358979323846 | jq-1.6 '.'
3.141592653589793                           # 切り捨て
```

蛇足ですが、数値の桁数を調べるには、あらかじめ（`jq`に入力する前に）文字列化して、`length`です。

```bash
$ echo '"3.14159265358979323846"' | jq-1.6 'length'
22                                          # 先頭の値とドットを含めて22桁なので、小数点以下は20桁
```

`-n`オプション（ロングフォーマットは`--null-input`）を使ってフィルタ内に記述するのは✕です。その時点でIEEE 754表現に変換されてしまうからです。

```bash
$ jq-1.6 -n '3.14159265358979323846 | tostring | length'
17                                          # お前はすでに切り捨てられている
```


### JavaScriptでは

JavaScriptでも事情は同じです。数値はIEEE 754倍精度で表現されるので、一定以上の精度の値は丸められます。

Node.jsで試してみます。

```bash
$ node
Welcome to Node.js v20.9.0.
Type ".help" for more information.
> JSON.parse(3.14159265358979323846)
3.141592653589793
```


### これからは

数値の桁数は、読み込まれた時点ではその精度が保たれてるようになりました。

```bash
$ jq --version
jq-1.7.1                                    # 1.7です

$ echo 3.14159265358979323846 | jq '.'
3.14159265358979323846                      # 精度が保存されます
```

なので、`-n`でフィルタ内に記述することもできます。

```bash
$ jq -n '3.14159265358979323846 | tostring | length'
22                                          # 今度はあってる
```

`<`や`>`などの比較演算もできます。小数点数以下20桁のπとその末尾を１つ増やした値を比較してみます。

```bash
#                              V                       V が1つ違う
$ jq -n '[3.14159265358979323846, 3.14159265358979323847] | .[0] < .[1]'
true
```

1.6ではこうはいきません。

```bash
$ jq-1.6 -n '[3.14159265358979323846, 3.14159265358979323847] | .[0] < .[1]'
false
```

比較演算はできますが、普通の演算では1.6同様に桁落ちします。演算と同時にIEEE 754表現に変換されるからです。0を加えてみます。

```bash
$ jq -n '3.14159265358979323846 + 0'
3.141592653589793
```


### おわりに

数字（文字）をずらずら書き連ねるだけで数値を表現できるJSONでは、数値の精度に制約はありません。しかし、その処理系ではIEEE 754など表現形式の精度に依存します。そのため、JSON仕様の[RFC 8259](https://www.rfc-editor.org/info/rfc8259)は極端に大きい、あるいは精度の高い数値を使うときは、相互運用性に気をつけるように注意を促しています。

比較ができるようになったからといって、大きな数値を使うのは考えものなのは、これまでと変わりません。

1.7では、他にも、細かいところで性能向上も図られています。まだ1.6をお使いなら、これを機会に1.7にアップグレードしましょう。



### 参考

- [./jq](https://stedolan.github.io/jq/) ... `jq`のオフィシャルサイトです（英文）。
- [Github jqland/jq Releases](https://github.com/jqlang/jq/releases) ... `jq`のリリースノートです（英文）。
- 『[jqハンドブック](http://www.cutt.co.jp/book/978-4-87783-491-3.html)』（2021）... 1.6の数値精度の問題は、この本の5.8節で紹介しています。ご購入はこちらから【[出版社](http://www.cutt.co.jp/book/978-4-87783-491-3.html) | [honto](https://honto.jp/netstore/pd-book_31037910.html) | [amazon.co.jp](https://www.amazon.co.jp/jq/dp/4877834915) | [ヨドバシカメラ](https://www.yodobashi.com/product/100000009003450000/)】。
- 「[jqのむだづかい](https://qiita.com/stoyosawa)」 ... `jq`ではまずやろうとは思わない処理の探求記録です。完成版は他の（通常機能な）レシピとともに『[jqクックブック](https://www.cutt.co.jp/book/978-4-87783-508-8.html)』（2023）に掲載しました。ご購入はこちらから【[出版社](https://www.cutt.co.jp/book/978-4-87783-508-8.html) | [honto](https://honto.jp/netstore/pd-book_32427461.html) | [amazon.co.jp](https://www.amazon.co.jp/dp/4877835083) | [ヨドバシカメラ](https://www.yodobashi.com/product/100000009003710680/)】。

<img src="http://www.cutt.co.jp/book/images/978-4-87783-491-3.png" width="200" alt="表紙"></img> <img src="https://www.cutt.co.jp/book/images/978-4-87783-508-8.png" width="200" alt="表紙"></img>