Rustのf64謎挙動(軽量記事) 未解決 -> 解決

# はじまり

最近AtCoderにRustで参加してます(*1)。楽しいですねAtCoder。普段書かないようなコードを書くので言語に対する理解の浅さを痛感してます。
週末を利用して[鹿島建設プログラミングコンテスト2024（AtCoder Beginner Contest 340）](https://atcoder.jp/contests/abc340)のC問題にチャレンジした際、Rustの謎挙動で時間ががっつりとられてどうにもならなくなったので備忘的に残します。尚、謎は謎のままで未解決です(*2)。

*1: コンテスト参加回数2回の真正灰色
*2: もちろん、C問題も未解決

# 解決しました！

コメントありがとうございます！コメントいただいたとおり、

> いわゆるケチ表現を含めて有効桁数は十進数で約16桁です。
 16桁を超えた場合には小さい側の桁は意味のある数値にならないと思います。

有効桁数を超えていただけ。しかもRust特有の謎でもなかった。
いかにもコンピュータ言語っぽい謎って感じ。

業務では有効桁超えるようなコードをまず書かないようにするので、書き捨てのAtCoderならでは感ある謎だった。

とりあえず、u64とrust_deciamlを利用してAtCoderチャレンジを継続する。

# 謎挙動

## 謎の概要

f64で大きな数を標準出力に出そうとすると値が変わる。

![ダウンロード.jpg](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/42445/06f1849f-4f40-4841-4e0c-8b3842c6831d.jpeg)


## 謎の詳細

Rustで小数点以下の計算を行う場合、f32とかf64を利用する。

```rust
fn main() {
    let value_f32 = 12.5_f32;
    let value_f64 = 12.5_f64;
    // output: 12.5, 12.5
    println!("{}, {}", value_f32, value_f64);
}
```

[鹿島建設プログラミングコンテスト2024（AtCoder Beginner Contest 340）](https://atcoder.jp/contests/abc340)のC問題では割り算の端数処理が発生するのでf64で計算していたが、なぜか出力が計算値と違う。

```rust
fn main() {
    let value_f64 = 5655884811924144128.0_f64;
    // ↓？？？？？？？？？？
    // output: 5655884811924144000
    println!("{}", value_f64);
}
```

なぜか末尾の`128`がロスト。謎すぎる。試しにu64へ変換するとなぜか正しく出力される。

```rust
fn main() {
    let value_f64 = 5655884811924144128.0_f64;
    // output: 5655884811924144128
    println!("{}", value_f64 as u64);
}
```

ちなみに、128以外の差があっても下3桁が0になったりする。

```rust
fn main() {
    // output
    // 5655884811924144000
    // 5655884811924144000
    // 5655884811924144000
    // 5655884811924144000
    println!(
        "{}\n{}\n{}\n{}\n",
        5655884811924144128.0, 5655884811924144000.0, 5655884811924144001.0, 5655884811924144129.9,
    );
}
```

f64の比較でも謎挙動(こっちはu64に変換しても挙動変わらず)

```rust
fn main() {
    // ↓？？？？？？？？？？
    // output: why japanese people!
    if 5655884811924144128.0 == 5655884811924144000.0 {
        println!("why japanese people!");
    } else {
        println!("I'm japanese people!");
    }
}
```

# まとめ

謎すぎるので無視することにしました。どうにもならなそうなら[rust_decimal](https://docs.rs/crate/rust_decimal/latest)使ってみます。

# 追記

## AtCoderでrust_decimal使ってみた

rust_decimalでコード提出したら参照できないってエラーになりました。AtCoderではrust_decimalは使えないようです。

```text
error[E0432]: unresolved import `rust_decimal`
 --> src/main.rs:2:5
  |
2 | use rust_decimal::Decimal;
  |     ^^^^^^^^^^^^ use of undeclared crate or module `rust_decimal`

error[E0432]: unresolved import `rust_decimal_macros`
 --> src/main.rs:3:5
  |
3 | use rust_decimal_macros::dec;
  |     ^^^^^^^^^^^^^^^^^^^ use of undeclared crate or module `rust_decimal_macros`
```
  
## 公式みたらu64の小数点計算あったわ

[公式のu64](https://doc.rust-lang.org/std/primitive.u64.html#method.div_floor)見たら普通にdiv_floor()とかdiv_ceil()あった。div_floor()見たら

> Calculates the quotient of self and rhs, rounding the result towards negative infinity.
This is the same as performing self / rhs for all unsigned integers.

って書いてあったから下記でも問題で使う割り算は十分対応可能

```rust
fn calc_next_value(value: u64) -> Vec<u64> {
    if value % 2_u64 == 0 {
        let value = value / 2_u64;
        vec![value, value]
    } else {
        let value = value / 2_u64;
        vec![value, value + 1_u64]
    }
}
```