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