pyo3/types/
float.rs

1use super::any::PyAnyMethods;
2use crate::conversion::IntoPyObject;
3#[cfg(feature = "experimental-inspect")]
4use crate::inspect::types::TypeInfo;
5use crate::{
6    ffi, ffi_ptr_ext::FfiPtrExt, instance::Bound, Borrowed, FromPyObject, PyAny, PyErr, PyObject,
7    PyResult, Python,
8};
9#[allow(deprecated)]
10use crate::{IntoPy, ToPyObject};
11use std::convert::Infallible;
12use std::os::raw::c_double;
13
14/// Represents a Python `float` object.
15///
16/// Values of this type are accessed via PyO3's smart pointers, e.g. as
17/// [`Py<PyFloat>`][crate::Py] or [`Bound<'py, PyFloat>`][Bound].
18///
19/// For APIs available on `float` objects, see the [`PyFloatMethods`] trait which is implemented for
20/// [`Bound<'py, PyFloat>`][Bound].
21///
22/// You can usually avoid directly working with this type
23/// by using [`ToPyObject`] and [`extract`][PyAnyMethods::extract]
24/// with [`f32`]/[`f64`].
25#[repr(transparent)]
26pub struct PyFloat(PyAny);
27
28pyobject_subclassable_native_type!(PyFloat, crate::ffi::PyFloatObject);
29
30pyobject_native_type!(
31    PyFloat,
32    ffi::PyFloatObject,
33    pyobject_native_static_type_object!(ffi::PyFloat_Type),
34    #checkfunction=ffi::PyFloat_Check
35);
36
37impl PyFloat {
38    /// Creates a new Python `float` object.
39    pub fn new(py: Python<'_>, val: c_double) -> Bound<'_, PyFloat> {
40        unsafe {
41            ffi::PyFloat_FromDouble(val)
42                .assume_owned(py)
43                .downcast_into_unchecked()
44        }
45    }
46
47    /// Deprecated name for [`PyFloat::new`].
48    #[deprecated(since = "0.23.0", note = "renamed to `PyFloat::new`")]
49    #[inline]
50    pub fn new_bound(py: Python<'_>, val: c_double) -> Bound<'_, PyFloat> {
51        Self::new(py, val)
52    }
53}
54
55/// Implementation of functionality for [`PyFloat`].
56///
57/// These methods are defined for the `Bound<'py, PyFloat>` smart pointer, so to use method call
58/// syntax these methods are separated into a trait, because stable Rust does not yet support
59/// `arbitrary_self_types`.
60#[doc(alias = "PyFloat")]
61pub trait PyFloatMethods<'py>: crate::sealed::Sealed {
62    /// Gets the value of this float.
63    fn value(&self) -> c_double;
64}
65
66impl<'py> PyFloatMethods<'py> for Bound<'py, PyFloat> {
67    fn value(&self) -> c_double {
68        #[cfg(not(Py_LIMITED_API))]
69        unsafe {
70            // Safety: self is PyFloat object
71            ffi::PyFloat_AS_DOUBLE(self.as_ptr())
72        }
73
74        #[cfg(Py_LIMITED_API)]
75        unsafe {
76            ffi::PyFloat_AsDouble(self.as_ptr())
77        }
78    }
79}
80
81#[allow(deprecated)]
82impl ToPyObject for f64 {
83    #[inline]
84    fn to_object(&self, py: Python<'_>) -> PyObject {
85        self.into_pyobject(py).unwrap().into_any().unbind()
86    }
87}
88
89#[allow(deprecated)]
90impl IntoPy<PyObject> for f64 {
91    #[inline]
92    fn into_py(self, py: Python<'_>) -> PyObject {
93        self.into_pyobject(py).unwrap().into_any().unbind()
94    }
95}
96
97impl<'py> IntoPyObject<'py> for f64 {
98    type Target = PyFloat;
99    type Output = Bound<'py, Self::Target>;
100    type Error = Infallible;
101
102    #[inline]
103    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
104        Ok(PyFloat::new(py, self))
105    }
106
107    #[cfg(feature = "experimental-inspect")]
108    fn type_output() -> TypeInfo {
109        TypeInfo::builtin("float")
110    }
111}
112
113impl<'py> IntoPyObject<'py> for &f64 {
114    type Target = PyFloat;
115    type Output = Bound<'py, Self::Target>;
116    type Error = Infallible;
117
118    #[inline]
119    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
120        (*self).into_pyobject(py)
121    }
122
123    #[cfg(feature = "experimental-inspect")]
124    fn type_output() -> TypeInfo {
125        TypeInfo::builtin("float")
126    }
127}
128
129impl<'py> FromPyObject<'py> for f64 {
130    // PyFloat_AsDouble returns -1.0 upon failure
131    #![allow(clippy::float_cmp)]
132    fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
133        // On non-limited API, .value() uses PyFloat_AS_DOUBLE which
134        // allows us to have an optimized fast path for the case when
135        // we have exactly a `float` object (it's not worth going through
136        // `isinstance` machinery for subclasses).
137        #[cfg(not(Py_LIMITED_API))]
138        if let Ok(float) = obj.downcast_exact::<PyFloat>() {
139            return Ok(float.value());
140        }
141
142        let v = unsafe { ffi::PyFloat_AsDouble(obj.as_ptr()) };
143
144        if v == -1.0 {
145            if let Some(err) = PyErr::take(obj.py()) {
146                return Err(err);
147            }
148        }
149
150        Ok(v)
151    }
152
153    #[cfg(feature = "experimental-inspect")]
154    fn type_input() -> TypeInfo {
155        Self::type_output()
156    }
157}
158
159#[allow(deprecated)]
160impl ToPyObject for f32 {
161    #[inline]
162    fn to_object(&self, py: Python<'_>) -> PyObject {
163        self.into_pyobject(py).unwrap().into_any().unbind()
164    }
165}
166
167#[allow(deprecated)]
168impl IntoPy<PyObject> for f32 {
169    #[inline]
170    fn into_py(self, py: Python<'_>) -> PyObject {
171        self.into_pyobject(py).unwrap().into_any().unbind()
172    }
173}
174
175impl<'py> IntoPyObject<'py> for f32 {
176    type Target = PyFloat;
177    type Output = Bound<'py, Self::Target>;
178    type Error = Infallible;
179
180    #[inline]
181    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
182        Ok(PyFloat::new(py, self.into()))
183    }
184
185    #[cfg(feature = "experimental-inspect")]
186    fn type_output() -> TypeInfo {
187        TypeInfo::builtin("float")
188    }
189}
190
191impl<'py> IntoPyObject<'py> for &f32 {
192    type Target = PyFloat;
193    type Output = Bound<'py, Self::Target>;
194    type Error = Infallible;
195
196    #[inline]
197    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
198        (*self).into_pyobject(py)
199    }
200
201    #[cfg(feature = "experimental-inspect")]
202    fn type_output() -> TypeInfo {
203        TypeInfo::builtin("float")
204    }
205}
206
207impl<'py> FromPyObject<'py> for f32 {
208    fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
209        Ok(obj.extract::<f64>()? as f32)
210    }
211
212    #[cfg(feature = "experimental-inspect")]
213    fn type_input() -> TypeInfo {
214        Self::type_output()
215    }
216}
217
218macro_rules! impl_partial_eq_for_float {
219    ($float_type: ty) => {
220        impl PartialEq<$float_type> for Bound<'_, PyFloat> {
221            #[inline]
222            fn eq(&self, other: &$float_type) -> bool {
223                self.value() as $float_type == *other
224            }
225        }
226
227        impl PartialEq<$float_type> for &Bound<'_, PyFloat> {
228            #[inline]
229            fn eq(&self, other: &$float_type) -> bool {
230                self.value() as $float_type == *other
231            }
232        }
233
234        impl PartialEq<&$float_type> for Bound<'_, PyFloat> {
235            #[inline]
236            fn eq(&self, other: &&$float_type) -> bool {
237                self.value() as $float_type == **other
238            }
239        }
240
241        impl PartialEq<Bound<'_, PyFloat>> for $float_type {
242            #[inline]
243            fn eq(&self, other: &Bound<'_, PyFloat>) -> bool {
244                other.value() as $float_type == *self
245            }
246        }
247
248        impl PartialEq<&'_ Bound<'_, PyFloat>> for $float_type {
249            #[inline]
250            fn eq(&self, other: &&'_ Bound<'_, PyFloat>) -> bool {
251                other.value() as $float_type == *self
252            }
253        }
254
255        impl PartialEq<Bound<'_, PyFloat>> for &'_ $float_type {
256            #[inline]
257            fn eq(&self, other: &Bound<'_, PyFloat>) -> bool {
258                other.value() as $float_type == **self
259            }
260        }
261
262        impl PartialEq<$float_type> for Borrowed<'_, '_, PyFloat> {
263            #[inline]
264            fn eq(&self, other: &$float_type) -> bool {
265                self.value() as $float_type == *other
266            }
267        }
268
269        impl PartialEq<&$float_type> for Borrowed<'_, '_, PyFloat> {
270            #[inline]
271            fn eq(&self, other: &&$float_type) -> bool {
272                self.value() as $float_type == **other
273            }
274        }
275
276        impl PartialEq<Borrowed<'_, '_, PyFloat>> for $float_type {
277            #[inline]
278            fn eq(&self, other: &Borrowed<'_, '_, PyFloat>) -> bool {
279                other.value() as $float_type == *self
280            }
281        }
282
283        impl PartialEq<Borrowed<'_, '_, PyFloat>> for &$float_type {
284            #[inline]
285            fn eq(&self, other: &Borrowed<'_, '_, PyFloat>) -> bool {
286                other.value() as $float_type == **self
287            }
288        }
289    };
290}
291
292impl_partial_eq_for_float!(f64);
293impl_partial_eq_for_float!(f32);
294
295#[cfg(test)]
296mod tests {
297    use crate::{
298        conversion::IntoPyObject,
299        types::{PyAnyMethods, PyFloat, PyFloatMethods},
300        Python,
301    };
302
303    macro_rules! num_to_py_object_and_back (
304        ($func_name:ident, $t1:ty, $t2:ty) => (
305            #[test]
306            fn $func_name() {
307                use assert_approx_eq::assert_approx_eq;
308
309                Python::with_gil(|py| {
310
311                let val = 123 as $t1;
312                let obj = val.into_pyobject(py).unwrap();
313                assert_approx_eq!(obj.extract::<$t2>().unwrap(), val as $t2);
314                });
315            }
316        )
317    );
318
319    num_to_py_object_and_back!(to_from_f64, f64, f64);
320    num_to_py_object_and_back!(to_from_f32, f32, f32);
321    num_to_py_object_and_back!(int_to_float, i32, f64);
322
323    #[test]
324    fn test_float_value() {
325        use assert_approx_eq::assert_approx_eq;
326
327        Python::with_gil(|py| {
328            let v = 1.23f64;
329            let obj = PyFloat::new(py, 1.23);
330            assert_approx_eq!(v, obj.value());
331        });
332    }
333
334    #[test]
335    fn test_pyfloat_comparisons() {
336        Python::with_gil(|py| {
337            let f_64 = 1.01f64;
338            let py_f64 = PyFloat::new(py, 1.01);
339            let py_f64_ref = &py_f64;
340            let py_f64_borrowed = py_f64.as_borrowed();
341
342            // Bound<'_, PyFloat> == f64 and vice versa
343            assert_eq!(py_f64, f_64);
344            assert_eq!(f_64, py_f64);
345
346            // Bound<'_, PyFloat> == &f64 and vice versa
347            assert_eq!(py_f64, &f_64);
348            assert_eq!(&f_64, py_f64);
349
350            // &Bound<'_, PyFloat> == &f64 and vice versa
351            assert_eq!(py_f64_ref, f_64);
352            assert_eq!(f_64, py_f64_ref);
353
354            // &Bound<'_, PyFloat> == &f64 and vice versa
355            assert_eq!(py_f64_ref, &f_64);
356            assert_eq!(&f_64, py_f64_ref);
357
358            // Borrowed<'_, '_, PyFloat> == f64 and vice versa
359            assert_eq!(py_f64_borrowed, f_64);
360            assert_eq!(f_64, py_f64_borrowed);
361
362            // Borrowed<'_, '_, PyFloat> == &f64 and vice versa
363            assert_eq!(py_f64_borrowed, &f_64);
364            assert_eq!(&f_64, py_f64_borrowed);
365
366            let f_32 = 2.02f32;
367            let py_f32 = PyFloat::new(py, 2.02);
368            let py_f32_ref = &py_f32;
369            let py_f32_borrowed = py_f32.as_borrowed();
370
371            // Bound<'_, PyFloat> == f32 and vice versa
372            assert_eq!(py_f32, f_32);
373            assert_eq!(f_32, py_f32);
374
375            // Bound<'_, PyFloat> == &f32 and vice versa
376            assert_eq!(py_f32, &f_32);
377            assert_eq!(&f_32, py_f32);
378
379            // &Bound<'_, PyFloat> == &f32 and vice versa
380            assert_eq!(py_f32_ref, f_32);
381            assert_eq!(f_32, py_f32_ref);
382
383            // &Bound<'_, PyFloat> == &f32 and vice versa
384            assert_eq!(py_f32_ref, &f_32);
385            assert_eq!(&f_32, py_f32_ref);
386
387            // Borrowed<'_, '_, PyFloat> == f32 and vice versa
388            assert_eq!(py_f32_borrowed, f_32);
389            assert_eq!(f_32, py_f32_borrowed);
390
391            // Borrowed<'_, '_, PyFloat> == &f32 and vice versa
392            assert_eq!(py_f32_borrowed, &f_32);
393            assert_eq!(&f_32, py_f32_borrowed);
394        });
395    }
396}
⚠️ Internal Docs ⚠️ Not Public API 👉 Official Docs Here