use super::dta::release::Release;
use super::missing_value::MissingValue;
use super::stata_error::{Result, StataError};
const MISSING_FLOAT_SYSTEM_113: u32 = 0x7F00_0000;
const MISSING_FLOAT_A_113: u32 = 0x7F00_0800;
const MISSING_FLOAT_STRIDE: u32 = 0x0800;
const PRE_113_FLOAT_MAX_VALID_BITS: u32 = 0x7EFF_FFFF;
const MISSING_FLOAT_SYSTEM_PRE_113: u32 = 0x7F00_0000;
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum StataFloat {
Present(f32),
Missing(MissingValue),
}
impl StataFloat {
pub fn from_raw(raw: f32, release: Release) -> Result<Self> {
let bits = raw.to_bits();
let is_positive = bits & 0x8000_0000 == 0;
if release.supports_tagged_missing() {
if is_positive && bits >= MISSING_FLOAT_SYSTEM_113 {
Ok(Self::Missing(MissingValue::try_from(raw)?))
} else {
Ok(Self::Present(raw))
}
} else if is_positive && bits > PRE_113_FLOAT_MAX_VALID_BITS {
Ok(Self::Missing(MissingValue::System))
} else {
Ok(Self::Present(raw))
}
}
pub fn to_raw(self, release: Release) -> Result<f32> {
match self {
Self::Present(v) => Ok(v),
Self::Missing(mv) => {
if release.supports_tagged_missing() {
let offset = u32::from(mv.code());
let bits = if offset == 0 {
MISSING_FLOAT_SYSTEM_113
} else {
MISSING_FLOAT_A_113 + (offset - 1) * MISSING_FLOAT_STRIDE
};
Ok(f32::from_bits(bits))
} else if mv == MissingValue::System {
Ok(f32::from_bits(MISSING_FLOAT_SYSTEM_PRE_113))
} else {
Err(StataError::TaggedMissingUnsupported)
}
}
}
}
}
#[cfg(test)]
mod tests {
use super::*;
use float_cmp::assert_approx_eq;
#[test]
fn v113_present_zero() {
assert_eq!(
StataFloat::from_raw(0.0_f32, Release::V113).unwrap(),
StataFloat::Present(0.0),
);
}
#[test]
fn v113_present_negative_zero() {
assert_eq!(
StataFloat::from_raw(-0.0_f32, Release::V113).unwrap(),
StataFloat::Present(-0.0),
);
}
#[test]
fn v113_present_one() {
assert_eq!(
StataFloat::from_raw(1.0_f32, Release::V113).unwrap(),
StataFloat::Present(1.0),
);
}
#[test]
fn v113_present_negative() {
assert_eq!(
StataFloat::from_raw(-1.5_f32, Release::V113).unwrap(),
StataFloat::Present(-1.5),
);
}
#[test]
fn v113_present_large_just_below_missing_range() {
let val = f32::from_bits(MISSING_FLOAT_SYSTEM_113 - 1);
assert_eq!(
StataFloat::from_raw(val, Release::V113).unwrap(),
StataFloat::Present(val),
);
}
#[test]
fn v113_present_negative_infinity() {
assert_eq!(
StataFloat::from_raw(f32::NEG_INFINITY, Release::V113).unwrap(),
StataFloat::Present(f32::NEG_INFINITY),
);
}
#[test]
fn v113_error_non_stata_nan() {
let val = f32::from_bits(0x7F00_0001);
assert_eq!(
StataFloat::from_raw(val, Release::V113),
Err(StataError::NotMissingValue),
);
}
#[test]
fn v113_error_positive_infinity() {
assert_eq!(
StataFloat::from_raw(f32::INFINITY, Release::V113),
Err(StataError::NotMissingValue),
);
}
#[test]
fn v113_missing_system() {
assert_eq!(
StataFloat::from_raw(f32::from_bits(0x7F00_0000), Release::V113).unwrap(),
StataFloat::Missing(MissingValue::System),
);
}
#[test]
fn v113_missing_a() {
assert_eq!(
StataFloat::from_raw(f32::from_bits(0x7F00_0800), Release::V113).unwrap(),
StataFloat::Missing(MissingValue::A),
);
}
#[test]
fn v113_missing_z() {
assert_eq!(
StataFloat::from_raw(f32::from_bits(0x7F00_D000), Release::V113).unwrap(),
StataFloat::Missing(MissingValue::Z),
);
}
#[test]
fn v104_present_zero() {
assert_eq!(
StataFloat::from_raw(0.0_f32, Release::V104).unwrap(),
StataFloat::Present(0.0),
);
}
#[test]
fn v104_present_normal() {
assert_eq!(
StataFloat::from_raw(42.0_f32, Release::V104).unwrap(),
StataFloat::Present(42.0),
);
}
#[test]
fn v104_present_negative() {
assert_eq!(
StataFloat::from_raw(-1.5_f32, Release::V104).unwrap(),
StataFloat::Present(-1.5),
);
}
#[test]
fn v104_present_negative_infinity() {
assert_eq!(
StataFloat::from_raw(f32::NEG_INFINITY, Release::V104).unwrap(),
StataFloat::Present(f32::NEG_INFINITY),
);
}
#[test]
fn v104_present_max_valid() {
let val = f32::from_bits(PRE_113_FLOAT_MAX_VALID_BITS);
assert_eq!(
StataFloat::from_raw(val, Release::V104).unwrap(),
StataFloat::Present(val),
);
}
#[test]
fn v104_missing_system_canonical() {
assert_eq!(
StataFloat::from_raw(f32::from_bits(0x7F00_0000), Release::V104).unwrap(),
StataFloat::Missing(MissingValue::System),
);
}
#[test]
fn v104_missing_system_above_max() {
assert_eq!(
StataFloat::from_raw(f32::from_bits(0x7F00_0001), Release::V104).unwrap(),
StataFloat::Missing(MissingValue::System),
);
}
#[test]
fn v104_missing_positive_infinity() {
assert_eq!(
StataFloat::from_raw(f32::INFINITY, Release::V104).unwrap(),
StataFloat::Missing(MissingValue::System),
);
}
#[test]
fn v112_missing_system() {
assert_eq!(
StataFloat::from_raw(f32::from_bits(0x7F00_0000), Release::V112).unwrap(),
StataFloat::Missing(MissingValue::System),
);
}
#[test]
fn v113_to_raw_present_zero() {
assert_approx_eq!(
f32,
StataFloat::Present(0.0).to_raw(Release::V113).unwrap(),
0.0
);
}
#[test]
fn v113_to_raw_present_normal() {
assert_approx_eq!(
f32,
StataFloat::Present(1.5).to_raw(Release::V113).unwrap(),
1.5
);
}
#[test]
fn v113_to_raw_present_negative() {
assert_approx_eq!(
f32,
StataFloat::Present(-1.5).to_raw(Release::V113).unwrap(),
-1.5
);
}
#[test]
fn v113_to_raw_missing_system() {
let got = StataFloat::Missing(MissingValue::System)
.to_raw(Release::V113)
.unwrap();
assert_eq!(got.to_bits(), 0x7F00_0000);
}
#[test]
fn v113_to_raw_missing_a() {
let got = StataFloat::Missing(MissingValue::A)
.to_raw(Release::V113)
.unwrap();
assert_eq!(got.to_bits(), 0x7F00_0800);
}
#[test]
fn v113_to_raw_missing_z() {
let got = StataFloat::Missing(MissingValue::Z)
.to_raw(Release::V113)
.unwrap();
assert_eq!(got.to_bits(), 0x7F00_D000);
}
#[test]
fn v104_to_raw_missing_system() {
let got = StataFloat::Missing(MissingValue::System)
.to_raw(Release::V104)
.unwrap();
assert_eq!(got.to_bits(), 0x7F00_0000);
}
#[test]
fn v104_to_raw_missing_tagged_errors() {
assert_eq!(
StataFloat::Missing(MissingValue::A).to_raw(Release::V104),
Err(StataError::TaggedMissingUnsupported),
);
}
#[test]
fn v112_to_raw_missing_tagged_errors() {
assert_eq!(
StataFloat::Missing(MissingValue::Z).to_raw(Release::V112),
Err(StataError::TaggedMissingUnsupported),
);
}
}