Skip to content

Commit 4c20f71

Browse files
add more docs
1 parent 3e9b533 commit 4c20f71

File tree

5 files changed

+151
-14
lines changed

5 files changed

+151
-14
lines changed

Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ edition = "2018"
88
license = "LGPL-2.1-or-later"
99
description = "soft-float library that intends to be a straightforward reference implementation of IEEE 754"
1010
readme = "README.md"
11+
repository = "https://salsa.debian.org/Kazan-team/simple-soft-float"
1112

1213
[lib]
1314
name = "simple_soft_float"

README.md

+57-1
Original file line numberDiff line numberDiff line change
@@ -1 +1,57 @@
1-
Soft-float library that intends to be a straightforward reference implementation of IEEE 754
1+
Soft-float library that intends to be a straightforward reference implementation of IEEE 754.
2+
3+
## Installation for use from Rust
4+
5+
Add to your `Cargo.toml`:
6+
7+
```toml
8+
[dependencies.simple-soft-float]
9+
# switch to crates.io version once new version is published
10+
git = "https://salsa.debian.org/Kazan-team/simple-soft-float"
11+
rev = "<current git revision>"
12+
```
13+
14+
## Installation for use from Python
15+
16+
Install Rust using [rustup.rs](https://rustup.rs).
17+
18+
Create CPython 3.6 to 3.7 virtualenv (not sure if 3.8 is supported yet).
19+
20+
Install Python bindings build tool:
21+
```bash
22+
pip install maturin
23+
```
24+
25+
Get source:
26+
```bash
27+
git clone https://salsa.debian.org/Kazan-team/simple-soft-float.git
28+
cd simple-soft-float
29+
```
30+
31+
Change source dir to use specific version of Rust nightly:
32+
(must be in `simple-soft-float` dir):
33+
```bash
34+
rustup override set nightly-2019-07-19
35+
```
36+
37+
Build and Test (like `setup.py develop`):
38+
```bash
39+
cargo test --features python # runs tests from Rust
40+
# build and install to python
41+
maturin develop --cargo-extra-args="--features python-extension"
42+
python -m unittest # runs smoke tests from Python
43+
```
44+
45+
Build Rust docs:
46+
```bash
47+
cargo doc --features python # ignore warning about rand_core name collision
48+
open docs in default browser:
49+
xdg-open target/doc/simple_soft_float/struct.DynamicFloat.html
50+
```
51+
52+
Build Python docs:
53+
```bash
54+
pip install pdoc3
55+
pdoc3 simple_soft_float --html -o target/python-docs
56+
xdg-open target/python-docs/simple_soft_float.html
57+
```

src/lib.rs

+36
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,9 @@ python_enum! {
6161
#[pyenum(module = simple_soft_float, repr = u8, test_fn = test_sign_enum)]
6262
/// sign of floating-point number
6363
pub enum Sign {
64+
/// + sign
6465
Positive = 0,
66+
/// - sign
6567
Negative = 1,
6668
}
6769
}
@@ -167,6 +169,7 @@ impl Default for RoundingMode {
167169
}
168170

169171
bitflags! {
172+
/// IEEE 754 status flags
170173
pub struct StatusFlags: u32 {
171174
const INVALID_OPERATION = 0b00001;
172175
const DIVISION_BY_ZERO = 0b00010;
@@ -184,6 +187,21 @@ impl Default for StatusFlags {
184187

185188
python_enum! {
186189
#[pyenum(module = simple_soft_float, repr = u8, test_fn = test_exception_handling_mode_enum)]
190+
/// Select if the underflow exception should be signaled when the result is exact.
191+
///
192+
/// In IEEE 754, when exceptions are set to use the default handlers
193+
/// (they are ignored -- the default for most programming languages), then
194+
/// underflow exceptions are only signalled when the result is not exact.
195+
///
196+
/// When exceptions are instead set to trap, then underflow exceptions are
197+
/// signalled even when the result is exact, to allow the exception handler
198+
/// to emulate flush-to-zero FP semantics.
199+
///
200+
/// Since simple-soft-float doesn't support trapping exceptions, to simulate
201+
/// trapping exceptions, use `DefaultSignalExactUnderflow` as the exception
202+
/// handling mode and check `status_flags` after every operation.
203+
///
204+
/// Otherwise, use the default value of `DefaultIgnoreExactUnderflow`.
187205
pub enum ExceptionHandlingMode {
188206
DefaultIgnoreExactUnderflow,
189207
DefaultSignalExactUnderflow,
@@ -198,6 +216,7 @@ impl Default for ExceptionHandlingMode {
198216

199217
python_enum! {
200218
#[pyenum(module = simple_soft_float, repr = u8, test_fn = test_tininess_detection_mode_enum)]
219+
/// IEEE 754 tininess detection mode
201220
pub enum TininessDetectionMode {
202221
AfterRounding,
203222
BeforeRounding,
@@ -212,6 +231,7 @@ impl Default for TininessDetectionMode {
212231

213232
python_enum! {
214233
#[pyenum(module = simple_soft_float, repr = u8, test_fn = test_binary_nan_propagation_mode_enum)]
234+
/// Select how NaN payloads should be propagated
215235
pub enum BinaryNaNPropagationMode {
216236
AlwaysCanonical,
217237
FirstSecond,
@@ -223,6 +243,7 @@ python_enum! {
223243

224244
python_enum! {
225245
#[pyenum(module = simple_soft_float, repr = u8, test_fn = test_unary_nan_propagation_mode_enum)]
246+
/// Select how NaN payloads should be propagated
226247
pub enum UnaryNaNPropagationMode {
227248
AlwaysCanonical,
228249
First,
@@ -377,6 +398,7 @@ impl Default for TernaryNaNPropagationResults {
377398

378399
python_enum! {
379400
#[pyenum(module = simple_soft_float, repr = u8, test_fn = test_ternary_nan_propagation_mode_enum)]
401+
/// Select how NaN payloads should be propagated
380402
pub enum TernaryNaNPropagationMode {
381403
AlwaysCanonical,
382404
FirstSecondThird,
@@ -586,6 +608,7 @@ impl TernaryNaNPropagationMode {
586608

587609
python_enum! {
588610
#[pyenum(module = simple_soft_float, repr = u8, test_fn = test_fma_inf_zero_qnan_result_enum)]
611+
/// select the result of fused `Infinity * 0.0 + QNaN` and `0.0 * Infinity + QNaN`
589612
pub enum FMAInfZeroQNaNResult {
590613
FollowNaNPropagationMode,
591614
CanonicalAndGenerateInvalid,
@@ -601,6 +624,7 @@ impl Default for FMAInfZeroQNaNResult {
601624

602625
python_enum! {
603626
#[pyenum(module = simple_soft_float, repr = u8, test_fn = test_float_to_float_conversion_nan_propagation_mode_enum)]
627+
/// select how NaN payloads are propagated in float -> float conversions
604628
pub enum FloatToFloatConversionNaNPropagationMode {
605629
AlwaysCanonical,
606630
RetainMostSignificantBits,
@@ -636,6 +660,7 @@ impl From<FPStateMergeFailed> for PyErr {
636660
}
637661

638662
impl FPState {
663+
/// combine two `FPState` values into one, assigning the result to `self`
639664
pub fn checked_merge_assign(&mut self, rhs: Self) -> Result<(), FPStateMergeFailed> {
640665
let status_flags = self.status_flags | rhs.status_flags;
641666
let same = Self {
@@ -652,13 +677,16 @@ impl FPState {
652677
Err(FPStateMergeFailed)
653678
}
654679
}
680+
/// combine two `FPState` values into one, assigning the result to `self`
655681
pub fn merge_assign(&mut self, rhs: Self) {
656682
self.checked_merge_assign(rhs).unwrap();
657683
}
684+
/// combine two `FPState` values into one, returning the result
658685
pub fn checked_merge(mut self, rhs: Self) -> Result<Self, FPStateMergeFailed> {
659686
self.checked_merge_assign(rhs)?;
660687
Ok(self)
661688
}
689+
/// combine two `FPState` values into one, returning the result
662690
pub fn merge(mut self, rhs: Self) -> Self {
663691
self.merge_assign(rhs);
664692
self
@@ -667,6 +695,7 @@ impl FPState {
667695

668696
python_enum! {
669697
#[pyenum(module = simple_soft_float, repr = u8, test_fn = test_float_class_enum)]
698+
/// float classification
670699
pub enum FloatClass {
671700
NegativeInfinity,
672701
NegativeNormal,
@@ -816,6 +845,12 @@ impl Neg for FloatClass {
816845

817846
python_enum! {
818847
#[pyenum(module = simple_soft_float, repr = u8, test_fn = test_quiet_nan_format_enum)]
848+
/// the format for quiet NaN values
849+
///
850+
/// IEEE 754 states that implementations *should* use the MSB of the
851+
/// mantissa being set to indicate a quiet NaN, however MIPS before
852+
/// the 2008 revision and PA-RISC use the MSB of the mantissa being clear
853+
/// to indicate a quiet NaN.
819854
pub enum QuietNaNFormat {
820855
/// MSB of mantissa set to indicate quiet NaN
821856
Standard,
@@ -1611,6 +1646,7 @@ impl RoundedMantissa {
16111646

16121647
python_enum! {
16131648
#[pyenum(module = simple_soft_float, repr = u8, test_fn = test_up_or_down_enum)]
1649+
/// select Up or Down
16141650
pub enum UpOrDown {
16151651
Up,
16161652
Down,

src/python.rs

+7
Original file line numberDiff line numberDiff line change
@@ -155,12 +155,19 @@ impl StatusFlags {
155155
writeln!(src, "def f(status_flags_repr):")?;
156156
writeln!(src, " import enum")?;
157157
writeln!(src, " class {}(enum.Flag):", StatusFlags::NAME)?;
158+
writeln!(src, " \"\"\"IEEE 754 status flags\"\"\"")?;
158159
for &(name, value) in StatusFlags::MEMBERS {
159160
writeln!(src, " {} = {}", name, value.bits())?;
160161
}
161162
writeln!(src, " __module__ = \"simple_soft_float\"")?;
163+
writeln!(src, " __qualname__ = \"{}\"", StatusFlags::NAME)?;
162164
writeln!(src, " def __repr__(self):")?;
163165
writeln!(src, " return status_flags_repr(self)")?;
166+
writeln!(
167+
src,
168+
" __repr__.__qualname__ = \"{}.__repr__\"",
169+
StatusFlags::NAME
170+
)?;
164171
writeln!(src, " return {}", StatusFlags::NAME)?;
165172
writeln!(src, "{} = f(status_flags_repr)", StatusFlags::NAME)?;
166173
Ok(src)

src/python_macros.rs

+50-13
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,13 @@ use pyo3::PyNativeType;
1818
#[cfg(feature = "python")]
1919
use std::fmt::{self, Write as _};
2020

21+
#[cfg(feature = "python")]
22+
pub(crate) struct PythonEnumMember<T: PythonEnum> {
23+
pub(crate) name: &'static str,
24+
pub(crate) value: T,
25+
pub(crate) docs: Option<&'static str>,
26+
}
27+
2128
#[cfg(feature = "python")]
2229
pub(crate) trait PythonEnum:
2330
Copy
@@ -29,8 +36,9 @@ pub(crate) trait PythonEnum:
2936
+ crate::python::ToPythonRepr
3037
{
3138
const NAME: &'static str;
39+
const DOCS: Option<&'static str>;
3240
const MODULE_NAME: &'static str;
33-
const MEMBERS: &'static [(&'static str, Self)];
41+
const MEMBERS: &'static [PythonEnumMember<Self>];
3442
type Repr: Copy + 'static + fmt::Display + for<'source> FromPyObject<'source> + IntoPy<PyObject>;
3543
fn to_repr(self) -> Self::Repr;
3644
fn from_repr(value: Self::Repr) -> Option<Self>;
@@ -44,8 +52,14 @@ pub(crate) trait PythonEnum:
4452
let get_class_src = || -> Result<String, fmt::Error> {
4553
let mut retval = String::new();
4654
writeln!(retval, "class {}(enum.Enum):", Self::NAME)?;
47-
for &(name, value) in Self::MEMBERS {
55+
if let Some(docs) = Self::DOCS {
56+
writeln!(retval, " {:?}", docs)?;
57+
}
58+
for &PythonEnumMember { name, value, docs } in Self::MEMBERS {
4859
writeln!(retval, " {} = {}", name, value.to_repr())?;
60+
if let Some(docs) = docs {
61+
writeln!(retval, " {:?}", docs)?;
62+
}
4963
}
5064
writeln!(retval, "{}.__module__ = module_name", Self::NAME)?;
5165
Ok(retval)
@@ -87,7 +101,7 @@ pub(crate) trait PythonEnum:
87101
Self::NAME,
88102
Self::MODULE_NAME
89103
);
90-
for &(_, value) in Self::MEMBERS {
104+
for &PythonEnumMember { value, .. } in Self::MEMBERS {
91105
let object: PyObject = value.into_py(py);
92106
assert_eq!(value, object.extract::<Self>(py)?);
93107
}
@@ -128,20 +142,40 @@ pub(crate) fn python_enum_extract_impl<T: PythonEnum>(object: &PyAny) -> PyResul
128142
)))
129143
}
130144

145+
#[cfg(feature = "python")]
146+
macro_rules! docs_to_string {
147+
(#[doc = $doc_first:literal] $(#[doc = $doc_rest:literal])*) => {
148+
Some(concat!($doc_first $(, "\n", $doc_rest)*))
149+
};
150+
() => {
151+
None
152+
};
153+
}
154+
131155
#[cfg(feature = "python")]
132156
macro_rules! python_enum_impl {
133157
(
134158
#[pyenum(module = $module:ident, repr = $repr_type:ident, test_fn = $test_fn:ident)]
135-
$(#[$meta:meta])*
159+
$(#[doc = $enum_doc:literal])*
136160
$vis:vis enum $enum_name:ident {
137-
$($value_name:ident $(= $value_init:expr)*,)+
161+
$(
162+
$(#[doc = $value_doc:literal])*
163+
$value_name:ident $(= $value_init:expr)*,
164+
)+
138165
}
139166
) => {
140167
impl $crate::python_macros::PythonEnum for $enum_name {
141168
const NAME: &'static str = stringify!($enum_name);
169+
const DOCS: Option<&'static str> = docs_to_string!($(#[doc = $enum_doc])*);
142170
const MODULE_NAME: &'static str = stringify!($module);
143-
const MEMBERS: &'static [(&'static str, Self)] = &[
144-
$((stringify!($value_name), Self::$value_name),)+
171+
const MEMBERS: &'static [$crate::python_macros::PythonEnumMember<Self>] = &[
172+
$(
173+
$crate::python_macros::PythonEnumMember {
174+
name: stringify!($value_name),
175+
value: Self::$value_name,
176+
docs: docs_to_string!($(#[doc = $value_doc])*),
177+
},
178+
)+
145179
];
146180
type Repr = $repr_type;
147181
fn to_repr(self) -> Self::Repr {
@@ -203,29 +237,32 @@ macro_rules! python_enum_impl {
203237
macro_rules! python_enum {
204238
(
205239
#[pyenum(module = $module:ident, repr = $repr_type:ident, test_fn = $test_fn:ident)]
206-
$(#[$meta:meta])*
240+
$(#[doc = $enum_doc:literal])*
207241
$vis:vis enum $enum_name:ident {
208242
$(
209-
$(#[doc $($value_doc:tt)*])*
243+
$(#[doc = $value_doc:literal])*
210244
$value_name:ident $(= $value_init:expr)*,
211245
)+
212246
}
213247
) => {
214-
$(#[$meta])*
248+
$(#[doc = $enum_doc])*
215249
#[derive(Copy, Clone, Hash, Eq, PartialEq, Debug)]
216250
#[repr($repr_type)]
217251
$vis enum $enum_name {
218252
$(
219-
$(#[doc $($value_doc)*])*
253+
$(#[doc = $value_doc])*
220254
$value_name $(= $value_init)*,
221255
)+
222256
}
223257

224258
python_enum_impl! {
225259
#[pyenum(module = $module, repr = $repr_type, test_fn = $test_fn)]
226-
$(#[$meta])*
260+
$(#[doc = $enum_doc])*
227261
$vis enum $enum_name {
228-
$($value_name $(= $value_init)*,)+
262+
$(
263+
$(#[doc = $value_doc])*
264+
$value_name $(= $value_init)*,
265+
)+
229266
}
230267
}
231268
};

0 commit comments

Comments
 (0)