Skip to content

[BUG] umul192_lower128 can error on due to overflow on addition #84

@Alexhuszagh

Description

@Alexhuszagh

Description

The function umul192_lower128, used in writing floats, can overflow in rare circumstances in the following logic:

hi + (hi_lo >> 64) as u64

The wrapping is actually desired, as shown by the failing test case.

#[inline(always)]
pub const fn umul192_lower128(x: u64, yhi: u64, ylo: u64) -> (u64, u64) {
    let hi = x.wrapping_mul(yhi);
    let hi_lo = x as u128 * ylo as u128;
    (hi + (hi_lo >> 64) as u64, hi_lo as u64)
}

umul192_lower128(15966911296221875, 0xcccccccccccccccc, 0xcccccccccccccccd);

In Python code, we have the same logic with the following:

>>> x = 15966911296221875
>>> yhi = 0xcccccccccccccccc
>>> ylo = 0xcccccccccccccccd
>>> y = (yhi << 64) + ylo
>>> z = x * y
>>> print(z & (2**128 - 1))
3193382259244375

This means hi + (hi_lo >> 64) as u64 is expected to wrap.

Prerequisites

Here are a few things you should provide to help me understand the issue:

  • Rust version: rustc 1.62.0-nightly (6dd68402c 2022-05-11)
  • lexical version: lexical-write-float v0.8.4
  • lexical compilation features used: default

Test case

Please provide a short, complete (with crate import, etc) test case for
the issue, showing clearly the expected and obtained results.

Example test case:

#[inline(always)]
pub const fn umul192_lower128(x: u64, yhi: u64, ylo: u64) -> (u64, u64) {
    let hi = x.wrapping_mul(yhi);
    let hi_lo = x as u128 * ylo as u128;
    (hi + (hi_lo >> 64) as u64, hi_lo as u64)
}

fn main() {
    umul192_lower128(15966911296221875, 14757395258967641292, 14757395258967641293);
}

Metadata

Metadata

Assignees

Labels

bugSomething isn't workinghigh priorityHigh priority

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions