В моем понимании, установка направления округления в сторону +Inf
даст 0.333334
при оценке 1/3 и 0.33333
при установке его в сторону -Inf
,
Это не тот случай, когда я пытался в C ++, используя fesetround(0x400)
а также fesetround(0x800)
, Я получаю такое же поведение в Rust, используя FFI для вызова fsetround
из С.
Код C ++:
#include <cfenv>
#include <iostream>
#include <iomanip>
using namespace std;
int main() {
double ratio = (double)1/(double)10;
fesetround(FE_UPWARD);
cout << fegetround() << " upward " << setprecision(18) << ratio << std::endl;
fesetround(FE_DOWNWARD);
cout << fegetround() << " downward " << setprecision(18) << ratio << std::endl;
return 0;
}
(Pastebin)
Код ржавчины:
extern crate libc;
use libc::c_int;
#[link(name = "rounding")]
extern {
pub static fe_upward: c_int;
pub static fe_downward: c_int;
fn fesetround(rount: c_int) -> c_int;
fn fegetround() -> c_int;
}
pub fn upward_round() -> i64 {
unsafe {
fesetround(fe_upward);
fegetround() as i64
}
}
pub fn downward_round() -> i64 {
unsafe {
fesetround(fe_downward);
fegetround() as i64
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_upward() {
unsafe {
assert_eq!(fe_upward as i64, upward_round());
}
}
#[test]
fn test_downward() {
unsafe {
assert_eq!(fe_downward as i64, downward_round());
}
}
}
(Pastebin)
Комментарий Starblue прав, но позвольте мне расширить его.
«Округление» означает аппроксимацию действительного числа конечным набором цифр в некоторой неопределенной базе. Ваш пример 1/3 = 0,333333 предполагает округление до 6 десятичный цифры, то есть основание 10.
Компьютеры работают в базе 2, однако. Двоичный 1/11 является .1010101010101...
Как видите, округление это немного своеобразно. Если вы округлите до ближайшего к 6 битам, это будет .101011
и если вы округлите его до 7 бит, это .1010100
— последний бит всегда совпадает с предпоследним битом, и это потому, что биты чередуются.
Конечно, округлять вверх-вниз проще. округление .10101010...
down просто усекает результат до N битов: 0.101010
, И округление просто добавляет 1 к последнему биту.
Теперь вы делаете округление в двоичном виде, но вы выводите результат в десятичном виде. Это означает, что эти шаблоны совсем не очевидны.
Вот где все усложняется «: округление необходимо почти везде в функциях FP, поэтому оно должно быть быстрым. Это означает, что вы хотите, чтобы режим округления был скомпилирован. Но вы не можете перекомпилировать свой код при каждом вызове fesetround
, Это означает, что нужен компромисс, и компромисс #pragma STDC FENV_ACCESS
. Если это ON
, вы получаете медленный код и fesetround
работает. Если он выключен (по умолчанию), fesetround
имеет неопределенные Результаты.
Других решений пока нет …