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#[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 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(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#[doc(alias = "PyFloat")]
61pub trait PyFloatMethods<'py>: crate::sealed::Sealed {
62 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 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 #![allow(clippy::float_cmp)]
132 fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
133 #[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 assert_eq!(py_f64, f_64);
344 assert_eq!(f_64, py_f64);
345
346 assert_eq!(py_f64, &f_64);
348 assert_eq!(&f_64, py_f64);
349
350 assert_eq!(py_f64_ref, f_64);
352 assert_eq!(f_64, py_f64_ref);
353
354 assert_eq!(py_f64_ref, &f_64);
356 assert_eq!(&f_64, py_f64_ref);
357
358 assert_eq!(py_f64_borrowed, f_64);
360 assert_eq!(f_64, py_f64_borrowed);
361
362 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 assert_eq!(py_f32, f_32);
373 assert_eq!(f_32, py_f32);
374
375 assert_eq!(py_f32, &f_32);
377 assert_eq!(&f_32, py_f32);
378
379 assert_eq!(py_f32_ref, f_32);
381 assert_eq!(f_32, py_f32_ref);
382
383 assert_eq!(py_f32_ref, &f_32);
385 assert_eq!(&f_32, py_f32_ref);
386
387 assert_eq!(py_f32_borrowed, f_32);
389 assert_eq!(f_32, py_f32_borrowed);
390
391 assert_eq!(py_f32_borrowed, &f_32);
393 assert_eq!(&f_32, py_f32_borrowed);
394 });
395 }
396}