大佬教程收集整理的这篇文章主要介绍了将double转换为float时如何确定舍入方向?,大佬教程大佬觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在寻找一种算法来确定将任意 64 位双精度值转换为 32 位浮点数时的舍入方向。我进行此检查的具体用例是将 64 位双精度值转换为 32 位浮点数,并朝无穷大舍入。
起初,我应用以下标准来检查尾数截断部分中的位。如果尾数的截断部分不为零,则强制转换必须向下舍入!
const F64_MASK: u64 = (1 << 29) - 1;
fn is_rounded_up_when_casted(v: f64) -> bool {
v.to_bits() & F64_MASK > 0
}
然而,这个标准不能识别所有情况——指数的最后三位也被截断。我尝试修改掩码以检查这些指数位:
const F64_MASK: u64 = (1u64 << 55) - (1 << 52) + (1 << 29) - 1;
很遗憾,此检查不起作用。例如,数字 1.401298464324817e−45
有一个指数,其中三个截断的位是 010
,但仍然以 float/f32 精确表示。
编辑:我认为不能说非零尾数意味着正舍入。我想我需要一种不同的方法。我认为指数只是增加了数字的范围,因此可以通过一些单独的检查来处理。舍入方向可能只是尾数截断部分的前导位的函数?
您发现的边缘情况与以下事实有关:低于正常的 f32 值实际上可以表示小于其典型最小值的指数。我编写了一个我认为涵盖所有边缘情况的函数:
const F64_MANTISSA_SIZE: u64 = 52;
const F64_MANTISSA_MASK: u64 = (1 << F64_MANTISSA_SIZE) - 1;
const F64_EXPONENT_SIZE: u64 = 64 - F64_MANTISSA_SIZE - 1;
const F64_EXPONENT_MASK: u64 = (1 << F64_EXPONENT_SIZE) - 1; // shift away the mantissa first
const F32_MANTISSA_SIZE: u64 = 23;
const F64_TRUNCATED_MANTISSA_SIZE: u64 = F64_MANTISSA_SIZE - F32_MANTISSA_SIZE;
const F64_TRUNCATED_MANTISSA_MASK: u64 = (1 << F64_TRUNCATED_MANTISSA_SIZE) - 1;
fn is_exactly_representable_as_f32(v: f64) -> bool {
let bits = v.to_bits();
let mantissa = bits & F64_MANTISSA_MASK;
let exponent = (bits >> F64_MANTISSA_SIZE) & F64_EXPONENT_MASK;
let _sign = bits >> (F64_MANTISSA_SIZE + F64_EXPONENT_SIZE) != 0;
if exponent == 0 {
// if mantissa == 0,the float is 0 or -0,which is representable
// if mantissa != 0,it's a subnormal,which is never representable
return mantissa == 0;
}
if exponent == F64_EXPONENT_MASK {
// either infinity or nan,all of which are representable
return true;
}
// remember to subtract the bias
let computed_exponent = exponent as i64 - 1023;
// -126 and 127 are the min and max value for a standard f32 exponent
if (-126..=127).contains(&computed_exponent) {
// at this point,it's only exactly representable if the truncated mantissa is all zero
return mantissa & F64_TRUNCATED_MANTISSA_MASK == 0;
}
// exponents less than 2**(-126) may be representable by f32 subnormals
if computed_exponent < -126 {
// this is the number of leading zeroes that need to be in the f32 mantissa
let diff = -127 - computed_exponent;
// this is the number of bits in the mantissa that must be preserved (essentially mantissa with Trailing zeroes trimmed off)
let mantissa_bits = F64_MANTISSA_SIZE - (mantissa.Trailing_zeros() as u64).min(F64_MANTISSA_SIZE) + 1;
// the leading zeroes + essential mantissa bits must be able to fit in the smaller mantissa size
return diff as u64 + mantissa_bits <= F32_MANTISSA_SIZE;
}
// the exponent is >127 so f32s can't go that high
return false;
}
,
无需修改位:
#[derive(PartialEq,std::fmt::Debug)]
enum Direction { Equal,Up,Down }
fn get_rounding_direction(v: f64) -> Direction {
match v.partial_cmp(&(v as f32 as f64)) {
Some(Ordering::Greater) => Direction::Down,Some(Ordering::Less) => Direction::Up,_ => Direction::Equal
}
}
还有一些测试来检查正确性。
#[cfg(test)]
#[test]
fn test_get_rounding_direction() {
// check that the f64 one step below 2 casts to exactly 2
assert_eq!(get_rounding_direction(1.9999999999999998),Direction::Up);
// check edge cases
assert_eq!(get_rounding_direction(f64::NAN),Direction::Equal);
assert_eq!(get_rounding_direction(f64::NEG_INFINITY),Direction::Equal);
assert_eq!(get_rounding_direction(f64::MIN),Direction::Down);
assert_eq!(get_rounding_direction(-f64::MIN_POSITIVE),Direction::Up);
assert_eq!(get_rounding_direction(-0.),Direction::Equal);
assert_eq!(get_rounding_direction(0.),Direction::Equal);
assert_eq!(get_rounding_direction(f64::MIN_POSITIVE),Direction::Down);
assert_eq!(get_rounding_direction(f64::MAX),Direction::Up);
assert_eq!(get_rounding_direction(f64::INFINITY),Direction::Equal);
// for all other f32
for u32_bits in 1..f32::INFINITY.to_bits() - 1 {
let f64_value = f32::from_bits(u32_bits) as f64;
let u64_bits = f64_value.to_bits();
if u32_bits % 100_000_000 == 0 {
println!("checkpoint every 600 million tests: {}",f64_value);
}
// check that the f64 equivalent to the current f32 casts to a value that is equivalent
assert_eq!(get_rounding_direction(f64_value),Direction::Equal,"at {},{}",u32_bits,f64_value);
// check that the f64 one step below the f64 equivalent to the current f32 casts to a value that is one step greater
assert_eq!(get_rounding_direction(f64::from_bits(u64_bits - 1)),Direction::Up,f64_value);
// check that the f64 one step above the f64 equivalent to the current f32 casts to a value that is one step less
assert_eq!(get_rounding_direction(f64::from_bits(u64_bits + 1)),Direction::Down,f64_value);
// same checks for negative numbers
let u64_bits = (-f64_value).to_bits();
assert_eq!(get_rounding_direction(f64_value),f64_value);
assert_eq!(get_rounding_direction(f64::from_bits(u64_bits - 1)),f64_value);
assert_eq!(get_rounding_direction(f64::from_bits(u64_bits + 1)),f64_value);
}
}
特别是向无穷大舍入:
fn cast_toWARD_inf(vf64: f64) -> f32 {
let vf32 = vf64 as f32;
if vf64 > vf32 as f64 { f32::from_bits(vf32.to_bits() + 1) } else { vf32 }
}
可以主要从第 28 位(尾数截断部分的第一位)确定舍入,但处理边缘情况会带来显着的复杂性。
以上是大佬教程为你收集整理的将double转换为float时如何确定舍入方向?全部内容,希望文章能够帮你解决将double转换为float时如何确定舍入方向?所遇到的程序开发问题。
如果觉得大佬教程网站内容还不错,欢迎将大佬教程推荐给程序员好友。
本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。