はじまり
最近AtCoderにRustで参加してます(*1)。楽しいですねAtCoder。普段書かないようなコードを書くので言語に対する理解の浅さを痛感してます。
週末を利用して鹿島建設プログラミングコンテスト2024(AtCoder Beginner Contest 340)のC問題にチャレンジした際、Rustの謎挙動で時間ががっつりとられてどうにもならなくなったので備忘的に残します。尚、謎は謎のままで未解決です(*2)。
*1: コンテスト参加回数2回の真正灰色
*2: もちろん、C問題も未解決
解決しました!
コメントありがとうございます!コメントいただいたとおり、
いわゆるケチ表現を含めて有効桁数は十進数で約16桁です。
16桁を超えた場合には小さい側の桁は意味のある数値にならないと思います。
有効桁数を超えていただけ。しかもRust特有の謎でもなかった。
いかにもコンピュータ言語っぽい謎って感じ。
業務では有効桁超えるようなコードをまず書かないようにするので、書き捨てのAtCoderならでは感ある謎だった。
とりあえず、u64とrust_deciamlを利用してAtCoderチャレンジを継続する。
謎挙動
謎の概要
f64で大きな数を標準出力に出そうとすると値が変わる。
謎の詳細
Rustで小数点以下の計算を行う場合、f32とかf64を利用する。
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)のC問題では割り算の端数処理が発生するのでf64で計算していたが、なぜか出力が計算値と違う。
fn main() {
let value_f64 = 5655884811924144128.0_f64;
// ↓??????????
// output: 5655884811924144000
println!("{}", value_f64);
}
なぜか末尾の128
がロスト。謎すぎる。試しにu64へ変換するとなぜか正しく出力される。
fn main() {
let value_f64 = 5655884811924144128.0_f64;
// output: 5655884811924144128
println!("{}", value_f64 as u64);
}
ちなみに、128以外の差があっても下3桁が0になったりする。
fn main() {
// output
// 5655884811924144000
// 5655884811924144000
// 5655884811924144000
// 5655884811924144000
println!(
"{}\n{}\n{}\n{}\n",
5655884811924144128.0, 5655884811924144000.0, 5655884811924144001.0, 5655884811924144129.9,
);
}
f64の比較でも謎挙動(こっちはu64に変換しても挙動変わらず)
fn main() {
// ↓??????????
// output: why japanese people!
if 5655884811924144128.0 == 5655884811924144000.0 {
println!("why japanese people!");
} else {
println!("I'm japanese people!");
}
}
まとめ
謎すぎるので無視することにしました。どうにもならなそうならrust_decimal使ってみます。
追記
AtCoderでrust_decimal使ってみた
rust_decimalでコード提出したら参照できないってエラーになりました。AtCoderではrust_decimalは使えないようです。
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見たら普通に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.
って書いてあったから下記でも問題で使う割り算は十分対応可能
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]
}
}