1use crate::conversion::IntoPyObject;
2#[cfg(feature = "experimental-inspect")]
3use crate::inspect::types::TypeInfo;
4use crate::{
5 ffi, ffi_ptr_ext::FfiPtrExt, instance::Bound, Borrowed, FromPyObject, PyAny, PyErr, Python,
6};
7use std::convert::Infallible;
8use std::ffi::c_double;
9
10#[repr(transparent)]
22pub struct PyFloat(PyAny);
23
24pyobject_subclassable_native_type!(PyFloat, crate::ffi::PyFloatObject);
25
26pyobject_native_type!(
27 PyFloat,
28 ffi::PyFloatObject,
29 pyobject_native_static_type_object!(ffi::PyFloat_Type),
30 #checkfunction=ffi::PyFloat_Check
31);
32
33impl PyFloat {
34 pub fn new(py: Python<'_>, val: c_double) -> Bound<'_, PyFloat> {
36 unsafe {
37 ffi::PyFloat_FromDouble(val)
38 .assume_owned(py)
39 .cast_into_unchecked()
40 }
41 }
42}
43
44#[doc(alias = "PyFloat")]
50pub trait PyFloatMethods<'py>: crate::sealed::Sealed {
51 fn value(&self) -> c_double;
53}
54
55impl<'py> PyFloatMethods<'py> for Bound<'py, PyFloat> {
56 fn value(&self) -> c_double {
57 #[cfg(not(Py_LIMITED_API))]
58 unsafe {
59 ffi::PyFloat_AS_DOUBLE(self.as_ptr())
61 }
62
63 #[cfg(Py_LIMITED_API)]
64 unsafe {
65 ffi::PyFloat_AsDouble(self.as_ptr())
66 }
67 }
68}
69
70impl<'py> IntoPyObject<'py> for f64 {
71 type Target = PyFloat;
72 type Output = Bound<'py, Self::Target>;
73 type Error = Infallible;
74
75 #[cfg(feature = "experimental-inspect")]
76 const OUTPUT_TYPE: &'static str = "float";
77
78 #[inline]
79 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
80 Ok(PyFloat::new(py, self))
81 }
82
83 #[cfg(feature = "experimental-inspect")]
84 fn type_output() -> TypeInfo {
85 TypeInfo::builtin("float")
86 }
87}
88
89impl<'py> IntoPyObject<'py> for &f64 {
90 type Target = PyFloat;
91 type Output = Bound<'py, Self::Target>;
92 type Error = Infallible;
93
94 #[cfg(feature = "experimental-inspect")]
95 const OUTPUT_TYPE: &'static str = f64::OUTPUT_TYPE;
96
97 #[inline]
98 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
99 (*self).into_pyobject(py)
100 }
101
102 #[cfg(feature = "experimental-inspect")]
103 fn type_output() -> TypeInfo {
104 TypeInfo::builtin("float")
105 }
106}
107
108impl<'py> FromPyObject<'_, 'py> for f64 {
109 type Error = PyErr;
110
111 #[cfg(feature = "experimental-inspect")]
112 const INPUT_TYPE: &'static str = "float";
113
114 #[allow(clippy::float_cmp)]
116 fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
117 #[cfg(not(Py_LIMITED_API))]
122 if let Ok(float) = obj.cast_exact::<PyFloat>() {
123 return Ok(float.value());
124 }
125
126 let v = unsafe { ffi::PyFloat_AsDouble(obj.as_ptr()) };
127
128 if v == -1.0 {
129 if let Some(err) = PyErr::take(obj.py()) {
130 return Err(err);
131 }
132 }
133
134 Ok(v)
135 }
136
137 #[cfg(feature = "experimental-inspect")]
138 fn type_input() -> TypeInfo {
139 Self::type_output()
140 }
141}
142
143impl<'py> IntoPyObject<'py> for f32 {
144 type Target = PyFloat;
145 type Output = Bound<'py, Self::Target>;
146 type Error = Infallible;
147
148 #[cfg(feature = "experimental-inspect")]
149 const OUTPUT_TYPE: &'static str = "float";
150
151 #[inline]
152 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
153 Ok(PyFloat::new(py, self.into()))
154 }
155
156 #[cfg(feature = "experimental-inspect")]
157 fn type_output() -> TypeInfo {
158 TypeInfo::builtin("float")
159 }
160}
161
162impl<'py> IntoPyObject<'py> for &f32 {
163 type Target = PyFloat;
164 type Output = Bound<'py, Self::Target>;
165 type Error = Infallible;
166
167 #[cfg(feature = "experimental-inspect")]
168 const OUTPUT_TYPE: &'static str = f32::OUTPUT_TYPE;
169
170 #[inline]
171 fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
172 (*self).into_pyobject(py)
173 }
174
175 #[cfg(feature = "experimental-inspect")]
176 fn type_output() -> TypeInfo {
177 TypeInfo::builtin("float")
178 }
179}
180
181impl<'a, 'py> FromPyObject<'a, 'py> for f32 {
182 type Error = <f64 as FromPyObject<'a, 'py>>::Error;
183
184 #[cfg(feature = "experimental-inspect")]
185 const INPUT_TYPE: &'static str = "float";
186
187 fn extract(obj: Borrowed<'_, 'py, PyAny>) -> Result<Self, Self::Error> {
188 Ok(obj.extract::<f64>()? as f32)
189 }
190
191 #[cfg(feature = "experimental-inspect")]
192 fn type_input() -> TypeInfo {
193 Self::type_output()
194 }
195}
196
197macro_rules! impl_partial_eq_for_float {
198 ($float_type: ty) => {
199 impl PartialEq<$float_type> for Bound<'_, PyFloat> {
200 #[inline]
201 fn eq(&self, other: &$float_type) -> bool {
202 self.value() as $float_type == *other
203 }
204 }
205
206 impl PartialEq<$float_type> for &Bound<'_, PyFloat> {
207 #[inline]
208 fn eq(&self, other: &$float_type) -> bool {
209 self.value() as $float_type == *other
210 }
211 }
212
213 impl PartialEq<&$float_type> for Bound<'_, PyFloat> {
214 #[inline]
215 fn eq(&self, other: &&$float_type) -> bool {
216 self.value() as $float_type == **other
217 }
218 }
219
220 impl PartialEq<Bound<'_, PyFloat>> for $float_type {
221 #[inline]
222 fn eq(&self, other: &Bound<'_, PyFloat>) -> bool {
223 other.value() as $float_type == *self
224 }
225 }
226
227 impl PartialEq<&'_ Bound<'_, PyFloat>> for $float_type {
228 #[inline]
229 fn eq(&self, other: &&'_ Bound<'_, PyFloat>) -> bool {
230 other.value() as $float_type == *self
231 }
232 }
233
234 impl PartialEq<Bound<'_, PyFloat>> for &'_ $float_type {
235 #[inline]
236 fn eq(&self, other: &Bound<'_, PyFloat>) -> bool {
237 other.value() as $float_type == **self
238 }
239 }
240
241 impl PartialEq<$float_type> for Borrowed<'_, '_, PyFloat> {
242 #[inline]
243 fn eq(&self, other: &$float_type) -> bool {
244 self.value() as $float_type == *other
245 }
246 }
247
248 impl PartialEq<&$float_type> for Borrowed<'_, '_, PyFloat> {
249 #[inline]
250 fn eq(&self, other: &&$float_type) -> bool {
251 self.value() as $float_type == **other
252 }
253 }
254
255 impl PartialEq<Borrowed<'_, '_, PyFloat>> for $float_type {
256 #[inline]
257 fn eq(&self, other: &Borrowed<'_, '_, PyFloat>) -> bool {
258 other.value() as $float_type == *self
259 }
260 }
261
262 impl PartialEq<Borrowed<'_, '_, PyFloat>> for &$float_type {
263 #[inline]
264 fn eq(&self, other: &Borrowed<'_, '_, PyFloat>) -> bool {
265 other.value() as $float_type == **self
266 }
267 }
268 };
269}
270
271impl_partial_eq_for_float!(f64);
272impl_partial_eq_for_float!(f32);
273
274#[cfg(test)]
275mod tests {
276 use crate::{
277 conversion::IntoPyObject,
278 types::{PyAnyMethods, PyFloat, PyFloatMethods},
279 Python,
280 };
281
282 macro_rules! num_to_py_object_and_back (
283 ($func_name:ident, $t1:ty, $t2:ty) => (
284 #[test]
285 fn $func_name() {
286 use assert_approx_eq::assert_approx_eq;
287
288 Python::attach(|py| {
289
290 let val = 123 as $t1;
291 let obj = val.into_pyobject(py).unwrap();
292 assert_approx_eq!(obj.extract::<$t2>().unwrap(), val as $t2);
293 });
294 }
295 )
296 );
297
298 num_to_py_object_and_back!(to_from_f64, f64, f64);
299 num_to_py_object_and_back!(to_from_f32, f32, f32);
300 num_to_py_object_and_back!(int_to_float, i32, f64);
301
302 #[test]
303 fn test_float_value() {
304 use assert_approx_eq::assert_approx_eq;
305
306 Python::attach(|py| {
307 let v = 1.23f64;
308 let obj = PyFloat::new(py, 1.23);
309 assert_approx_eq!(v, obj.value());
310 });
311 }
312
313 #[test]
314 fn test_pyfloat_comparisons() {
315 Python::attach(|py| {
316 let f_64 = 1.01f64;
317 let py_f64 = PyFloat::new(py, 1.01);
318 let py_f64_ref = &py_f64;
319 let py_f64_borrowed = py_f64.as_borrowed();
320
321 assert_eq!(py_f64, f_64);
323 assert_eq!(f_64, py_f64);
324
325 assert_eq!(py_f64, &f_64);
327 assert_eq!(&f_64, py_f64);
328
329 assert_eq!(py_f64_ref, f_64);
331 assert_eq!(f_64, py_f64_ref);
332
333 assert_eq!(py_f64_ref, &f_64);
335 assert_eq!(&f_64, py_f64_ref);
336
337 assert_eq!(py_f64_borrowed, f_64);
339 assert_eq!(f_64, py_f64_borrowed);
340
341 assert_eq!(py_f64_borrowed, &f_64);
343 assert_eq!(&f_64, py_f64_borrowed);
344
345 let f_32 = 2.02f32;
346 let py_f32 = PyFloat::new(py, 2.02);
347 let py_f32_ref = &py_f32;
348 let py_f32_borrowed = py_f32.as_borrowed();
349
350 assert_eq!(py_f32, f_32);
352 assert_eq!(f_32, py_f32);
353
354 assert_eq!(py_f32, &f_32);
356 assert_eq!(&f_32, py_f32);
357
358 assert_eq!(py_f32_ref, f_32);
360 assert_eq!(f_32, py_f32_ref);
361
362 assert_eq!(py_f32_ref, &f_32);
364 assert_eq!(&f_32, py_f32_ref);
365
366 assert_eq!(py_f32_borrowed, f_32);
368 assert_eq!(f_32, py_f32_borrowed);
369
370 assert_eq!(py_f32_borrowed, &f_32);
372 assert_eq!(&f_32, py_f32_borrowed);
373 });
374 }
375}