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