Closed
Description
This tests (taken from rust tests):
#![feature(dec2flt)]
#![allow(unused_macros, unused_imports, dead_code)]
extern crate core;
use std::num::FpCategory::*;
use std::num::FpCategory as Fp;
use core::num::dec2flt::rawfp::{RawFloat, Unpacked};
const SOME_FLOATS: [f64; 9] =
[0.1f64, 33.568, 42.1e-5, 777.0e9, 1.1111, 0.347997,
9843579834.35892, 12456.0e-150, 54389573.0e-150];
// src/libcore/tests/num/dec2flt/mod.rs
// Take a float literal, turn it into a string in various ways (that are all trusted
// to be correct) and see if those strings are parsed back to the value of the literal.
// Requires a *polymorphic literal*, i.e. one that can serve as f64 as well as f32.
macro_rules! test_literal {
($x: expr) => ({
let x32: f32 = $x;
let x64: f64 = $x;
let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)];
for input in inputs {
assert_eq!(input.parse(), Ok(x64));
assert_eq!(input.parse(), Ok(x32));
let neg_input = &format!("-{}", input);
assert_eq!(neg_input.parse(), Ok(-x64));
assert_eq!(neg_input.parse(), Ok(-x32));
}
})
}
#[test]
fn ordinary() {
test_literal!(1.0);
test_literal!(3e-5);
test_literal!(0.1);
test_literal!(12345.);
test_literal!(0.9999999);
test_literal!(2.2250738585072014e-308);
}
#[test]
fn special_code_paths() {
test_literal!(36893488147419103229.0); // 2^65 - 3, triggers half-to-even with even significand
test_literal!(101e-33); // Triggers the tricky underflow case in AlgorithmM (for f32)
test_literal!(1e23); // Triggers AlgorithmR
test_literal!(2075e23); // Triggers another path through AlgorithmR
test_literal!(8713e-23); // ... and yet another.
}
// src/libcore/tests/num/dec2flt/rawfp.rs
#[test]
fn prev_float_monotonic() {
let mut x = 1.0;
for _ in 0..100 {
let x1 = prev_float(x);
assert!(x1 < x);
assert!(x - x1 < 1e-15);
x = x1;
}
}
#[test]
fn next_prev_identity() {
for &x in &SOME_FLOATS {
assert_eq!(prev_float(next_float(x)), x);
assert_eq!(prev_float(prev_float(next_float(next_float(x)))), x);
assert_eq!(next_float(prev_float(x)), x);
assert_eq!(next_float(next_float(prev_float(prev_float(x)))), x);
}
}
/// Inverse of `RawFloat::unpack()` for normalized numbers.
/// Panics if the significand or exponent are not valid for normalized numbers.
pub fn encode_normal<T: RawFloat>(x: Unpacked) -> T {
debug_assert!(T::MIN_SIG <= x.sig && x.sig <= T::MAX_SIG,
"encode_normal: significand not normalized");
// Remove the hidden bit
let sig_enc = x.sig & !(1 << T::EXPLICIT_SIG_BITS);
// Adjust the exponent for exponent bias and mantissa shift
let k_enc = x.k + T::MAX_EXP + T::EXPLICIT_SIG_BITS as i16;
debug_assert!(k_enc != 0 && k_enc < T::MAX_ENCODED_EXP,
"encode_normal: exponent out of range");
// Leave sign bit at 0 ("+"), our numbers are all positive
let bits = (k_enc as u64) << T::EXPLICIT_SIG_BITS | sig_enc;
T::from_bits(bits)
}
/// Find the largest floating point number strictly smaller than the argument.
/// Does not handle subnormals, zero, or exponent underflow.
pub fn prev_float<T: RawFloat>(x: T) -> T {
match x.classify() {
Infinite => panic!("prev_float: argument is infinite"),
Nan => panic!("prev_float: argument is NaN"),
Subnormal => panic!("prev_float: argument is subnormal"),
Zero => panic!("prev_float: argument is zero"),
Normal => {
let Unpacked { sig, k } = x.unpack();
if sig == T::MIN_SIG {
encode_normal(Unpacked::new(T::MAX_SIG, k - 1))
} else {
encode_normal(Unpacked::new(sig - 1, k))
}
}
}
}
// Find the smallest floating point number strictly larger than the argument.
// This operation is saturating, i.e. next_float(inf) == inf.
// Unlike most code in this module, this function does handle zero, subnormals, and infinities.
// However, like all other code here, it does not deal with NaN and negative numbers.
pub fn next_float<T: RawFloat>(x: T) -> T {
match x.classify() {
Nan => panic!("next_float: argument is NaN"),
Infinite => T::INFINITY,
// This seems too good to be true, but it works.
// 0.0 is encoded as the all-zero word. Subnormals are 0x000m...m where m is the mantissa.
// In particular, the smallest subnormal is 0x0...01 and the largest is 0x000F...F.
// The smallest normal number is 0x0010...0, so this corner case works as well.
// If the increment overflows the mantissa, the carry bit increments the exponent as we
// want, and the mantissa bits become zero. Because of the hidden bit convention, this
// too is exactly what we want!
// Finally, f64::MAX + 1 = 7eff...f + 1 = 7ff0...0 = f64::INFINITY.
Zero | Subnormal | Normal => {
let bits: u64 = x.transmute();
T::from_bits(bits + 1)
}
}
}
// src/libcore/tests/num/mod.rs
#[test]
fn test_f32f64() {
use core::f32;
let max: f64 = f32::MAX.into();
assert_eq!(max as f32, f32::MAX);
assert!(max.is_normal());
let min: f64 = f32::MIN.into();
assert_eq!(min as f32, f32::MIN);
assert!(min.is_normal());
let min_positive: f64 = f32::MIN_POSITIVE.into();
assert_eq!(min_positive as f32, f32::MIN_POSITIVE);
assert!(min_positive.is_normal());
let epsilon: f64 = f32::EPSILON.into();
assert_eq!(epsilon as f32, f32::EPSILON);
assert!(epsilon.is_normal());
let zero: f64 = (0.0f32).into();
assert_eq!(zero as f32, 0.0f32);
assert!(zero.is_sign_positive());
let neg_zero: f64 = (-0.0f32).into();
assert_eq!(neg_zero as f32, -0.0f32);
assert!(neg_zero.is_sign_negative());
let infinity: f64 = f32::INFINITY.into();
assert_eq!(infinity as f32, f32::INFINITY);
assert!(infinity.is_infinite());
assert!(infinity.is_sign_positive());
let neg_infinity: f64 = f32::NEG_INFINITY.into();
assert_eq!(neg_infinity as f32, f32::NEG_INFINITY);
assert!(neg_infinity.is_infinite());
assert!(neg_infinity.is_sign_negative());
let nan: f64 = f32::NAN.into();
assert!(nan.is_nan());
}
// src/libstd/f64.rs
#[test]
fn test_one() {
let one: f64 = 1.0f64;
assert_eq!(1.0, one);
assert!(!one.is_infinite());
assert!(one.is_finite());
assert!(one.is_sign_positive());
assert!(!one.is_sign_negative());
assert!(!one.is_nan());
assert!(one.is_normal());
assert_eq!(Fp::Normal, one.classify());
}
#[test]
fn test_is_normal() {
use std::f64::*;
let nan: f64 = NAN;
let inf: f64 = INFINITY;
let neg_inf: f64 = NEG_INFINITY;
let zero: f64 = 0.0f64;
let neg_zero: f64 = -0.0;
assert!(!nan.is_normal());
assert!(!inf.is_normal());
assert!(!neg_inf.is_normal());
assert!(!zero.is_normal());
assert!(!neg_zero.is_normal());
assert!(1f64.is_normal());
assert!(1e-307f64.is_normal());
assert!(!1e-308f64.is_normal());
}
#[test]
fn test_classify() {
use std::f64::*;
let nan: f64 = NAN;
let inf: f64 = INFINITY;
let neg_inf: f64 = NEG_INFINITY;
let zero: f64 = 0.0f64;
let neg_zero: f64 = -0.0;
assert_eq!(nan.classify(), Fp::Nan);
assert_eq!(inf.classify(), Fp::Infinite);
assert_eq!(neg_inf.classify(), Fp::Infinite);
assert_eq!(zero.classify(), Fp::Zero);
assert_eq!(neg_zero.classify(), Fp::Zero);
assert_eq!(1e-307f64.classify(), Fp::Normal);
assert_eq!(1e-308f64.classify(), Fp::Subnormal);
}
fails on wasm32-unknown-emscripten
. I think that this as bug in asm2wasm because the same tests works on asmjs-unknown-emscripten
. I used cross to run the tests (it is necessary to create a project and put the code in lib.rs):
cross test --target asmjs-unknown-emscripten # works
cross test --target wasm32-unknown-emscripten # fails
Tests results:
running 8 tests
test next_prev_identity ... FAILED
test ordinary ... FAILED
test prev_float_monotonic ... FAILED
test special_code_paths ... FAILED
test test_classify ... FAILED
test test_f32f64 ... FAILED
test test_is_normal ... FAILED
test test_one ... FAILED
failures:
---- next_prev_identity stdout ----
thread 'main' panicked at 'prev_float: argument is subnormal', src/lib.rs:101
note: Run with `RUST_BACKTRACE=1` for a backtrace.
---- ordinary stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)` (left: `Ok(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002225073858507201)`, right: `Ok(0.00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000002225073858507201)`)', src/lib.rs:42
---- prev_float_monotonic stdout ----
thread 'main' panicked at 'prev_float: argument is subnormal', src/lib.rs:101
---- special_code_paths stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)` (left: `Ok(36893488147419100000)`, right: `Ok(36893488147419100000)`)', src/lib.rs:47
---- test_classify stdout ----
thread 'main' panicked at 'assertion failed: `(left == right)` (left: `Subnormal`, right: `Normal`)', src/lib.rs:227
---- test_f32f64 stdout ----
thread 'main' panicked at 'assertion failed: max.is_normal()', src/lib.rs:145
---- test_is_normal stdout ----
thread 'main' panicked at 'assertion failed: 1f64.is_normal()', src/lib.rs:209
---- test_one stdout ----
thread 'main' panicked at 'assertion failed: one.is_normal()', src/lib.rs:192
failures:
next_prev_identity
ordinary
prev_float_monotonic
special_code_paths
test_classify
test_f32f64
test_is_normal
test_one
test result: FAILED. 0 passed; 8 failed; 0 ignored; 0 measured; 0 filtered out