Closed
Description
core::num::dec2flt
interprets the floating-point rounding mode incorrectly for negative numbers. The assumption in the implementation that negative numbers can be handled by just flipping the sign bit is wrong in the general case.
Observed Behavior
core::num::dec2flt
rounds negative numbers upwards when the rounding mode is set to downwards and vice versa.
Here is the code that demonstrates the issue.
extern crate libc;
use libc::c_int;
use std::str::FromStr;
const FE_TONEAREST: c_int = 0;
const FE_DOWNWARD: c_int = 0x400;
const FE_UPWARD: c_int = 0x800;
extern {
fn fesetround(rdir: c_int) -> c_int;
}
fn main() {
unsafe { fesetround(FE_DOWNWARD); }
let p_lo = f64::from_str("0.9").unwrap();
let n_lo = f64::from_str("-0.9").unwrap();
unsafe { fesetround(FE_UPWARD); }
let p_hi = f64::from_str("0.9").unwrap();
let n_hi = f64::from_str("-0.9").unwrap();
unsafe { fesetround(FE_TONEAREST); }
println!(" 0.9: <{:19.16}, {:19.16}>", p_lo, p_hi);
println!("-0.9: <{:19.16}, {:19.16}>", n_lo, n_hi);
}
Output:
0.9: < 0.8999999999999999, 0.9000000000000000>
-0.9: <-0.8999999999999999, -0.9000000000000000>
Here is the equivalent code in C.
#include <fenv.h>
#include <stdio.h>
#include <stdlib.h>
int main() {
double p_lo, p_hi, n_lo, n_hi;
fesetround(FE_DOWNWARD);
p_lo = strtod("0.9", NULL);
n_lo = strtod("-0.9", NULL);
fesetround(FE_UPWARD);
p_hi = strtod("0.9", NULL);
n_hi = strtod("-0.9", NULL);
fesetround(FE_TONEAREST);
printf(" 0.9: <%19.16f, %19.16f>\n", p_lo, p_hi);
printf("-0.9: <%19.16f, %19.16f>\n", n_lo, n_hi);
}
Output:
0.9: < 0.8999999999999999, 0.9000000000000000>
-0.9: <-0.9000000000000000, -0.8999999999999999>
C handles the rounding correctly whereas Rust gets it wrong for negative numbers.
Expected Behavior
core::num::dec2flt
should either not be affected by the rounding mode and always use half-to-even rounding as stated in the documentation or interpret the rounding mode correctly.
Meta
rustc --version --verbose
:
rustc 1.19.0-nightly (6a5fc9eec 2017-05-02)
binary: rustc
commit-hash: 6a5fc9eecec235312755e737fb5b984abe537f2e
commit-date: 2017-05-02
host: x86_64-unknown-linux-gnu
release: 1.19.0-nightly
LLVM version: 4.0