1 | use super::any::PyAnyMethods; |
2 | use crate::conversion::IntoPyObject; |
3 | #[cfg (feature = "experimental-inspect" )] |
4 | use crate::inspect::types::TypeInfo; |
5 | use crate::{ |
6 | ffi, ffi_ptr_ext::FfiPtrExt, instance::Bound, Borrowed, FromPyObject, PyAny, PyErr, PyObject, |
7 | PyResult, Python, |
8 | }; |
9 | #[allow (deprecated)] |
10 | use crate::{IntoPy, ToPyObject}; |
11 | use std::convert::Infallible; |
12 | use 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)] |
26 | pub struct PyFloat(PyAny); |
27 | |
28 | pyobject_subclassable_native_type!(PyFloat, crate::ffi::PyFloatObject); |
29 | |
30 | pyobject_native_type!( |
31 | PyFloat, |
32 | ffi::PyFloatObject, |
33 | pyobject_native_static_type_object!(ffi::PyFloat_Type), |
34 | #checkfunction=ffi::PyFloat_Check |
35 | ); |
36 | |
37 | impl PyFloat { |
38 | /// Creates a new Python `float` object. |
39 | pub fn new(py: Python<'_>, val: c_double) -> Bound<'_, PyFloat> { |
40 | unsafe { |
41 | ffiBound<'_, PyAny>::PyFloat_FromDouble(arg1: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" )] |
61 | pub trait PyFloatMethods<'py>: crate::sealed::Sealed { |
62 | /// Gets the value of this float. |
63 | fn value(&self) -> c_double; |
64 | } |
65 | |
66 | impl<'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)] |
82 | impl 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)] |
90 | impl 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 | |
97 | impl<'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 | |
113 | impl<'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 | |
129 | impl<'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)] |
160 | impl 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)] |
168 | impl 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 | |
175 | impl<'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 | |
191 | impl<'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 | |
207 | impl<'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 | |
218 | macro_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 | |
292 | impl_partial_eq_for_float!(f64); |
293 | impl_partial_eq_for_float!(f32); |
294 | |
295 | #[cfg (test)] |
296 | mod 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 | } |
397 | |