Skip to content

Loss of precision in Duration::mul_f32 (and mul_f64) #149794

@quaternic

Description

@quaternic

The methods to multiply a Duration by a float are currently implemented by converting to that float type (as seconds), multiplying, and then converting back to a Duration. This implies unnecessary rounding that loses precision in cases that ideally wouldn't.

It is particularly surprising that multiplying by 1.0 can change a duration, as floating point multiplication by 1.0 does preserve numeric values exactly.

use std::time::Duration;

fn main() {
    let d1 = Duration::from_nanos_u128(1 << 90);
    let d2 = Duration::from_nanos_u128(2 << 90);
    let d3 = Duration::from_nanos_u128(3 << 90);
    
    // Each of these asserts fail
    assert_eq!(d1.mul_f32(1.0), d1);
    assert_eq!(d1.mul_f32(2.0), d2);
    assert_eq!(d1.mul_f32(3.0), d3);
    assert_eq!(d2.mul_f32(1.5), d3);
    // This panics due to rounding up
    Duration::MAX.mul_f32(1.0);
}

I expected these methods to behave just like floating point multiplication: As if the exact result was rounded to the resulting type.

The above uses very long durations, but for more realistic examples, this can be observed with durations like:

100ms
[src/main.rs:5:5] d = 100ms
[src/main.rs:6:5] d.mul_f32(1.0) = 100.000001ms
[src/main.rs:7:5] d.mul_f32(2.0) = 200.000003ms
[src/main.rs:8:5] d.mul_f32(3.0) = 300.000012ms
[src/main.rs:9:5] d * 1 = 100ms
[src/main.rs:10:5] d * 2 = 200ms
[src/main.rs:11:5] d * 3 = 300ms
1s + 111ns
[src/main.rs:5:5] d = 1.000000111s
[src/main.rs:6:5] d.mul_f32(1.0) = 1.000000119s
[src/main.rs:7:5] d.mul_f32(2.0) = 2.000000238s
[src/main.rs:8:5] d.mul_f32(3.0) = 3.000000477s
[src/main.rs:9:5] d * 1 = 1.000000111s
[src/main.rs:10:5] d * 2 = 2.000000222s
[src/main.rs:11:5] d * 3 = 3.000000333s

Metadata

Metadata

Assignees

No one assigned

    Labels

    A-floating-pointArea: Floating point numbers and arithmeticA-timeArea: TimeC-bugCategory: This is a bug.T-libsRelevant to the library team, which will review and decide on the PR/issue.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions