据我所知,将舍入方向设置为 +Inf
会在计算 1/3 时产生 0.333334
,而在设置为 0.33333
时会产生 0.33333
-Inf
.
当我尝试在 C++ 中使用 fesetround(0x400)
和 fesetround(0x800)
时,情况并非如此。我在 Rust 中使用 FFI 从 C 调用 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 )
防 rust 代码:
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...
只是将结果截断为 N 位:0.101010
。向上舍入只是将最后一位加 1。
现在您以二进制进行四舍五入,但以十进制打印结果。这意味着这些模式根本不明显。
这就是事情变得复杂的地方”:在 FP 函数中几乎所有地方都需要舍入,因此它应该很快。这意味着您需要编译舍入模式。但是您不能在每次调用 fesetround
。这意味着需要妥协,妥协是 #pragma STDC FENV_ACCESS
。如果它是 ON
,您将获得慢速代码并且 fesetround
可以正常工作。如果关闭(默认),fesetround
会产生未指定的结果。
关于c++ - 从 Rust 和 C++ 设置 FPU 舍入方向没有任何改变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36681157/