mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1614971 - Update cubeb-coreaudio to 4acd802. r=padenot
Pick commits: - 4acd802: Destroy the stream properly - 54d950a: Run the tests in the subcrate (#51) - 132d209: Clean up clippy warnings and errors (#49) Differential Revision: https://phabricator.services.mozilla.com/D63725 --HG-- extra : moz-landing-system : lando
This commit is contained in:
parent
9b7d17bb03
commit
eb3b669f6b
@ -70,7 +70,7 @@ rev = "5e870faf6f95d79d11efc813e56370ad124bbed5"
|
||||
[source."https://github.com/ChunMinChang/cubeb-coreaudio-rs"]
|
||||
git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "7fe03b4201160f84aad1c12536affe6b05f8839f"
|
||||
rev = "4acd80233efa645ac79769f37b07d495c1b42070"
|
||||
|
||||
[source.crates-io]
|
||||
replace-with = "vendored-sources"
|
||||
|
14
Cargo.lock
generated
14
Cargo.lock
generated
@ -678,7 +678,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "coreaudio-sys-utils"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=7fe03b4201160f84aad1c12536affe6b05f8839f#7fe03b4201160f84aad1c12536affe6b05f8839f"
|
||||
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=4acd80233efa645ac79769f37b07d495c1b42070#4acd80233efa645ac79769f37b07d495c1b42070"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"coreaudio-sys",
|
||||
@ -915,13 +915,14 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "cubeb-coreaudio"
|
||||
version = "0.1.0"
|
||||
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=7fe03b4201160f84aad1c12536affe6b05f8839f#7fe03b4201160f84aad1c12536affe6b05f8839f"
|
||||
source = "git+https://github.com/ChunMinChang/cubeb-coreaudio-rs?rev=4acd80233efa645ac79769f37b07d495c1b42070#4acd80233efa645ac79769f37b07d495c1b42070"
|
||||
dependencies = [
|
||||
"atomic",
|
||||
"audio-mixer",
|
||||
"bitflags",
|
||||
"coreaudio-sys-utils",
|
||||
"cubeb-backend",
|
||||
"float-cmp",
|
||||
"lazy_static",
|
||||
"libc",
|
||||
"mach",
|
||||
@ -1260,6 +1261,15 @@ dependencies = [
|
||||
"miniz_oxide",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "float-cmp"
|
||||
version = "0.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "da62c4f1b81918835a8c6a484a397775fff5953fe83529afd51b05f5c6a6617d"
|
||||
dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "fluent-langneg"
|
||||
version = "0.12.1"
|
||||
|
@ -1 +1 @@
|
||||
{"files":{"Cargo.toml":"35acbd2f8633a6109f3d3e554bef8d847c049ce6ef7a5f570468819e41344d7f","src/aggregate_device.rs":"7d2bd5f5fd7f3d008ebb69ad81f522ca0cb73db6d7b3e50ed1a63ea26ff721f4","src/audio_object.rs":"df10160d9fd83a2c23a49e69b78d39db3a9d6389607df6acfc05821293b6af5f","src/audio_unit.rs":"bc743a1b8033ab5459520c75d7f5230d24cda5ea1198a5b4e1594256af308f47","src/cf_mutable_dict.rs":"fc42edd270c6dfb02f123214d2d8e487bbd62b5bd923b71eec13190fd0104d2a","src/dispatch.rs":"195ca94cbc61948637bfdcbe22070a1e6d41e97cec22301df4e45dcef7b1c208","src/lib.rs":"bcc559d69ef6ed0cbea5b2a36fec89d8c011eb9da70e2f26c00f881ad97a2546","src/string.rs":"28f88b816c768bcfcc674a60d962b93f1c94e5e0f4cc8ed2a1301138b91039e7"},"package":null}
|
||||
{"files":{"Cargo.toml":"35acbd2f8633a6109f3d3e554bef8d847c049ce6ef7a5f570468819e41344d7f","src/aggregate_device.rs":"7d2bd5f5fd7f3d008ebb69ad81f522ca0cb73db6d7b3e50ed1a63ea26ff721f4","src/audio_object.rs":"df10160d9fd83a2c23a49e69b78d39db3a9d6389607df6acfc05821293b6af5f","src/audio_unit.rs":"d783878930df4923b57ad230138c0f3fd6b0b9bb80a39725092ff4c6615162d8","src/cf_mutable_dict.rs":"fc42edd270c6dfb02f123214d2d8e487bbd62b5bd923b71eec13190fd0104d2a","src/dispatch.rs":"195ca94cbc61948637bfdcbe22070a1e6d41e97cec22301df4e45dcef7b1c208","src/lib.rs":"bcc559d69ef6ed0cbea5b2a36fec89d8c011eb9da70e2f26c00f881ad97a2546","src/string.rs":"28f88b816c768bcfcc674a60d962b93f1c94e5e0f4cc8ed2a1301138b91039e7"},"package":null}
|
@ -1,23 +1,26 @@
|
||||
use coreaudio_sys::*;
|
||||
use std::convert::TryFrom;
|
||||
use std::os::raw::c_void;
|
||||
use std::ptr;
|
||||
|
||||
pub fn audio_unit_get_property_info(
|
||||
unit: AudioUnit,
|
||||
property: AudioUnitPropertyID,
|
||||
scope: AudioUnitScope,
|
||||
element: AudioUnitElement,
|
||||
size: *mut usize,
|
||||
writable: *mut Boolean,
|
||||
size: &mut usize,
|
||||
writable: Option<&mut bool>, // Use `Option` since `writable` is nullable.
|
||||
) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
assert!(UInt32::try_from(*size).is_ok()); // Check if `size` can be converted to a UInt32.
|
||||
unsafe {
|
||||
AudioUnitGetPropertyInfo(
|
||||
unit,
|
||||
property,
|
||||
scope,
|
||||
element,
|
||||
size as *mut UInt32,
|
||||
writable,
|
||||
size as *mut usize as *mut UInt32,
|
||||
writable.map_or(ptr::null_mut(), |v| v as *mut bool as *mut Boolean),
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -27,18 +30,19 @@ pub fn audio_unit_get_property<T>(
|
||||
property: AudioUnitPropertyID,
|
||||
scope: AudioUnitScope,
|
||||
element: AudioUnitElement,
|
||||
data: *mut T,
|
||||
size: *mut usize,
|
||||
data: &mut T,
|
||||
size: &mut usize,
|
||||
) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
assert!(UInt32::try_from(*size).is_ok()); // Check if `size` can be converted to a UInt32.
|
||||
unsafe {
|
||||
AudioUnitGetProperty(
|
||||
unit,
|
||||
property,
|
||||
scope,
|
||||
element,
|
||||
data as *mut c_void,
|
||||
size as *mut UInt32,
|
||||
data as *mut T as *mut c_void,
|
||||
size as *mut usize as *mut UInt32,
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -48,7 +52,7 @@ pub fn audio_unit_set_property<T>(
|
||||
property: AudioUnitPropertyID,
|
||||
scope: AudioUnitScope,
|
||||
element: AudioUnitElement,
|
||||
data: *const T,
|
||||
data: &T,
|
||||
size: usize,
|
||||
) -> OSStatus {
|
||||
assert!(!unit.is_null());
|
||||
@ -58,7 +62,7 @@ pub fn audio_unit_set_property<T>(
|
||||
property,
|
||||
scope,
|
||||
element,
|
||||
data as *const c_void,
|
||||
data as *const T as *const c_void,
|
||||
size as UInt32,
|
||||
)
|
||||
}
|
||||
@ -121,11 +125,11 @@ pub fn audio_output_unit_stop(unit: AudioUnit) -> OSStatus {
|
||||
|
||||
pub fn audio_unit_render(
|
||||
in_unit: AudioUnit,
|
||||
io_action_flags: *mut AudioUnitRenderActionFlags,
|
||||
in_time_stamp: *const AudioTimeStamp,
|
||||
io_action_flags: &mut AudioUnitRenderActionFlags,
|
||||
in_time_stamp: &AudioTimeStamp,
|
||||
in_output_bus_number: u32,
|
||||
in_number_frames: u32,
|
||||
io_data: *mut AudioBufferList,
|
||||
io_data: &mut AudioBufferList,
|
||||
) -> OSStatus {
|
||||
assert!(!in_unit.is_null());
|
||||
unsafe {
|
||||
|
@ -1 +1 @@
|
||||
{"files":{".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".travis.yml":"71e11635d066577025bc5c4a37bf4257f74fef3ae9ee82884b4a2ef73a55f639","Cargo.toml":"6215c23a25515907eabb24a265c192458f93836b6721ab1282a2e43201230714","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"fa323b7386a8a0c75478b77a30a3c5d33f1df23d9775b97570fa0501ef784e95","run_sanitizers.sh":"6d0eb16ec37d7808b7efe862426f7b8805985da31074f645594723f48a2f003c","run_tests.sh":"c764a89fe2a6b7ccdeb01895d29b2017c5b76f468aff456050babd66c1472687","src/backend/aggregate_device.rs":"7cd732f3e1e71876753515b26ee69a315414e58869216e99ff95c6828408c4db","src/backend/auto_array.rs":"5f35545baba2b005e13a2225bd1cbdd94ffc2097554d61479929bfc5442a6dd6","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/device_property.rs":"1b066b48ed09026a9286b1b8f40e2720854c3410b0f02c795a580792fda34ea9","src/backend/mixer.rs":"6887f90867d0f47a6cd14b3b77b3a0050beb6cf0e69447ec34719871ffca5e9b","src/backend/mod.rs":"3a4c6a953d312298d669a3fbf32d918e72e8e6836ab78775cd71530e4d35e7b4","src/backend/resampler.rs":"fd1281d28a4db1659d2f75e43b8457651745e1b6eb5a53a77f04d752135f6dc7","src/backend/tests/aggregate_device.rs":"107f5c637844cd5ae43d2b42cec4ef3369bb702751586078c0a9d50f039161cd","src/backend/tests/api.rs":"0c78b255921a5c13310132134bb194fc6d9c6fbe90fd05624b82b552711c5a8a","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"8533df1357c154922200a706bbb21d20007fd3a343d9c47697314f91c1c6a281","src/backend/tests/device_property.rs":"b1a9ae79aa5b9a3f180040d0ef0954b134680d586882d2062c5e017b555bff57","src/backend/tests/interfaces.rs":"14943e84a79976a7ef52882edeb9330350705d5190db6647f98b4ffa851a8396","src/backend/tests/manual.rs":"dc707836dab31f83d4b325afbc4dc4c8104ac8036e87f59ade3309ee83fe2d3f","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"f9e1883660d6146b6e5075806561f5f689810e25c5e7764dfd28c9b939821a49","src/backend/tests/tone.rs":"16150438317ce501986734167b5fb97bfec567228acbcd8f3b4c4484c22f29e0","src/backend/tests/utils.rs":"b74f10216bd02d5336f46db371c9c8e1a648deead84068d9397eca03986aae1e","src/backend/utils.rs":"ee77bc266d672d3d9e23eb3290c1f897687394c6e459338804a17433380a6fd2","src/capi.rs":"61f8f0c4373adaefba1eb6e7084687e83a10136db96438bc35884327668e411f","src/lib.rs":"1ff4b738ed194061fca4ff745f847dea4de4e7a4fa1f898e7b4ad5e70c62386d","todo.md":"29545b4d9c516396f82bd392797e2713d4602036eaba0f151b384af764f8515f"},"package":null}
|
||||
{"files":{".editorconfig":"4e53b182bcc78b83d7e1b5c03efa14d22d4955c4ed2514d1ba4e99c1eb1a50ba",".travis.yml":"878d9519da0844cd3de2999f81fce966452f0fb65150605c25a1abf3523360ab","Cargo.toml":"bcb3ec3785c3cbe799bb41c07176afdd0028328be79932a4e44bc97a364e8c69","LICENSE":"6e6f56aff5bbf3cbc60747e152fb1a719bd0716aaf6d711c554f57d92e96297c","README.md":"fa323b7386a8a0c75478b77a30a3c5d33f1df23d9775b97570fa0501ef784e95","install_rustfmt_clippy.sh":"4ae90d8dcb9757cb3ae4ae142ef80e5377c0dde61c63f4a3c32418646e80ca7b","run_sanitizers.sh":"913efec447b4c37751af93eef4a974f293426c20c720d37c5bbe69e955d105eb","run_tests.sh":"fcdae57b81426c5dbfc8873a9ff1ac07924ed0717f822b50a67934cac2e9d4f1","src/backend/aggregate_device.rs":"ae21129aa6b3c7bd3376751b6a94d1ebe6c9f7afcd1db3107fb4d703d04da6b3","src/backend/auto_array.rs":"5f35545baba2b005e13a2225bd1cbdd94ffc2097554d61479929bfc5442a6dd6","src/backend/auto_release.rs":"050fdcee74cf46b9a8a85a877e166d72a853d33220f59cf734cbb6ea09daa441","src/backend/device_property.rs":"324a7c9672daa49ee2221a56d140e1c8298719dab66f204b19d10f3632f96602","src/backend/mixer.rs":"0c237bd5ca63b028c4b22ddc5bc026d7e21c0fa9b4e337f00b6131ed0a0806a5","src/backend/mod.rs":"250cf24aabe1f1e9e8eafd375a538554523accaa6b514588b157d0c413d39b86","src/backend/resampler.rs":"fd1281d28a4db1659d2f75e43b8457651745e1b6eb5a53a77f04d752135f6dc7","src/backend/tests/aggregate_device.rs":"107f5c637844cd5ae43d2b42cec4ef3369bb702751586078c0a9d50f039161cd","src/backend/tests/api.rs":"d81128f0982ecc816666e91f57e722cc21ba9cc09a2a36103acc7c981f57b36d","src/backend/tests/backlog.rs":"3b189a7e036543c467cc242af0ed3332721179ee2b1c8847a6db563546f1ac52","src/backend/tests/device_change.rs":"2138e7ed4721872ce3a41f3652bfa4e7eca97fd136473af6472313c61ff24ed3","src/backend/tests/device_property.rs":"b1a9ae79aa5b9a3f180040d0ef0954b134680d586882d2062c5e017b555bff57","src/backend/tests/interfaces.rs":"14943e84a79976a7ef52882edeb9330350705d5190db6647f98b4ffa851a8396","src/backend/tests/manual.rs":"dc707836dab31f83d4b325afbc4dc4c8104ac8036e87f59ade3309ee83fe2d3f","src/backend/tests/mod.rs":"8dba770023d7f9c4228f0e11915347f0e07da5fd818e3ee4478c4b197af9aa2a","src/backend/tests/parallel.rs":"f9e1883660d6146b6e5075806561f5f689810e25c5e7764dfd28c9b939821a49","src/backend/tests/tone.rs":"16150438317ce501986734167b5fb97bfec567228acbcd8f3b4c4484c22f29e0","src/backend/tests/utils.rs":"1bb99ef71d3c18545bca49767e7b6bfffbe11896246994f41be7ed372772fd48","src/backend/utils.rs":"5ce1b753af0ffb654b6b2038d649aea88eebd27390a607a6d5988a9e40a4a717","src/capi.rs":"21b66b70545bf04ec719928004d1d9adb45b24ced51288f5b2993d79aaf78f5f","src/lib.rs":"5e586d45cd6b3722f0a6736d9252593299269817a153eef1930a5fb9bfbb56f5","todo.md":"29545b4d9c516396f82bd392797e2713d4602036eaba0f151b384af764f8515f"},"package":null}
|
3
third_party/rust/cubeb-coreaudio/.travis.yml
vendored
3
third_party/rust/cubeb-coreaudio/.travis.yml
vendored
@ -5,9 +5,12 @@ rust:
|
||||
- nightly
|
||||
os:
|
||||
- osx
|
||||
env:
|
||||
- RUST_BACKTRACE=1
|
||||
before_script:
|
||||
- rustc --version
|
||||
- cargo --version
|
||||
- sh install_rustfmt_clippy.sh
|
||||
script:
|
||||
- cargo build --verbose
|
||||
- sh run_tests.sh
|
||||
|
1
third_party/rust/cubeb-coreaudio/Cargo.toml
vendored
1
third_party/rust/cubeb-coreaudio/Cargo.toml
vendored
@ -12,6 +12,7 @@ atomic = "0.4"
|
||||
bitflags = "1.0"
|
||||
coreaudio-sys-utils = { path = "coreaudio-sys-utils" }
|
||||
cubeb-backend = "0.6"
|
||||
float-cmp = "0.6"
|
||||
libc = "0.2"
|
||||
lazy_static = "1.2"
|
||||
mach = "0.3"
|
||||
|
17
third_party/rust/cubeb-coreaudio/install_rustfmt_clippy.sh
vendored
Normal file
17
third_party/rust/cubeb-coreaudio/install_rustfmt_clippy.sh
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
# https://github.com/rust-lang/rustup-components-history/blob/master/README.md
|
||||
# https://github.com/rust-lang/rust-clippy/blob/27acea0a1baac6cf3ac6debfdbce04f91e15d772/.travis.yml#L40-L46
|
||||
if ! rustup component add rustfmt; then
|
||||
TARGET=$(rustc -Vv | awk '/host/{print $2}')
|
||||
NIGHTLY=$(curl -s "https://rust-lang.github.io/rustup-components-history/${TARGET}/rustfmt")
|
||||
curl -sSL "https://static.rust-lang.org/dist/${NIGHTLY}/rustfmt-nightly-${TARGET}.tar.xz" | \
|
||||
tar -xJf - --strip-components=3 -C ~/.cargo/bin
|
||||
rm -rf ~/.cargo/bin/doc
|
||||
fi
|
||||
|
||||
if ! rustup component add clippy; then
|
||||
TARGET=$(rustc -Vv | awk '/host/{print $2}')
|
||||
NIGHTLY=$(curl -s "https://rust-lang.github.io/rustup-components-history/${TARGET}/clippy")
|
||||
rustup default nightly-${NIGHTLY}
|
||||
rustup component add rustfmt
|
||||
rustup component add clippy
|
||||
fi
|
@ -14,6 +14,26 @@ else
|
||||
exit
|
||||
fi
|
||||
|
||||
# Run tests in the sub crate
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
cd coreaudio-sys-utils
|
||||
|
||||
echo "\n\nRun ASan\n-----------\n"
|
||||
RUSTFLAGS="-Z sanitizer=address" cargo test
|
||||
|
||||
echo "\n\nRun LSan\n-----------\n"
|
||||
RUSTFLAGS="-Z sanitizer=leak" cargo test
|
||||
|
||||
echo "\n\nRun MSan\n-----------\n"
|
||||
RUSTFLAGS="-Z sanitizer=memory" cargo test
|
||||
|
||||
echo "\n\nRun TSan\n-----------\n"
|
||||
RUSTFLAGS="-Z sanitizer=thread" cargo test
|
||||
|
||||
cd ..
|
||||
|
||||
# Run tests in the main crate
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
echo "\n\nRun ASan\n-----------\n"
|
||||
RUSTFLAGS="-Z sanitizer=address" sh run_tests.sh
|
||||
|
||||
|
23
third_party/rust/cubeb-coreaudio/run_tests.sh
vendored
23
third_party/rust/cubeb-coreaudio/run_tests.sh
vendored
@ -1,6 +1,29 @@
|
||||
# display backtrace for debugging
|
||||
export RUST_BACKTRACE=1
|
||||
|
||||
# Run tests in the sub crate
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
cd coreaudio-sys-utils
|
||||
|
||||
# Format check
|
||||
cargo fmt --all -- --check
|
||||
|
||||
# Lints check
|
||||
cargo clippy -- -D warnings
|
||||
|
||||
# Regular Tests
|
||||
cargo test
|
||||
|
||||
cd ..
|
||||
|
||||
# Run tests in the main crate
|
||||
# -------------------------------------------------------------------------------------------------
|
||||
# Format check
|
||||
cargo fmt --all -- --check
|
||||
|
||||
# Lints check
|
||||
cargo clippy -- -D warnings
|
||||
|
||||
# Regular Tests
|
||||
cargo test --verbose
|
||||
cargo test test_configure_output -- --ignored
|
||||
|
@ -242,15 +242,14 @@ impl AggregateDevice {
|
||||
CFRelease(device_stacked_key as *const c_void);
|
||||
|
||||
// This call will fire `audiounit_collection_changed_callback` indirectly!
|
||||
let status = audio_object_get_property_data_with_qualifier(
|
||||
audio_object_get_property_data_with_qualifier(
|
||||
plugin_id,
|
||||
&address,
|
||||
mem::size_of_val(&device_dict),
|
||||
&device_dict,
|
||||
&mut size,
|
||||
&mut device_id,
|
||||
);
|
||||
status
|
||||
)
|
||||
};
|
||||
if status == NO_ERR {
|
||||
assert_ne!(device_id, kAudioObjectUnknown);
|
||||
|
@ -83,7 +83,7 @@ pub fn get_device_label(
|
||||
id: AudioDeviceID,
|
||||
devtype: DeviceType,
|
||||
) -> std::result::Result<StringRef, OSStatus> {
|
||||
get_device_source_name(id, devtype).or(get_device_name(id, devtype))
|
||||
get_device_source_name(id, devtype).or_else(|_| get_device_name(id, devtype))
|
||||
}
|
||||
|
||||
pub fn get_device_manufacturer(
|
||||
@ -217,6 +217,7 @@ pub fn get_device_stream_format(
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_ptr_alignment)] // Allow casting *mut u8 to *mut AudioBufferList
|
||||
pub fn get_device_stream_configuration(
|
||||
id: AudioDeviceID,
|
||||
devtype: DeviceType,
|
||||
|
@ -44,13 +44,17 @@ pub fn get_channel_order(channel_layout: ChannelLayout) -> Vec<audio_mixer::Chan
|
||||
fn get_default_channel_order(channel_count: usize) -> Vec<audio_mixer::Channel> {
|
||||
assert_ne!(channel_count, 0);
|
||||
let mut channels = Vec::with_capacity(channel_count);
|
||||
for i in 0..channel_count {
|
||||
channels.push(if i < CHANNEL_OERDER.len() {
|
||||
CHANNEL_OERDER[i]
|
||||
} else {
|
||||
audio_mixer::Channel::Silence
|
||||
});
|
||||
for channel in CHANNEL_OERDER.iter().take(channel_count) {
|
||||
channels.push(*channel);
|
||||
}
|
||||
|
||||
if channel_count > CHANNEL_OERDER.len() {
|
||||
channels.extend(vec![
|
||||
audio_mixer::Channel::Silence;
|
||||
channel_count - CHANNEL_OERDER.len()
|
||||
]);
|
||||
}
|
||||
|
||||
channels
|
||||
}
|
||||
|
||||
@ -202,7 +206,7 @@ impl Mixer {
|
||||
}
|
||||
|
||||
let all_silence = vec![audio_mixer::Channel::Silence; out_channel_count];
|
||||
if output_channels.len() == 0
|
||||
if output_channels.is_empty()
|
||||
|| out_channel_count != output_channels.len()
|
||||
|| all_silence == output_channels
|
||||
{
|
||||
@ -430,3 +434,17 @@ fn test_get_channel_order() {
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_get_default_channel_order() {
|
||||
for len in 1..CHANNEL_OERDER.len() + 10 {
|
||||
let channels = get_default_channel_order(len);
|
||||
if len <= CHANNEL_OERDER.len() {
|
||||
assert_eq!(channels, &CHANNEL_OERDER[..len]);
|
||||
} else {
|
||||
let silences = vec![audio_mixer::Channel::Silence; len - CHANNEL_OERDER.len()];
|
||||
assert_eq!(channels[..CHANNEL_OERDER.len()], CHANNEL_OERDER);
|
||||
assert_eq!(&channels[CHANNEL_OERDER.len()..], silences.as_slice());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -194,7 +194,7 @@ fn create_device_info(id: AudioDeviceID, devtype: DeviceType) -> Result<device_i
|
||||
assert_ne!(id, kAudioObjectSystemObject);
|
||||
|
||||
let mut info = device_info {
|
||||
id: id,
|
||||
id,
|
||||
flags: match devtype {
|
||||
DeviceType::INPUT => device_flags::DEV_INPUT,
|
||||
DeviceType::OUTPUT => device_flags::DEV_OUTPUT,
|
||||
@ -328,9 +328,9 @@ fn get_volume(unit: AudioUnit) -> Result<f32> {
|
||||
}
|
||||
|
||||
fn minimum_resampling_input_frames(input_rate: f64, output_rate: f64, output_frames: i64) -> i64 {
|
||||
assert_ne!(input_rate, 0_f64);
|
||||
assert_ne!(output_rate, 0_f64);
|
||||
if input_rate == output_rate {
|
||||
assert!(!approx_eq!(f64, input_rate, 0_f64));
|
||||
assert!(!approx_eq!(f64, output_rate, 0_f64));
|
||||
if approx_eq!(f64, input_rate, output_rate) {
|
||||
return output_frames;
|
||||
}
|
||||
(input_rate * output_frames as f64 / output_rate).ceil() as i64
|
||||
@ -383,6 +383,12 @@ extern "C" fn audiounit_input_callback(
|
||||
user_ptr as *const AudioUnitStream
|
||||
);
|
||||
|
||||
// `flags` and `tstamp` must be non-null so they can be casted into the references.
|
||||
assert!(!flags.is_null());
|
||||
let flags = unsafe { &mut (*flags) };
|
||||
assert!(!tstamp.is_null());
|
||||
let tstamp = unsafe { &(*tstamp) };
|
||||
|
||||
// Create the AudioBufferList to store input.
|
||||
let mut input_buffer_list = AudioBufferList::default();
|
||||
input_buffer_list.mBuffers[0].mDataByteSize =
|
||||
@ -533,21 +539,20 @@ extern "C" fn audiounit_input_callback(
|
||||
}
|
||||
}
|
||||
|
||||
let status = match handle {
|
||||
match handle {
|
||||
ErrorHandle::Reinit => {
|
||||
stm.reinit_async();
|
||||
NO_ERR
|
||||
}
|
||||
ErrorHandle::Return(s) => s,
|
||||
};
|
||||
status
|
||||
}
|
||||
}
|
||||
|
||||
fn host_time_to_ns(host_time: u64) -> u64 {
|
||||
let mut rv: f64 = host_time as f64;
|
||||
rv *= HOST_TIME_TO_NS_RATIO.0 as f64;
|
||||
rv /= HOST_TIME_TO_NS_RATIO.1 as f64;
|
||||
return rv as u64;
|
||||
rv as u64
|
||||
}
|
||||
|
||||
fn compute_output_latency(stm: &AudioUnitStream, host_time: u64) -> u32 {
|
||||
@ -748,8 +753,8 @@ extern "C" fn audiounit_output_callback(
|
||||
)
|
||||
};
|
||||
let start = frames_to_bytes(outframes as usize);
|
||||
for i in start..out_bytes.len() {
|
||||
out_bytes[i] = 0;
|
||||
for byte in out_bytes.iter_mut().skip(start) {
|
||||
*byte = 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -926,7 +931,7 @@ fn audiounit_get_preferred_channel_layout(output_unit: AudioUnit) -> Vec<mixer::
|
||||
kAudioUnitScope_Output,
|
||||
AU_OUT_BUS,
|
||||
&mut size,
|
||||
ptr::null_mut(),
|
||||
None,
|
||||
);
|
||||
if rv != NO_ERR {
|
||||
cubeb_log!(
|
||||
@ -968,7 +973,7 @@ fn audiounit_get_current_channel_layout(output_unit: AudioUnit) -> Vec<mixer::Ch
|
||||
kAudioUnitScope_Output,
|
||||
AU_OUT_BUS,
|
||||
&mut size,
|
||||
ptr::null_mut(),
|
||||
None,
|
||||
);
|
||||
if rv != NO_ERR {
|
||||
cubeb_log!(
|
||||
@ -1226,6 +1231,7 @@ fn set_buffer_size(
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::mutex_atomic)] // The mutex needs to be fed into Condvar::wait_timeout.
|
||||
fn set_buffer_size_sync(unit: AudioUnit, devtype: DeviceType, frames: u32) -> Result<()> {
|
||||
let current_frames = get_buffer_size(unit, devtype).map_err(|e| {
|
||||
cubeb_log!(
|
||||
@ -1392,7 +1398,7 @@ fn get_range_of_sample_rates(
|
||||
) -> std::result::Result<(f64, f64), String> {
|
||||
let result = get_ranges_of_device_sample_rate(devid, devtype);
|
||||
if let Err(e) = result {
|
||||
return Err(format!("status {}", e).to_string());
|
||||
return Err(format!("status {}", e));
|
||||
}
|
||||
let rates = result.unwrap();
|
||||
if rates.is_empty() {
|
||||
@ -1766,9 +1772,7 @@ impl DevicesData {
|
||||
}
|
||||
|
||||
fn is_empty(&self) -> bool {
|
||||
self.changed_callback == None
|
||||
&& self.callback_user_ptr == ptr::null_mut()
|
||||
&& self.devices.is_empty()
|
||||
self.changed_callback == None && self.callback_user_ptr.is_null() && self.devices.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
@ -2138,7 +2142,7 @@ impl ContextOps for AudioUnitContext {
|
||||
cubeb_log!("Fail to create device info for input.");
|
||||
e
|
||||
})?;
|
||||
let stm_params = StreamParams::from(unsafe { (*params.as_ptr()) });
|
||||
let stm_params = StreamParams::from(unsafe { *params.as_ptr() });
|
||||
Some((stm_params, in_device))
|
||||
} else {
|
||||
None
|
||||
@ -2150,7 +2154,7 @@ impl ContextOps for AudioUnitContext {
|
||||
cubeb_log!("Fail to create device info for output.");
|
||||
e
|
||||
})?;
|
||||
let stm_params = StreamParams::from(unsafe { (*params.as_ptr()) });
|
||||
let stm_params = StreamParams::from(unsafe { *params.as_ptr() });
|
||||
Some((stm_params, out_device))
|
||||
} else {
|
||||
None
|
||||
@ -2391,6 +2395,7 @@ impl<'ctx> CoreStreamData<'ctx> {
|
||||
&& !is_device_a_type_of(self.output_device.id, DeviceType::INPUT)
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)] // TODO: Refactoring.
|
||||
fn setup(&mut self) -> Result<()> {
|
||||
if self
|
||||
.input_stream_params
|
||||
@ -2756,14 +2761,14 @@ impl<'ctx> CoreStreamData<'ctx> {
|
||||
};
|
||||
|
||||
let resampler_input_params = if self.has_input() {
|
||||
let mut params = unsafe { (*(self.input_stream_params.as_ptr())) };
|
||||
let mut params = unsafe { *(self.input_stream_params.as_ptr()) };
|
||||
params.rate = self.input_hw_rate as u32;
|
||||
Some(params)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let resampler_output_params = if self.has_output() {
|
||||
let params = unsafe { (*(self.output_stream_params.as_ptr())) };
|
||||
let params = unsafe { *(self.output_stream_params.as_ptr()) };
|
||||
Some(params)
|
||||
} else {
|
||||
None
|
||||
@ -3061,6 +3066,8 @@ impl<'ctx> Drop for CoreStreamData<'ctx> {
|
||||
// #[repr(C)] is used to prevent any padding from being added in the beginning of the AudioUnitStream.
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
// Allow exposing this private struct in public interfaces when running tests.
|
||||
#[cfg_attr(test, allow(private_in_public))]
|
||||
struct AudioUnitStream<'ctx> {
|
||||
context: &'ctx mut AudioUnitContext,
|
||||
user_ptr: *mut c_void,
|
||||
@ -3231,8 +3238,8 @@ impl<'ctx> AudioUnitStream<'ctx> {
|
||||
}
|
||||
}
|
||||
|
||||
if vol_rv.is_ok() {
|
||||
set_volume(self.core_stream_data.output_unit, vol_rv.unwrap());
|
||||
if let Ok(volume) = vol_rv {
|
||||
set_volume(self.core_stream_data.output_unit, volume);
|
||||
}
|
||||
|
||||
// If the stream was running, start it again.
|
||||
@ -3295,6 +3302,29 @@ impl<'ctx> AudioUnitStream<'ctx> {
|
||||
}
|
||||
|
||||
fn destroy(&mut self) {
|
||||
if self
|
||||
.core_stream_data
|
||||
.uninstall_system_changed_callback()
|
||||
.is_err()
|
||||
{
|
||||
cubeb_log!(
|
||||
"({:p}) Could not uninstall the system changed callback",
|
||||
self as *const AudioUnitStream
|
||||
);
|
||||
}
|
||||
|
||||
if self
|
||||
.core_stream_data
|
||||
.uninstall_device_changed_callback()
|
||||
.is_err()
|
||||
{
|
||||
cubeb_log!(
|
||||
"({:p}) Could not uninstall all device change listeners",
|
||||
self as *const AudioUnitStream
|
||||
);
|
||||
}
|
||||
|
||||
// Execute the stream destroy work.
|
||||
self.destroy_pending.store(true, Ordering::SeqCst);
|
||||
|
||||
let queue = self.context.serial_queue;
|
||||
@ -3407,10 +3437,10 @@ impl<'ctx> StreamOps for AudioUnitStream<'ctx> {
|
||||
|
||||
let mut device: Box<ffi::cubeb_device> = Box::new(ffi::cubeb_device::default());
|
||||
|
||||
let input_name = input_name.unwrap_or(CString::default());
|
||||
let input_name = input_name.unwrap_or_default();
|
||||
device.input_name = input_name.into_raw();
|
||||
|
||||
let output_name = output_name.unwrap_or(CString::default());
|
||||
let output_name = output_name.unwrap_or_default();
|
||||
device.output_name = output_name.into_raw();
|
||||
|
||||
Ok(unsafe { DeviceRef::from_ptr(Box::into_raw(device)) })
|
||||
|
@ -730,7 +730,7 @@ fn test_get_preferred_channel_layout_output() {
|
||||
("ispk", STEREO.to_vec()),
|
||||
("FApd", STEREO.to_vec()),
|
||||
]
|
||||
.into_iter()
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
@ -766,7 +766,7 @@ fn test_get_current_channel_layout_output() {
|
||||
("ispk", STEREO.to_vec()),
|
||||
("FApd", STEREO.to_vec()),
|
||||
]
|
||||
.into_iter()
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect();
|
||||
|
||||
|
@ -337,6 +337,7 @@ fn test_register_device_changed_callback_to_check_default_device_changed(stm_typ
|
||||
|
||||
if !run_available {
|
||||
println!("No enough devices to run the test!");
|
||||
return;
|
||||
}
|
||||
|
||||
let changed_count = Arc::new(Mutex::new(0u32));
|
||||
|
@ -503,6 +503,7 @@ pub fn test_audiounit_scope_is_enabled(unit: AudioUnit, scope: Scope) -> bool {
|
||||
Scope::Input => (kAudioUnitScope_Input, AU_IN_BUS),
|
||||
Scope::Output => (kAudioUnitScope_Output, AU_OUT_BUS),
|
||||
};
|
||||
let mut size = mem::size_of::<UInt32>();
|
||||
assert_eq!(
|
||||
audio_unit_get_property(
|
||||
unit,
|
||||
@ -510,7 +511,7 @@ pub fn test_audiounit_scope_is_enabled(unit: AudioUnit, scope: Scope) -> bool {
|
||||
scope,
|
||||
element,
|
||||
&mut has_io,
|
||||
&mut mem::size_of::<UInt32>()
|
||||
&mut size
|
||||
),
|
||||
NO_ERR
|
||||
);
|
||||
|
@ -5,18 +5,14 @@
|
||||
use cubeb_backend::SampleFormat as fmt;
|
||||
use std::mem;
|
||||
|
||||
pub fn allocate_array_by_size<T>(size: usize) -> Vec<T> {
|
||||
pub fn allocate_array_by_size<T: Clone + Default>(size: usize) -> Vec<T> {
|
||||
assert_eq!(size % mem::size_of::<T>(), 0);
|
||||
let elements = size / mem::size_of::<T>();
|
||||
allocate_array::<T>(elements)
|
||||
}
|
||||
|
||||
pub fn allocate_array<T>(elements: usize) -> Vec<T> {
|
||||
let mut array = Vec::<T>::with_capacity(elements);
|
||||
unsafe {
|
||||
array.set_len(elements);
|
||||
}
|
||||
array
|
||||
pub fn allocate_array<T: Clone + Default>(elements: usize) -> Vec<T> {
|
||||
vec![T::default(); elements]
|
||||
}
|
||||
|
||||
pub fn forget_vec<T>(v: Vec<T>) -> (*mut T, usize) {
|
||||
@ -43,7 +39,9 @@ struct Finalizer<F: FnOnce()>(Option<F>);
|
||||
|
||||
impl<F: FnOnce()> Drop for Finalizer<F> {
|
||||
fn drop(&mut self) {
|
||||
self.0.take().map(|f| f());
|
||||
if let Some(f) = self.0.take() {
|
||||
f()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
4
third_party/rust/cubeb-coreaudio/src/capi.rs
vendored
4
third_party/rust/cubeb-coreaudio/src/capi.rs
vendored
@ -7,7 +7,9 @@ use crate::backend::AudioUnitContext;
|
||||
use cubeb_backend::{capi, ffi};
|
||||
use std::os::raw::{c_char, c_int};
|
||||
|
||||
// Entry point from C code.
|
||||
/// # Safety
|
||||
///
|
||||
/// This function should only be called once per process.
|
||||
#[no_mangle]
|
||||
pub unsafe extern "C" fn audiounit_rust_init(
|
||||
c: *mut *mut ffi::cubeb,
|
||||
|
2
third_party/rust/cubeb-coreaudio/src/lib.rs
vendored
2
third_party/rust/cubeb-coreaudio/src/lib.rs
vendored
@ -9,6 +9,8 @@ extern crate bitflags;
|
||||
#[macro_use]
|
||||
extern crate cubeb_backend;
|
||||
#[macro_use]
|
||||
extern crate float_cmp;
|
||||
#[macro_use]
|
||||
extern crate lazy_static;
|
||||
extern crate mach;
|
||||
|
||||
|
1
third_party/rust/float-cmp/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/float-cmp/.cargo-checksum.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"files":{"Cargo.toml":"9dbf5b8451e618eaccd9c9e9360042f8e9aa1db0f05df9300e2b677a9393eff4","LICENSE":"0b4228e50c57acadaef1e9c39eb4494abe91b312088a8d1744ddf1a50310a915","README.md":"3dc6976c44ce23c76f38a9834b0cd3c4bd1d2103a7c3bcf71517b9722c07aae7","src/eq.rs":"44197a5635757405c59b3870fb4741bd2425a16be9a92657bd24adbb0ac7f7df","src/lib.rs":"ef87d7668ffa8bd4f80654871e1108b2e75b176fcd97b66dca0ef6924595cbe4","src/macros.rs":"85f7d267aee869b5a3c97727f6a6b66848ea3adcf7ea1f4ea0e3626264b216d0","src/ratio.rs":"4c097204d91a20346fcf1e44b94cb116bb6ee130517d29635ade58ddea08750e","src/ulps.rs":"bfea6ab54c98c8337c76c9efd50cbd4ba911ebc146c9b6cfa2952fc55740f01b","src/ulps_eq.rs":"28ff2086c0c11ddfbb40c5b4d5aa3f0c3f4d399137ee37c71a24e8e106b866d4"},"package":"da62c4f1b81918835a8c6a484a397775fff5953fe83529afd51b05f5c6a6617d"}
|
37
third_party/rust/float-cmp/Cargo.toml
vendored
Normal file
37
third_party/rust/float-cmp/Cargo.toml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies
|
||||
#
|
||||
# If you believe there's an error in this file please file an
|
||||
# issue against the rust-lang/cargo repository. If you're
|
||||
# editing this file be aware that the upstream Cargo.toml
|
||||
# will likely look very different (and much more reasonable)
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "float-cmp"
|
||||
version = "0.6.0"
|
||||
authors = ["Mike Dilger <mike@mikedilger.com>"]
|
||||
description = "Floating point approximate comparison traits"
|
||||
documentation = "https://docs.rs/float-cmp"
|
||||
readme = "README.md"
|
||||
keywords = ["float", "comparison", "fuzzy", "approximate"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/mikedilger/float-cmp"
|
||||
|
||||
[lib]
|
||||
name = "float_cmp"
|
||||
path = "src/lib.rs"
|
||||
test = true
|
||||
doctest = true
|
||||
doc = true
|
||||
[dependencies.num-traits]
|
||||
version = "0.2"
|
||||
optional = true
|
||||
default-features = false
|
||||
|
||||
[features]
|
||||
default = ["num-traits"]
|
19
third_party/rust/float-cmp/LICENSE
vendored
Normal file
19
third_party/rust/float-cmp/LICENSE
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
Copyright (c) 2014-2018 Optimal Computing (NZ) Ltd
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
||||
of the Software, and to permit persons to whom the Software is furnished to do
|
||||
so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
||||
IN THE SOFTWARE.
|
143
third_party/rust/float-cmp/README.md
vendored
Normal file
143
third_party/rust/float-cmp/README.md
vendored
Normal file
@ -0,0 +1,143 @@
|
||||
# float-cmp
|
||||
|
||||
[![Build Status](https://travis-ci.org/mikedilger/float-cmp.svg?branch=master)](https://travis-ci.org/mikedilger/float-cmp)
|
||||
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
|
||||
|
||||
Documentation is available at https://docs.rs/float-cmp
|
||||
|
||||
float-cmp defines and implements traits for approximate comparison of floating point types
|
||||
which have fallen away from exact equality due to the limited precision available within
|
||||
floating point representations. Implementations of these traits are provided for `f32`
|
||||
and `f64` types.
|
||||
|
||||
When I was a kid in the '80s, the programming rule was "Never compare floating point
|
||||
numbers". If you can follow that rule and still get the outcome you desire, then more
|
||||
power to you. However, if you really do need to compare them, this crate provides a
|
||||
reasonable way to do so.
|
||||
|
||||
Another crate `efloat` offers another solution by providing a floating point type that
|
||||
tracks its error bounds as operations are performed on it, and thus can implement the
|
||||
`ApproxEq` trait in this crate more accurately, without specifying a `Margin`.
|
||||
|
||||
The recommended go-to solution (although it may not be appropriate in all cases) is the
|
||||
`approx_eq()` function in the `ApproxEq` trait (or better yet, the macros). For `f32`
|
||||
and `f64`, the `F32Margin` and `F64Margin` types are provided for specifying margins as
|
||||
both an epsilon value and an ULPs value, and defaults are provided via `Default`
|
||||
(although there is no perfect default value that is always appropriate, so beware).
|
||||
|
||||
Several other traits are provided including `Ulps`, `ApproxEqUlps`, `ApproxOrdUlps`, and
|
||||
`ApproxEqRatio`.
|
||||
|
||||
## The problem
|
||||
|
||||
Floating point operations must round answers to the nearest representable number. Multiple
|
||||
operations may result in an answer different from what you expect. In the following example,
|
||||
the assert will fail, even though the printed output says "0.45 == 0.45":
|
||||
|
||||
```rust
|
||||
let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
println!("{} == {}", a, b);
|
||||
assert!(a==b) // Fails, because they are not exactly equal
|
||||
```
|
||||
|
||||
This fails because the correct answer to most operations isn't exactly representable, and so
|
||||
your computer's processor chooses to represent the answer with the closest value it has
|
||||
available. This introduces error, and this error can accumulate as multiple operations are
|
||||
performed.
|
||||
|
||||
## The solution
|
||||
|
||||
With `ApproxEq`, we can get the answer we intend:
|
||||
```rust
|
||||
let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
println!("{} == {}", a, b);
|
||||
assert!( approx_eq!(f32, a, b, ulps = 2) );
|
||||
```
|
||||
|
||||
## Some explanation
|
||||
|
||||
We use the term ULP (units of least precision, or units in the last place) to mean the
|
||||
difference between two adjacent floating point representations (adjacent meaning that there is
|
||||
no floating point number between them). This term is borrowed from prior work (personally I
|
||||
would have chosen "quanta"). The size of an ULP (measured as a float) varies
|
||||
depending on the exponents of the floating point numbers in question. That is a good thing,
|
||||
because as numbers fall away from equality due to the imprecise nature of their representation,
|
||||
they fall away in ULPs terms, not in absolute terms. Pure epsilon-based comparisons are
|
||||
absolute and thus don't map well to the nature of the additive error issue. They work fine
|
||||
for many ranges of numbers, but not for others (consider comparing -0.0000000028
|
||||
to +0.00000097).
|
||||
|
||||
## Using this crate
|
||||
|
||||
You can use the `ApproxEq` trait directly like so:
|
||||
|
||||
```rust
|
||||
assert!( a.approx_eq(b, F32Margin { ulps: 2, epsilon: 0.0 }) );
|
||||
```
|
||||
|
||||
We have implemented `From<(f32,i32)>` for `F32Margin` (and similarly for `F64Margin`)
|
||||
so you can use this shorthand:
|
||||
|
||||
```rust
|
||||
assert!( a.approx_eq(b, (0.0, 2)) );
|
||||
```
|
||||
|
||||
With macros, it is easier to be explicit about which type of margin you wish to set,
|
||||
without mentioning the other one (the other one will be zero). But the downside is
|
||||
that you have to specify the type you are dealing with:
|
||||
|
||||
```rust
|
||||
assert!( approx_eq!(f32, a, b, ulps = 2) );
|
||||
assert!( approx_eq!(f32, a, b, epsilon = 0.00000003) );
|
||||
assert!( approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2) );
|
||||
assert!( approx_eq!(f32, a, b, (0.0, 2)) );
|
||||
assert!( approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 }) );
|
||||
assert!( approx_eq!(f32, a, b, F32Margin::default()) );
|
||||
assert!( approx_eq!(f32, a, b) ); // uses the default
|
||||
```
|
||||
|
||||
For most cases, I recommend you use a smallish integer for the `ulps` parameter (1 to 5
|
||||
or so), and a similar small multiple of the floating point's EPSILON constant (1.0 to 5.0
|
||||
or so), but there are *plenty* of cases where this is insufficient.
|
||||
|
||||
## Implementing these traits
|
||||
|
||||
You can implement `ApproxEq` for your own complex types like shown below.
|
||||
The floating point type `F` must be `Copy`, but for large types you can implement
|
||||
it for references to your type as shown.
|
||||
|
||||
```rust
|
||||
use float_cmp::ApproxEq;
|
||||
|
||||
pub struct Vec2<F> {
|
||||
pub x: F,
|
||||
pub y: F,
|
||||
}
|
||||
|
||||
impl<'a, M: Copy, F: Copy + ApproxEq<Margin=M>> ApproxEq for &'a Vec2<F> {
|
||||
type Margin = M;
|
||||
|
||||
fn approx_eq<T: Into<Self::Margin>>(self, other: Self, margin: T) -> bool {
|
||||
let margin = margin.into();
|
||||
self.x.approx_eq(other.x, margin)
|
||||
&& self.y.approx_eq(other.y, margin)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Non floating-point types
|
||||
|
||||
`ApproxEq` can be implemented for non floating-point types as well, since `Margin` is
|
||||
an associated type.
|
||||
|
||||
The `efloat` crate implements (or soon will implement) `ApproxEq` for a compound type
|
||||
that tracks floating point error bounds by checking if the error bounds overlap.
|
||||
In that case `type Margin = ()`.
|
||||
|
||||
## Inspiration
|
||||
|
||||
This crate was inspired by this Random ASCII blog post:
|
||||
|
||||
[https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/)
|
284
third_party/rust/float-cmp/src/eq.rs
vendored
Normal file
284
third_party/rust/float-cmp/src/eq.rs
vendored
Normal file
@ -0,0 +1,284 @@
|
||||
// Copyright 2014-2018 Optimal Computing (NZ) Ltd.
|
||||
// Licensed under the MIT license. See LICENSE for details.
|
||||
|
||||
use std::{f32,f64};
|
||||
use super::Ulps;
|
||||
|
||||
/// ApproxEq is a trait for approximate equality comparisons.
|
||||
pub trait ApproxEq: Sized {
|
||||
/// The Margin type defines a margin within which two values are to be
|
||||
/// considered approximately equal. It must implement Default so that
|
||||
/// approx_eq() can be called on unknown types.
|
||||
type Margin: Copy + Default;
|
||||
|
||||
/// This method tests for `self` and `other` values to be approximately equal
|
||||
/// within `margin`.
|
||||
fn approx_eq<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool;
|
||||
|
||||
/// This method tests for `self` and `other` values to be not approximately
|
||||
/// equal within `margin`.
|
||||
fn approx_ne<M: Into<Self::Margin>>(self, other: Self, margin: M) -> bool {
|
||||
!self.approx_eq(other, margin)
|
||||
}
|
||||
}
|
||||
|
||||
/// This type defines a margin within two f32s might be considered equal
|
||||
/// and is intended as the associated type for the `ApproxEq` trait.
|
||||
///
|
||||
/// Two methods are used to determine approximate equality.
|
||||
///
|
||||
/// First an epsilon method is used, considering them approximately equal if they
|
||||
/// differ by <= `epsilon`. This will only succeed for very small numbers.
|
||||
/// Note that it may succeed even if the parameters are of differing signs straddling
|
||||
/// zero.
|
||||
///
|
||||
/// The second method considers how many ULPs (units of least precision, units in
|
||||
/// the last place, which is the integer number of floating point representations
|
||||
/// that the parameters are separated by) different the parameters are and considers
|
||||
/// them approximately equal if this is <= `ulps`. For large floating point numbers,
|
||||
/// an ULP can be a rather large gap, but this kind of comparison is necessary
|
||||
/// because floating point operations must round to the nearest representable value
|
||||
/// and so larger floating point values accumulate larger errors.
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct F32Margin {
|
||||
pub epsilon: f32,
|
||||
pub ulps: i32
|
||||
}
|
||||
impl Default for F32Margin {
|
||||
#[inline]
|
||||
fn default() -> F32Margin {
|
||||
F32Margin {
|
||||
epsilon: f32::EPSILON,
|
||||
ulps: 4
|
||||
}
|
||||
}
|
||||
}
|
||||
impl F32Margin {
|
||||
#[inline]
|
||||
pub fn zero() -> F32Margin {
|
||||
F32Margin {
|
||||
epsilon: 0.0,
|
||||
ulps: 0
|
||||
}
|
||||
}
|
||||
pub fn epsilon(self, epsilon: f32) -> Self {
|
||||
F32Margin {
|
||||
epsilon: epsilon,
|
||||
..self
|
||||
}
|
||||
}
|
||||
pub fn ulps(self, ulps: i32) -> Self {
|
||||
F32Margin {
|
||||
ulps: ulps,
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<(f32, i32)> for F32Margin {
|
||||
fn from(m: (f32, i32)) -> F32Margin {
|
||||
F32Margin {
|
||||
epsilon: m.0,
|
||||
ulps: m.1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApproxEq for f32 {
|
||||
type Margin = F32Margin;
|
||||
|
||||
fn approx_eq<M: Into<Self::Margin>>(self, other: f32, margin: M) -> bool {
|
||||
let margin = margin.into();
|
||||
|
||||
// Check for exact equality first. This is often true, and so we get the
|
||||
// performance benefit of only doing one compare in most cases.
|
||||
self==other ||
|
||||
|
||||
// Perform epsilon comparison next
|
||||
((self - other).abs() <= margin.epsilon) ||
|
||||
|
||||
{
|
||||
// Perform ulps comparion last
|
||||
let diff: i32 = self.ulps(&other);
|
||||
saturating_abs_i32!(diff) <= margin.ulps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_test1() {
|
||||
let f: f32 = 0.0_f32;
|
||||
let g: f32 = -0.0000000000000005551115123125783_f32;
|
||||
assert!(f != g); // Should not be directly equal
|
||||
assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_test2() {
|
||||
let f: f32 = 0.0_f32;
|
||||
let g: f32 = -0.0_f32;
|
||||
assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_test3() {
|
||||
let f: f32 = 0.0_f32;
|
||||
let g: f32 = 0.00000000000000001_f32;
|
||||
assert!(f.approx_eq(g, (f32::EPSILON, 0)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_test4() {
|
||||
let f: f32 = 0.00001_f32;
|
||||
let g: f32 = 0.00000000000000001_f32;
|
||||
assert!(f.approx_eq(g, (f32::EPSILON, 0)) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_test5() {
|
||||
let f: f32 = 0.1_f32;
|
||||
let mut sum: f32 = 0.0_f32;
|
||||
for _ in 0_isize..10_isize { sum += f; }
|
||||
let product: f32 = f * 10.0_f32;
|
||||
assert!(sum != product); // Should not be directly equal:
|
||||
println!("Ulps Difference: {}",sum.ulps(&product));
|
||||
assert!(sum.approx_eq(product, (f32::EPSILON, 1)) == true);
|
||||
assert!(sum.approx_eq(product, F32Margin::zero()) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_test6() {
|
||||
let x: f32 = 1000000_f32;
|
||||
let y: f32 = 1000000.1_f32;
|
||||
assert!(x != y); // Should not be directly equal
|
||||
assert!(x.approx_eq(y, (0.0, 2)) == true); // 2 ulps does it
|
||||
// epsilon method no good here:
|
||||
assert!(x.approx_eq(y, (1000.0 * f32::EPSILON, 0)) == false);
|
||||
}
|
||||
|
||||
/// This type defines a margin within two f32s might be considered equal
|
||||
/// and is intended as the associated type for the `ApproxEq` trait.
|
||||
///
|
||||
/// Two methods are used to determine approximate equality.
|
||||
///
|
||||
/// First an epsilon method is used, considering them approximately equal if they
|
||||
/// differ by <= `epsilon`. This will only succeed for very small numbers.
|
||||
/// Note that it may succeed even if the parameters are of differing signs straddling
|
||||
/// zero.
|
||||
///
|
||||
/// The second method considers how many ULPs (units of least precision, units in
|
||||
/// the last place, which is the integer number of floating point representations
|
||||
/// that the parameters are separated by) different the parameters are and considers
|
||||
/// them approximately equal if this <= `ulps`. For large floating point numbers,
|
||||
/// an ULP can be a rather large gap, but this kind of comparison is necessary
|
||||
/// because floating point operations must round to the nearest representable value
|
||||
/// and so larger floating point values accumulate larger errors.
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
pub struct F64Margin {
|
||||
pub epsilon: f64,
|
||||
pub ulps: i64
|
||||
}
|
||||
impl Default for F64Margin {
|
||||
#[inline]
|
||||
fn default() -> F64Margin {
|
||||
F64Margin {
|
||||
epsilon: f64::EPSILON,
|
||||
ulps: 4
|
||||
}
|
||||
}
|
||||
}
|
||||
impl F64Margin {
|
||||
#[inline]
|
||||
pub fn zero() -> F64Margin {
|
||||
F64Margin {
|
||||
epsilon: 0.0,
|
||||
ulps: 0
|
||||
}
|
||||
}
|
||||
pub fn epsilon(self, epsilon: f64) -> Self {
|
||||
F64Margin {
|
||||
epsilon: epsilon,
|
||||
..self
|
||||
}
|
||||
}
|
||||
pub fn ulps(self, ulps: i64) -> Self {
|
||||
F64Margin {
|
||||
ulps: ulps,
|
||||
..self
|
||||
}
|
||||
}
|
||||
}
|
||||
impl From<(f64, i64)> for F64Margin {
|
||||
fn from(m: (f64, i64)) -> F64Margin {
|
||||
F64Margin {
|
||||
epsilon: m.0,
|
||||
ulps: m.1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ApproxEq for f64 {
|
||||
type Margin = F64Margin;
|
||||
|
||||
fn approx_eq<M: Into<Self::Margin>>(self, other: f64, margin: M) -> bool {
|
||||
let margin = margin.into();
|
||||
|
||||
// Check for exact equality first. This is often true, and so we get the
|
||||
// performance benefit of only doing one compare in most cases.
|
||||
self==other ||
|
||||
|
||||
// Perform epsilon comparison next
|
||||
((self - other).abs() <= margin.epsilon) ||
|
||||
|
||||
{
|
||||
// Perform ulps comparion last
|
||||
let diff: i64 = self.ulps(&other);
|
||||
saturating_abs_i64!(diff) <= margin.ulps
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_test1() {
|
||||
let f: f64 = 0.0_f64;
|
||||
let g: f64 = -0.0000000000000005551115123125783_f64;
|
||||
assert!(f != g); // Should not be directly equal
|
||||
assert!(f.approx_eq(g, (3.0 * f64::EPSILON, 0)) == true); // 3e is enough
|
||||
// ulps test wont ever call these equal
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_test2() {
|
||||
let f: f64 = 0.0_f64;
|
||||
let g: f64 = -0.0_f64;
|
||||
assert!(f.approx_eq(g, (f64::EPSILON, 0)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_test3() {
|
||||
let f: f64 = 0.0_f64;
|
||||
let g: f64 = 1e-17_f64;
|
||||
assert!(f.approx_eq(g, (f64::EPSILON, 0)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_test4() {
|
||||
let f: f64 = 0.00001_f64;
|
||||
let g: f64 = 0.00000000000000001_f64;
|
||||
assert!(f.approx_eq(g, (f64::EPSILON, 0)) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_test5() {
|
||||
let f: f64 = 0.1_f64;
|
||||
let mut sum: f64 = 0.0_f64;
|
||||
for _ in 0_isize..10_isize { sum += f; }
|
||||
let product: f64 = f * 10.0_f64;
|
||||
assert!(sum != product); // Should not be directly equal:
|
||||
println!("Ulps Difference: {}",sum.ulps(&product));
|
||||
assert!(sum.approx_eq(product, (f64::EPSILON, 0)) == true);
|
||||
assert!(sum.approx_eq(product, (0.0, 1)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_test6() {
|
||||
let x: f64 = 1000000_f64;
|
||||
let y: f64 = 1000000.0000000003_f64;
|
||||
assert!(x != y); // Should not be directly equal
|
||||
println!("Ulps Difference: {}",x.ulps(&y));
|
||||
assert!(x.approx_eq(y, (0.0, 3)) == true);
|
||||
}
|
||||
#[test]
|
||||
fn f64_code_triggering_issue_20() {
|
||||
assert_eq!((-25.0f64).approx_eq(25.0, (0.00390625, 1)), false);
|
||||
}
|
196
third_party/rust/float-cmp/src/lib.rs
vendored
Normal file
196
third_party/rust/float-cmp/src/lib.rs
vendored
Normal file
@ -0,0 +1,196 @@
|
||||
// Copyright 2014-2018 Optimal Computing (NZ) Ltd.
|
||||
// Licensed under the MIT license. See LICENSE for details.
|
||||
|
||||
//! # float-cmp
|
||||
//!
|
||||
//! float-cmp defines and implements traits for approximate comparison of floating point types
|
||||
//! which have fallen away from exact equality due to the limited precision available within
|
||||
//! floating point representations. Implementations of these traits are provided for `f32`
|
||||
//! and `f64` types.
|
||||
//!
|
||||
//! When I was a kid in the '80s, the programming rule was "Never compare floating point
|
||||
//! numbers". If you can follow that rule and still get the outcome you desire, then more
|
||||
//! power to you. However, if you really do need to compare them, this crate provides a
|
||||
//! reasonable way to do so.
|
||||
//!
|
||||
//! Another crate `efloat` offers another solution by providing a floating point type that
|
||||
//! tracks its error bounds as operations are performed on it, and thus can implement the
|
||||
//! `ApproxEq` trait in this crate more accurately, without specifying a `Margin`.
|
||||
//!
|
||||
//! The recommended go-to solution (although it may not be appropriate in all cases) is the
|
||||
//! `approx_eq()` function in the `ApproxEq` trait (or better yet, the macros). For `f32`
|
||||
//! and `f64`, the `F32Margin` and `F64Margin` types are provided for specifying margins as
|
||||
//! both an epsilon value and an ULPs value, and defaults are provided via `Default`
|
||||
//! (although there is no perfect default value that is always appropriate, so beware).
|
||||
//!
|
||||
//! Several other traits are provided including `Ulps`, `ApproxEqUlps`, `ApproxOrdUlps`, and
|
||||
//! `ApproxEqRatio`.
|
||||
//!
|
||||
//! ## The problem
|
||||
//!
|
||||
//! Floating point operations must round answers to the nearest representable number. Multiple
|
||||
//! operations may result in an answer different from what you expect. In the following example,
|
||||
//! the assert will fail, even though the printed output says "0.45 == 0.45":
|
||||
//!
|
||||
//! ```should_panic
|
||||
//! # extern crate float_cmp;
|
||||
//! # use float_cmp::ApproxEq;
|
||||
//! # fn main() {
|
||||
//! let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
//! let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
//! println!("{} == {}", a, b);
|
||||
//! assert!(a==b) // Fails, because they are not exactly equal
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! This fails because the correct answer to most operations isn't exactly representable, and so
|
||||
//! your computer's processor chooses to represent the answer with the closest value it has
|
||||
//! available. This introduces error, and this error can accumulate as multiple operations are
|
||||
//! performed.
|
||||
//!
|
||||
//! ## The solution
|
||||
//!
|
||||
//! With `ApproxEq`, we can get the answer we intend:
|
||||
//!
|
||||
//! ```
|
||||
//! # #[macro_use]
|
||||
//! # extern crate float_cmp;
|
||||
//! # use float_cmp::{ApproxEq, F32Margin};
|
||||
//! # fn main() {
|
||||
//! let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
//! let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
//! println!("{} == {}", a, b);
|
||||
//! // They are equal, within 2 ulps
|
||||
//! assert!( approx_eq!(f32, a, b, ulps = 2) );
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Some explanation
|
||||
//!
|
||||
//! We use the term ULP (units of least precision, or units in the last place) to mean the
|
||||
//! difference between two adjacent floating point representations (adjacent meaning that there is
|
||||
//! no floating point number between them). This term is borrowed from prior work (personally I
|
||||
//! would have chosen "quanta"). The size of an ULP (measured as a float) varies
|
||||
//! depending on the exponents of the floating point numbers in question. That is a good thing,
|
||||
//! because as numbers fall away from equality due to the imprecise nature of their representation,
|
||||
//! they fall away in ULPs terms, not in absolute terms. Pure epsilon-based comparisons are
|
||||
//! absolute and thus don't map well to the nature of the additive error issue. They work fine
|
||||
//! for many ranges of numbers, but not for others (consider comparing -0.0000000028
|
||||
//! to +0.00000097).
|
||||
//!
|
||||
//! ## Using this crate
|
||||
//!
|
||||
//! You can use the `ApproxEq` trait directly like so:
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate float_cmp;
|
||||
//! # use float_cmp::{ApproxEq, F32Margin};
|
||||
//! # fn main() {
|
||||
//! # let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
//! # let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
//! assert!( a.approx_eq(b, F32Margin { ulps: 2, epsilon: 0.0 }) );
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! We have implemented `From<(f32,i32)>` for `F32Margin` (and similarly for `F64Margin`)
|
||||
//! so you can use this shorthand:
|
||||
//!
|
||||
//! ```
|
||||
//! # extern crate float_cmp;
|
||||
//! # use float_cmp::{ApproxEq, F32Margin};
|
||||
//! # fn main() {
|
||||
//! # let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
//! # let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
//! assert!( a.approx_eq(b, (0.0, 2)) );
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! With macros, it is easier to be explicit about which type of margin you wish to set,
|
||||
//! without mentioning the other one (the other one will be zero). But the downside is
|
||||
//! that you have to specify the type you are dealing with:
|
||||
//!
|
||||
//! ```
|
||||
//! # #[macro_use]
|
||||
//! # extern crate float_cmp;
|
||||
//! # use float_cmp::{ApproxEq, F32Margin};
|
||||
//! # fn main() {
|
||||
//! # let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
//! # let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
//! assert!( approx_eq!(f32, a, b, ulps = 2) );
|
||||
//! assert!( approx_eq!(f32, a, b, epsilon = 0.00000003) );
|
||||
//! assert!( approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2) );
|
||||
//! assert!( approx_eq!(f32, a, b, (0.0, 2)) );
|
||||
//! assert!( approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 }) );
|
||||
//! assert!( approx_eq!(f32, a, b, F32Margin::default()) );
|
||||
//! assert!( approx_eq!(f32, a, b) ); // uses the default
|
||||
//! # }
|
||||
//! ```
|
||||
//!
|
||||
//! For most cases, I recommend you use a smallish integer for the `ulps` parameter (1 to 5
|
||||
//! or so), and a similar small multiple of the floating point's EPSILON constant (1.0 to 5.0
|
||||
//! or so), but there are *plenty* of cases where this is insufficient.
|
||||
//!
|
||||
//! ## Implementing these traits
|
||||
//!
|
||||
//! You can implement `ApproxEq` for your own complex types like shown below.
|
||||
//! The floating point type `F` must be `Copy`, but for large types you can implement
|
||||
//! it for references to your type as shown.
|
||||
//!
|
||||
//! ```
|
||||
//! use float_cmp::ApproxEq;
|
||||
//!
|
||||
//! pub struct Vec2<F> {
|
||||
//! pub x: F,
|
||||
//! pub y: F,
|
||||
//! }
|
||||
//!
|
||||
//! impl<'a, M: Copy + Default, F: Copy + ApproxEq<Margin=M>> ApproxEq for &'a Vec2<F> {
|
||||
//! type Margin = M;
|
||||
//!
|
||||
//! fn approx_eq<T: Into<Self::Margin>>(self, other: Self, margin: T) -> bool {
|
||||
//! let margin = margin.into();
|
||||
//! self.x.approx_eq(other.x, margin)
|
||||
//! && self.y.approx_eq(other.y, margin)
|
||||
//! }
|
||||
//! }
|
||||
//! ```
|
||||
//!
|
||||
//! ## Non floating-point types
|
||||
//!
|
||||
//! `ApproxEq` can be implemented for non floating-point types as well, since `Margin` is
|
||||
//! an associated type.
|
||||
//!
|
||||
//! The `efloat` crate implements (or soon will implement) `ApproxEq` for a compound type
|
||||
//! that tracks floating point error bounds by checking if the error bounds overlap.
|
||||
//! In that case `type Margin = ()`.
|
||||
//!
|
||||
//! ## Inspiration
|
||||
//!
|
||||
//! This crate was inspired by this Random ASCII blog post:
|
||||
//!
|
||||
//! [https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/](https://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/)
|
||||
|
||||
#[cfg(feature="num-traits")]
|
||||
extern crate num_traits;
|
||||
|
||||
#[macro_use]
|
||||
mod macros;
|
||||
|
||||
pub fn trials() {
|
||||
println!("are they approximately equal?: {:?}",
|
||||
approx_eq!(f32, 1.0, 1.0000001));
|
||||
}
|
||||
|
||||
mod ulps;
|
||||
pub use self::ulps::Ulps;
|
||||
|
||||
mod ulps_eq;
|
||||
pub use self::ulps_eq::ApproxEqUlps;
|
||||
|
||||
mod eq;
|
||||
pub use self::eq::{ApproxEq, F32Margin, F64Margin};
|
||||
|
||||
#[cfg(feature="num-traits")]
|
||||
mod ratio;
|
||||
#[cfg(feature="num-traits")]
|
||||
pub use self::ratio::ApproxEqRatio;
|
77
third_party/rust/float-cmp/src/macros.rs
vendored
Normal file
77
third_party/rust/float-cmp/src/macros.rs
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! approx_eq {
|
||||
($typ:ty, $lhs:expr, $rhs:expr) => {
|
||||
{
|
||||
let m: <$typ as $crate::ApproxEq>::Margin = Default::default();
|
||||
<$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, m)
|
||||
}
|
||||
};
|
||||
($typ:ty, $lhs:expr, $rhs:expr $(, $set:ident = $val:expr)*) => {
|
||||
{
|
||||
let m = <$typ as $crate::ApproxEq>::Margin::zero()$(.$set($val))*;
|
||||
<$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, m)
|
||||
}
|
||||
};
|
||||
($typ:ty, $lhs:expr, $rhs:expr, $marg:expr) => {
|
||||
{
|
||||
<$typ as $crate::ApproxEq>::approx_eq($lhs, $rhs, $marg)
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Until saturating_abs() comes out of nightly, we have to code it ourselves.
|
||||
macro_rules! saturating_abs_i32 {
|
||||
($val:expr) => {
|
||||
if $val.is_negative() {
|
||||
match $val.checked_neg() {
|
||||
Some(v) => v,
|
||||
None => std::i32::MAX
|
||||
}
|
||||
} else {
|
||||
$val
|
||||
}
|
||||
};
|
||||
}
|
||||
macro_rules! saturating_abs_i64 {
|
||||
($val:expr) => {
|
||||
if $val.is_negative() {
|
||||
match $val.checked_neg() {
|
||||
Some(v) => v,
|
||||
None => std::i64::MAX
|
||||
}
|
||||
} else {
|
||||
$val
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro() {
|
||||
let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
assert!( approx_eq!(f32, a, b) ); // uses the default
|
||||
assert!( approx_eq!(f32, a, b, ulps = 2) );
|
||||
assert!( approx_eq!(f32, a, b, epsilon = 0.00000003) );
|
||||
assert!( approx_eq!(f32, a, b, epsilon = 0.00000003, ulps = 2) );
|
||||
assert!( approx_eq!(f32, a, b, (0.0, 2)) );
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_2() {
|
||||
assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64) );
|
||||
assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, ulps=3) );
|
||||
assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, epsilon=0.0000000004) );
|
||||
assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0000000004, 0)) );
|
||||
assert!( approx_eq!(f64, 1000000_f64, 1000000.0000000003_f64, (0.0, 3)) );
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_macro_3() {
|
||||
use crate::F32Margin;
|
||||
|
||||
let a: f32 = 0.15 + 0.15 + 0.15;
|
||||
let b: f32 = 0.1 + 0.1 + 0.25;
|
||||
assert!( approx_eq!(f32, a, b, F32Margin { epsilon: 0.0, ulps: 2 }) );
|
||||
assert!( approx_eq!(f32, a, b, F32Margin::default()) );
|
||||
}
|
142
third_party/rust/float-cmp/src/ratio.rs
vendored
Normal file
142
third_party/rust/float-cmp/src/ratio.rs
vendored
Normal file
@ -0,0 +1,142 @@
|
||||
// Copyright 2014-2018 Optimal Computing (NZ) Ltd.
|
||||
// Licensed under the MIT license. See LICENSE for details.
|
||||
|
||||
use std::cmp::PartialOrd;
|
||||
use std::ops::{Sub,Div,Neg};
|
||||
use num_traits::Zero;
|
||||
|
||||
/// ApproxEqRatio is a trait for approximate equality comparisons bounding the ratio
|
||||
/// of the difference to the larger.
|
||||
pub trait ApproxEqRatio : Div<Output = Self> + Sub<Output = Self> + Neg<Output = Self>
|
||||
+ PartialOrd + Zero + Sized + Copy
|
||||
{
|
||||
/// This method tests if `self` and `other` are nearly equal by bounding the
|
||||
/// difference between them to some number much less than the larger of the two.
|
||||
/// This bound is set as the ratio of the difference to the larger.
|
||||
fn approx_eq_ratio(&self, other: &Self, ratio: Self) -> bool {
|
||||
|
||||
// Not equal if signs are not equal
|
||||
if *self < Self::zero() && *other > Self::zero() { return false; }
|
||||
if *self > Self::zero() && *other < Self::zero() { return false; }
|
||||
|
||||
// Handle all zero cases
|
||||
match (*self == Self::zero(), *other == Self::zero()) {
|
||||
(true,true) => return true,
|
||||
(true,false) => return false,
|
||||
(false,true) => return false,
|
||||
_ => { }
|
||||
}
|
||||
|
||||
// abs
|
||||
let (s,o) = if *self < Self::zero() {
|
||||
(-*self, -*other)
|
||||
} else {
|
||||
(*self, *other)
|
||||
};
|
||||
|
||||
let (smaller,larger) = if s < o {
|
||||
(s,o)
|
||||
} else {
|
||||
(o,s)
|
||||
};
|
||||
let difference: Self = larger.sub(smaller);
|
||||
let actual_ratio: Self = difference.div(larger);
|
||||
actual_ratio < ratio
|
||||
}
|
||||
|
||||
/// This method tests if `self` and `other` are not nearly equal by bounding the
|
||||
/// difference between them to some number much less than the larger of the two.
|
||||
/// This bound is set as the ratio of the difference to the larger.
|
||||
#[inline]
|
||||
fn approx_ne_ratio(&self, other: &Self, ratio: Self) -> bool {
|
||||
!self.approx_eq_ratio(other, ratio)
|
||||
}
|
||||
}
|
||||
|
||||
impl ApproxEqRatio for f32 { }
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ratio_test1() {
|
||||
let x: f32 = 0.00004_f32;
|
||||
let y: f32 = 0.00004001_f32;
|
||||
assert!(x.approx_eq_ratio(&y, 0.00025));
|
||||
assert!(y.approx_eq_ratio(&x, 0.00025));
|
||||
assert!(x.approx_ne_ratio(&y, 0.00024));
|
||||
assert!(y.approx_ne_ratio(&x, 0.00024));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ratio_test2() {
|
||||
let x: f32 = 0.00000000001_f32;
|
||||
let y: f32 = 0.00000000005_f32;
|
||||
assert!(x.approx_eq_ratio(&y, 0.81));
|
||||
assert!(y.approx_ne_ratio(&x, 0.79));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ratio_test_zero_eq_zero_returns_true() {
|
||||
let x: f32 = 0.0_f32;
|
||||
assert!(x.approx_eq_ratio(&x,0.1) == true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ratio_test_zero_ne_zero_returns_false() {
|
||||
let x: f32 = 0.0_f32;
|
||||
assert!(x.approx_ne_ratio(&x,0.1) == false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ratio_test_against_a_zero_is_false() {
|
||||
let x: f32 = 0.0_f32;
|
||||
let y: f32 = 0.1_f32;
|
||||
assert!(x.approx_eq_ratio(&y,0.1) == false);
|
||||
assert!(y.approx_eq_ratio(&x,0.1) == false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ratio_test_negative_numbers() {
|
||||
let x: f32 = -3.0_f32;
|
||||
let y: f32 = -4.0_f32;
|
||||
// -3 and -4 should not be equal at a ratio of 0.1
|
||||
assert!(x.approx_eq_ratio(&y,0.1) == false);
|
||||
}
|
||||
|
||||
impl ApproxEqRatio for f64 { }
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_ratio_test1() {
|
||||
let x: f64 = 0.000000004_f64;
|
||||
let y: f64 = 0.000000004001_f64;
|
||||
assert!(x.approx_eq_ratio(&y, 0.00025));
|
||||
assert!(y.approx_eq_ratio(&x, 0.00025));
|
||||
assert!(x.approx_ne_ratio(&y, 0.00024));
|
||||
assert!(y.approx_ne_ratio(&x, 0.00024));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_ratio_test2() {
|
||||
let x: f64 = 0.0000000000000001_f64;
|
||||
let y: f64 = 0.0000000000000005_f64;
|
||||
assert!(x.approx_eq_ratio(&y, 0.81));
|
||||
assert!(y.approx_ne_ratio(&x, 0.79));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_ratio_test_zero_eq_zero_returns_true() {
|
||||
let x: f64 = 0.0_f64;
|
||||
assert!(x.approx_eq_ratio(&x,0.1) == true);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_ratio_test_zero_ne_zero_returns_false() {
|
||||
let x: f64 = 0.0_f64;
|
||||
assert!(x.approx_ne_ratio(&x,0.1) == false);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_ratio_test_negative_numbers() {
|
||||
let x: f64 = -3.0_f64;
|
||||
let y: f64 = -4.0_f64;
|
||||
// -3 and -4 should not be equal at a ratio of 0.1
|
||||
assert!(x.approx_eq_ratio(&y,0.1) == false);
|
||||
}
|
240
third_party/rust/float-cmp/src/ulps.rs
vendored
Normal file
240
third_party/rust/float-cmp/src/ulps.rs
vendored
Normal file
@ -0,0 +1,240 @@
|
||||
// Copyright 2014-2018 Optimal Computing (NZ) Ltd.
|
||||
// Licensed under the MIT license. See LICENSE for details.
|
||||
|
||||
#[cfg(feature="num_traits")]
|
||||
use num_traits::NumCast;
|
||||
|
||||
/// A trait for floating point numbers which computes the number of representable
|
||||
/// values or ULPs (Units of Least Precision) that separate the two given values.
|
||||
#[cfg(feature="num_traits")]
|
||||
pub trait Ulps {
|
||||
type U: Copy + NumCast;
|
||||
|
||||
/// The number of representable values or ULPs (Units of Least Precision) that
|
||||
/// separate `self` and `other`. The result `U` is an integral value, and will
|
||||
/// be zero if `self` and `other` are exactly equal.
|
||||
fn ulps(&self, other: &Self) -> <Self as Ulps>::U;
|
||||
|
||||
/// The next representable number above this one
|
||||
fn next(&self) -> Self;
|
||||
|
||||
/// The previous representable number below this one
|
||||
fn prev(&self) -> Self;
|
||||
}
|
||||
|
||||
#[cfg(not(feature="num_traits"))]
|
||||
pub trait Ulps {
|
||||
type U: Copy;
|
||||
|
||||
/// The number of representable values or ULPs (Units of Least Precision) that
|
||||
/// separate `self` and `other`. The result `U` is an integral value, and will
|
||||
/// be zero if `self` and `other` are exactly equal.
|
||||
fn ulps(&self, other: &Self) -> <Self as Ulps>::U;
|
||||
|
||||
/// The next representable number above this one
|
||||
fn next(&self) -> Self;
|
||||
|
||||
/// The previous representable number below this one
|
||||
fn prev(&self) -> Self;
|
||||
}
|
||||
|
||||
impl Ulps for f32 {
|
||||
type U = i32;
|
||||
|
||||
fn ulps(&self, other: &f32) -> i32 {
|
||||
|
||||
// IEEE754 defined floating point storage representation to
|
||||
// maintain their order when their bit patterns are interpreted as
|
||||
// integers. This is a huge boon to the task at hand, as we can
|
||||
// reinterpret them as integers to find out how many ULPs apart any
|
||||
// two floats are
|
||||
|
||||
// Setup integer representations of the input
|
||||
let ai32: i32 = self.to_bits() as i32;
|
||||
let bi32: i32 = other.to_bits() as i32;
|
||||
|
||||
ai32.wrapping_sub(bi32)
|
||||
}
|
||||
|
||||
fn next(&self) -> Self {
|
||||
if self.is_infinite() && *self > 0.0 {
|
||||
*self
|
||||
} else if *self == -0.0 && self.is_sign_negative() {
|
||||
0.0
|
||||
} else {
|
||||
let mut u = self.to_bits();
|
||||
if *self >= 0.0 {
|
||||
u += 1;
|
||||
} else {
|
||||
u -= 1;
|
||||
}
|
||||
f32::from_bits(u)
|
||||
}
|
||||
}
|
||||
|
||||
fn prev(&self) -> Self {
|
||||
if self.is_infinite() && *self < 0.0 {
|
||||
*self
|
||||
} else if *self == 0.0 && self.is_sign_positive() {
|
||||
-0.0
|
||||
} else {
|
||||
let mut u = self.to_bits();
|
||||
if *self <= -0.0 {
|
||||
u += 1;
|
||||
} else {
|
||||
u -= 1;
|
||||
}
|
||||
f32::from_bits(u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_ulps_test1() {
|
||||
let x: f32 = 1000000_f32;
|
||||
let y: f32 = 1000000.1_f32;
|
||||
println!("DIST IS {}",x.ulps(&y));
|
||||
assert!(x.ulps(&y) == -2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_ulps_test2() {
|
||||
let pzero: f32 = f32::from_bits(0x00000000_u32);
|
||||
let nzero: f32 = f32::from_bits(0x80000000_u32);
|
||||
println!("DIST IS {}",pzero.ulps(&nzero));
|
||||
assert!(pzero.ulps(&nzero) == -2147483648);
|
||||
}
|
||||
#[test]
|
||||
fn f32_ulps_test3() {
|
||||
let pinf: f32 = f32::from_bits(0x7f800000_u32);
|
||||
let ninf: f32 = f32::from_bits(0xff800000_u32);
|
||||
println!("DIST IS {}",pinf.ulps(&ninf));
|
||||
assert!(pinf.ulps(&ninf) == -2147483648);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_ulps_test4() {
|
||||
let x: f32 = f32::from_bits(0x63a7f026_u32);
|
||||
let y: f32 = f32::from_bits(0x63a7f023_u32);
|
||||
println!("DIST IS {}",x.ulps(&y));
|
||||
assert!(x.ulps(&y) == 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_ulps_test5() {
|
||||
let x: f32 = 2.0;
|
||||
let ulps: i32 = x.to_bits() as i32;
|
||||
let x2: f32 = <f32>::from_bits(ulps as u32);
|
||||
assert_eq!(x, x2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_ulps_test6() {
|
||||
let negzero: f32 = -0.;
|
||||
let zero: f32 = 0.;
|
||||
assert_eq!(negzero.next(), zero);
|
||||
assert_eq!(zero.prev(), negzero);
|
||||
assert!(negzero.prev() < 0.0);
|
||||
assert!(zero.next() > 0.0);
|
||||
}
|
||||
|
||||
impl Ulps for f64 {
|
||||
type U = i64;
|
||||
|
||||
fn ulps(&self, other: &f64) -> i64 {
|
||||
|
||||
// IEEE754 defined floating point storage representation to
|
||||
// maintain their order when their bit patterns are interpreted as
|
||||
// integers. This is a huge boon to the task at hand, as we can
|
||||
// reinterpret them as integers to find out how many ULPs apart any
|
||||
// two floats are
|
||||
|
||||
// Setup integer representations of the input
|
||||
let ai64: i64 = self.to_bits() as i64;
|
||||
let bi64: i64 = other.to_bits() as i64;
|
||||
|
||||
ai64.wrapping_sub(bi64)
|
||||
}
|
||||
|
||||
fn next(&self) -> Self {
|
||||
if self.is_infinite() && *self > 0.0 {
|
||||
*self
|
||||
} else if *self == -0.0 && self.is_sign_negative() {
|
||||
0.0
|
||||
} else {
|
||||
let mut u = self.to_bits();
|
||||
if *self >= 0.0 {
|
||||
u += 1;
|
||||
} else {
|
||||
u -= 1;
|
||||
}
|
||||
f64::from_bits(u)
|
||||
}
|
||||
}
|
||||
|
||||
fn prev(&self) -> Self {
|
||||
if self.is_infinite() && *self < 0.0 {
|
||||
*self
|
||||
} else if *self == 0.0 && self.is_sign_positive() {
|
||||
-0.0
|
||||
} else {
|
||||
let mut u = self.to_bits();
|
||||
if *self <= -0.0 {
|
||||
u += 1;
|
||||
} else {
|
||||
u -= 1;
|
||||
}
|
||||
f64::from_bits(u)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_ulps_test1() {
|
||||
let x: f64 = 1000000_f64;
|
||||
let y: f64 = 1000000.00000001_f64;
|
||||
println!("DIST IS {}",x.ulps(&y));
|
||||
assert!(x.ulps(&y) == -86);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_ulps_test2() {
|
||||
let pzero: f64 = f64::from_bits(0x0000000000000000_u64);
|
||||
let nzero: f64 = f64::from_bits(0x8000000000000000_u64);
|
||||
println!("DIST IS {}",pzero.ulps(&nzero));
|
||||
assert!(pzero.ulps(&nzero) == -9223372036854775808i64);
|
||||
}
|
||||
#[test]
|
||||
fn f64_ulps_test3() {
|
||||
let pinf: f64 = f64::from_bits(0x7f80000000000000_u64);
|
||||
let ninf: f64 = f64::from_bits(0xff80000000000000_u64);
|
||||
println!("DIST IS {}",pinf.ulps(&ninf));
|
||||
assert!(pinf.ulps(&ninf) == -9223372036854775808i64);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_ulps_test4() {
|
||||
let x: f64 = f64::from_bits(0xd017f6cc63a7f026_u64);
|
||||
let y: f64 = f64::from_bits(0xd017f6cc63a7f023_u64);
|
||||
println!("DIST IS {}",x.ulps(&y));
|
||||
assert!(x.ulps(&y) == 3);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_ulps_test5() {
|
||||
let x: f64 = 2.0;
|
||||
let ulps: i64 = x.to_bits() as i64;
|
||||
let x2: f64 = <f64>::from_bits(ulps as u64);
|
||||
assert_eq!(x, x2);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_ulps_test6() {
|
||||
let negzero: f64 = -0.;
|
||||
let zero: f64 = 0.;
|
||||
assert_eq!(negzero.next(), zero);
|
||||
assert_eq!(zero.prev(), negzero);
|
||||
assert!(negzero.prev() < 0.0);
|
||||
assert!(zero.next() > 0.0);
|
||||
}
|
||||
|
119
third_party/rust/float-cmp/src/ulps_eq.rs
vendored
Normal file
119
third_party/rust/float-cmp/src/ulps_eq.rs
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
// Copyright 2014-2018 Optimal Computing (NZ) Ltd.
|
||||
// Licensed under the MIT license. See LICENSE for details.
|
||||
|
||||
use super::Ulps;
|
||||
|
||||
/// ApproxEqUlps is a trait for approximate equality comparisons.
|
||||
/// The associated type Flt is a floating point type which implements Ulps, and is
|
||||
/// required so that this trait can be implemented for compound types (e.g. vectors),
|
||||
/// not just for the floats themselves.
|
||||
pub trait ApproxEqUlps {
|
||||
type Flt: Ulps;
|
||||
|
||||
/// This method tests for `self` and `other` values to be approximately equal
|
||||
/// within ULPs (Units of Least Precision) floating point representations.
|
||||
/// Differing signs are always unequal with this method, and zeroes are only
|
||||
/// equal to zeroes. Use approx_eq() from the ApproxEq trait if that is more
|
||||
/// appropriate.
|
||||
fn approx_eq_ulps(&self, other: &Self, ulps: <Self::Flt as Ulps>::U) -> bool;
|
||||
|
||||
/// This method tests for `self` and `other` values to be not approximately
|
||||
/// equal within ULPs (Units of Least Precision) floating point representations.
|
||||
/// Differing signs are always unequal with this method, and zeroes are only
|
||||
/// equal to zeroes. Use approx_eq() from the ApproxEq trait if that is more
|
||||
/// appropriate.
|
||||
#[inline]
|
||||
fn approx_ne_ulps(&self, other: &Self, ulps: <Self::Flt as Ulps>::U) -> bool {
|
||||
!self.approx_eq_ulps(other, ulps)
|
||||
}
|
||||
}
|
||||
|
||||
impl ApproxEqUlps for f32 {
|
||||
type Flt = f32;
|
||||
|
||||
fn approx_eq_ulps(&self, other: &f32, ulps: i32) -> bool {
|
||||
// -0 and +0 are drastically far in ulps terms, so
|
||||
// we need a special case for that.
|
||||
if *self==*other { return true; }
|
||||
|
||||
// Handle differing signs as a special case, even if
|
||||
// they are very close, most people consider them
|
||||
// unequal.
|
||||
if self.is_sign_positive() != other.is_sign_positive() { return false; }
|
||||
|
||||
let diff: i32 = self.ulps(other);
|
||||
diff >= -ulps && diff <= ulps
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f32_approx_eq_ulps_test1() {
|
||||
let f: f32 = 0.1_f32;
|
||||
let mut sum: f32 = 0.0_f32;
|
||||
for _ in 0_isize..10_isize { sum += f; }
|
||||
let product: f32 = f * 10.0_f32;
|
||||
assert!(sum != product); // Should not be directly equal:
|
||||
println!("Ulps Difference: {}",sum.ulps(&product));
|
||||
assert!(sum.approx_eq_ulps(&product,1) == true); // But should be close
|
||||
assert!(sum.approx_eq_ulps(&product,0) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_ulps_test2() {
|
||||
let x: f32 = 1000000_f32;
|
||||
let y: f32 = 1000000.1_f32;
|
||||
assert!(x != y); // Should not be directly equal
|
||||
println!("Ulps Difference: {}",x.ulps(&y));
|
||||
assert!(x.approx_eq_ulps(&y,2) == true);
|
||||
assert!(x.approx_eq_ulps(&y,1) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f32_approx_eq_ulps_test_zeroes() {
|
||||
let x: f32 = 0.0_f32;
|
||||
let y: f32 = -0.0_f32;
|
||||
assert!(x.approx_eq_ulps(&y,0) == true);
|
||||
}
|
||||
|
||||
impl ApproxEqUlps for f64 {
|
||||
type Flt = f64;
|
||||
|
||||
fn approx_eq_ulps(&self, other: &f64, ulps: i64) -> bool {
|
||||
// -0 and +0 are drastically far in ulps terms, so
|
||||
// we need a special case for that.
|
||||
if *self==*other { return true; }
|
||||
|
||||
// Handle differing signs as a special case, even if
|
||||
// they are very close, most people consider them
|
||||
// unequal.
|
||||
if self.is_sign_positive() != other.is_sign_positive() { return false; }
|
||||
|
||||
let diff: i64 = self.ulps(other);
|
||||
diff >= -ulps && diff <= ulps
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn f64_approx_eq_ulps_test1() {
|
||||
let f: f64 = 0.1_f64;
|
||||
let mut sum: f64 = 0.0_f64;
|
||||
for _ in 0_isize..10_isize { sum += f; }
|
||||
let product: f64 = f * 10.0_f64;
|
||||
assert!(sum != product); // Should not be directly equal:
|
||||
println!("Ulps Difference: {}",sum.ulps(&product));
|
||||
assert!(sum.approx_eq_ulps(&product,1) == true); // But should be close
|
||||
assert!(sum.approx_eq_ulps(&product,0) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_ulps_test2() {
|
||||
let x: f64 = 1000000_f64;
|
||||
let y: f64 = 1000000.0000000003_f64;
|
||||
assert!(x != y); // Should not be directly equal
|
||||
println!("Ulps Difference: {}",x.ulps(&y));
|
||||
assert!(x.approx_eq_ulps(&y,3) == true);
|
||||
assert!(x.approx_eq_ulps(&y,2) == false);
|
||||
}
|
||||
#[test]
|
||||
fn f64_approx_eq_ulps_test_zeroes() {
|
||||
let x: f64 = 0.0_f64;
|
||||
let y: f64 = -0.0_f64;
|
||||
assert!(x.approx_eq_ulps(&y,0) == true);
|
||||
}
|
@ -19,7 +19,7 @@ static_prefs = { path = "../../../../modules/libpref/init/static_prefs" }
|
||||
profiler_helper = { path = "../../../../tools/profiler/rust-helper", optional = true }
|
||||
mozurl = { path = "../../../../netwerk/base/mozurl" }
|
||||
webrender_bindings = { path = "../../../../gfx/webrender_bindings", optional = true }
|
||||
cubeb-coreaudio = { git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs", rev = "7fe03b4201160f84aad1c12536affe6b05f8839f", optional = true }
|
||||
cubeb-coreaudio = { git = "https://github.com/ChunMinChang/cubeb-coreaudio-rs", rev = "4acd80233efa645ac79769f37b07d495c1b42070", optional = true }
|
||||
cubeb-pulse = { git = "https://github.com/djg/cubeb-pulse-rs", rev="8069f8f4189982e0b38fa6dc8993dd4fab41f728", optional = true, features=["pulse-dlopen"] }
|
||||
cubeb-sys = { version = "0.6", optional = true, features=["gecko-in-tree"] }
|
||||
encoding_glue = { path = "../../../../intl/encoding_glue" }
|
||||
|
Loading…
Reference in New Issue
Block a user