Battle-Of-Two-K
Battle-Of-Two-K

Reputation: 13

Data types in Rust

fn diapason(amount_bits: u32) -> [i128; 2] {
    let x:i128 = 2;
    [-x.pow(amount_bits - 1), x.pow(amount_bits - 1) - 1]
}

fn signed_and_unsigned_int() {
    let set_bits:[u32; 5] = [8, 16, 32, 64, 128];

    for i in set_bits {
        println!("i{} can store [{}, {}]", i,
                 diapason(i)[0], diapason(i)[1])
    }
}

fn main() {
    signed_and_unsigned_int()
}

Good day, I started to study Rust and came across an error (thread 'main' panicked at 'attempt to multiply with overflow'). I cannot understand why the number "2 to the power of 128" does not fit into the type i128?

C:/Users/HOME/.cargo/bin/cargo.exe run --color=always --package start_learning --bin start_learning
    Finished dev [unoptimized + debuginfo] target(s) in 0.01s
     Running `target\debug\start_learning.exe`
i8 can store [-128, 127]
i16 can store [-32768, 32767]
i32 can store [-2147483648, 2147483647]
i64 can store [-9223372036854775808, 9223372036854775807]
thread 'main' panicked at 'attempt to multiply with overflow', C:\Users\HOME\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib/rustlib/src/rust\library\core\src\num\mod.rs:119:5
stack backtrace:
   0: std::panicking::begin_panic_handler
             at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b\/library\std\src\panicking.rs:493
   1: core::panicking::panic_fmt
             at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b\/library\core\src\panicking.rs:92
   2: core::panicking::panic
             at /rustc/53cb7b09b00cbea8754ffb78e7e3cb521cb8af4b\/library\core\src\panicking.rs:50
   3: core::num::{{impl}}::pow
             at C:\Users\HOME\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\num\int_macros.rs:1586
   4: start_learning::diapason
             at .\src\main.rs:4
   5: start_learning::signed_and_unsigned_int
             at .\src\main.rs:12
   6: start_learning::main
             at .\src\main.rs:17
   7: core::ops::function::FnOnce::call_once<fn(),tuple<>>
             at C:\Users\HOME\.rustup\toolchains\stable-x86_64-pc-windows-msvc\lib\rustlib\src\rust\library\core\src\ops\function.rs:227
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.
error: process didn't exit successfully: `target\debug\start_learning.exe` (exit code: 101)

enter image description here

Process finished with exit code 101

Upvotes: 1

Views: 185

Answers (2)

mcarton
mcarton

Reputation: 29981

The maximum value of i128 is indeed 2¹²⁷-1, which is is smaller than 2¹²⁷. So “2 to the power of 127" does not fit into the type i128” is correct.

Since you're trying to compute x.pow(amount_bits - 1) - 1, you need to first compute x.pow(amount_bits - 1), and that tries to compute 2¹²⁷. All intermediate values also need to fit in i128, not just the final result. Similarly, you're trying to compute -x.pow(amount_bits - 1), which first need to compute x.pow(amount_bits - 1) which again, does not fit in a i128.

That formula works fine for amount_bits up to 64, since you compute the result in a i128, which is big enough for 2⁶⁴, but it overflows at 128.

With a bit of trivial math you you change to:

fn diapason(amount_bits: u32) -> [i128; 2] {
    let x:i128 = 2;
    [-2*x.pow(amount_bits - 2), 2*(x.pow(amount_bits - 2) - 1) + 1]
}

which does yield the expected result.

These use the following bit of trivial math, whose intermediate results are smaller and never overflow an i128:

-2**n == -2*(2 ** (n-1))
2**n-1 == 2**n - 2 + 1 == 2*(2 ** (n-1) - 1) + 1

Upvotes: 5

loops
loops

Reputation: 5635

The maximum value a i128 can store is 2127 - 1, and the minimum is -(2127). The first bit is used for tracking if the number is negative, leaving the remaining 127 bits to represent the actual number. 2127 values are used to represent negative numbers, 1 value is used to represent zero, and the remaining 2127 - 1 values are used to represent positive numbers. When evaluating -x.pow(amount_bits - 1), the negation is done after calculating the power. 2127 is 1 greater than the maximum value of an i128, so it panics. While the result would be in bounds if it got to doing the negation, Rust panics before the negation can even occur.

The simplest solution would be to use the i128::{MAX, MIN} constants instead of manually calculating them. Alternatively, you could use a library that supports arbitrary-sized numbers, such as num.

Upvotes: 3

Related Questions