1use super::any::PyAnyMethods;
2#[cfg(feature = "experimental-inspect")]
3use crate::inspect::types::TypeInfo;
4#[cfg(feature = "gil-refs")]
5use crate::PyNativeType;
6use crate::{
7 ffi, ffi_ptr_ext::FfiPtrExt, instance::Bound, FromPyObject, IntoPy, PyAny, PyErr, PyObject,
8 PyResult, Python, ToPyObject,
9};
10use std::os::raw::c_double;
11
12#[repr(transparent)]
24pub struct PyFloat(PyAny);
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_bound(py: Python<'_>, val: c_double) -> Bound<'_, PyFloat> {
36 unsafe {
37 ffi::PyFloat_FromDouble(val)
38 .assume_owned(py)
39 .downcast_into_unchecked()
40 }
41 }
42}
43
44#[cfg(feature = "gil-refs")]
45impl PyFloat {
46 #[inline]
48 #[deprecated(
49 since = "0.21.0",
50 note = "`PyFloat::new` will be replaced by `PyFloat::new_bound` in a future PyO3 version"
51 )]
52 pub fn new(py: Python<'_>, val: f64) -> &'_ Self {
53 Self::new_bound(py, val).into_gil_ref()
54 }
55
56 pub fn value(&self) -> c_double {
58 self.as_borrowed().value()
59 }
60}
61
62#[doc(alias = "PyFloat")]
68pub trait PyFloatMethods<'py>: crate::sealed::Sealed {
69 fn value(&self) -> c_double;
71}
72
73impl<'py> PyFloatMethods<'py> for Bound<'py, PyFloat> {
74 fn value(&self) -> c_double {
75 #[cfg(not(Py_LIMITED_API))]
76 unsafe {
77 ffi::PyFloat_AS_DOUBLE(self.as_ptr())
79 }
80
81 #[cfg(Py_LIMITED_API)]
82 unsafe {
83 ffi::PyFloat_AsDouble(self.as_ptr())
84 }
85 }
86}
87
88impl ToPyObject for f64 {
89 fn to_object(&self, py: Python<'_>) -> PyObject {
90 PyFloat::new_bound(py, *self).into()
91 }
92}
93
94impl IntoPy<PyObject> for f64 {
95 fn into_py(self, py: Python<'_>) -> PyObject {
96 PyFloat::new_bound(py, self).into()
97 }
98
99 #[cfg(feature = "experimental-inspect")]
100 fn type_output() -> TypeInfo {
101 TypeInfo::builtin("float")
102 }
103}
104
105impl<'py> FromPyObject<'py> for f64 {
106 #![allow(clippy::float_cmp)]
108 fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
109 #[cfg(not(Py_LIMITED_API))]
114 if let Ok(float) = obj.downcast_exact::<PyFloat>() {
115 return Ok(float.value());
116 }
117
118 let v = unsafe { ffi::PyFloat_AsDouble(obj.as_ptr()) };
119
120 if v == -1.0 {
121 if let Some(err) = PyErr::take(obj.py()) {
122 return Err(err);
123 }
124 }
125
126 Ok(v)
127 }
128
129 #[cfg(feature = "experimental-inspect")]
130 fn type_input() -> TypeInfo {
131 Self::type_output()
132 }
133}
134
135impl ToPyObject for f32 {
136 fn to_object(&self, py: Python<'_>) -> PyObject {
137 PyFloat::new_bound(py, f64::from(*self)).into()
138 }
139}
140
141impl IntoPy<PyObject> for f32 {
142 fn into_py(self, py: Python<'_>) -> PyObject {
143 PyFloat::new_bound(py, f64::from(self)).into()
144 }
145
146 #[cfg(feature = "experimental-inspect")]
147 fn type_output() -> TypeInfo {
148 TypeInfo::builtin("float")
149 }
150}
151
152impl<'py> FromPyObject<'py> for f32 {
153 fn extract_bound(obj: &Bound<'py, PyAny>) -> PyResult<Self> {
154 Ok(obj.extract::<f64>()? as f32)
155 }
156
157 #[cfg(feature = "experimental-inspect")]
158 fn type_input() -> TypeInfo {
159 Self::type_output()
160 }
161}
162
163#[cfg(test)]
164mod tests {
165 use crate::{
166 types::{PyFloat, PyFloatMethods},
167 Python, ToPyObject,
168 };
169
170 macro_rules! num_to_py_object_and_back (
171 ($func_name:ident, $t1:ty, $t2:ty) => (
172 #[test]
173 fn $func_name() {
174 use assert_approx_eq::assert_approx_eq;
175
176 Python::with_gil(|py| {
177
178 let val = 123 as $t1;
179 let obj = val.to_object(py);
180 assert_approx_eq!(obj.extract::<$t2>(py).unwrap(), val as $t2);
181 });
182 }
183 )
184 );
185
186 num_to_py_object_and_back!(to_from_f64, f64, f64);
187 num_to_py_object_and_back!(to_from_f32, f32, f32);
188 num_to_py_object_and_back!(int_to_float, i32, f64);
189
190 #[test]
191 fn test_float_value() {
192 use assert_approx_eq::assert_approx_eq;
193
194 Python::with_gil(|py| {
195 let v = 1.23f64;
196 let obj = PyFloat::new_bound(py, 1.23);
197 assert_approx_eq!(v, obj.value());
198 });
199 }
200}