Add ArgFloatLike.

This commit is contained in:
jfh
2021-10-07 12:34:30 +03:00
parent acfdf9ceaa
commit 856f77fb77
8 changed files with 97 additions and 93 deletions

View File

@@ -12,11 +12,13 @@ mod array {
};
use crate::vm::{
builtins::{
IntoPyFloat, PyByteArray, PyBytes, PyBytesRef, PyIntRef, PyList, PyListRef, PySliceRef,
PyStr, PyStrRef, PyTypeRef,
PyByteArray, PyBytes, PyBytesRef, PyIntRef, PyList, PyListRef, PySliceRef, PyStr,
PyStrRef, PyTypeRef,
},
class_or_notimplemented,
function::{ArgBytesLike, ArgIterable, IntoPyObject, IntoPyResult, OptionalArg},
function::{
ArgBytesLike, ArgFloatLike, ArgIterable, IntoPyObject, IntoPyResult, OptionalArg,
},
protocol::{
BufferInternal, BufferOptions, BufferResizeGuard, PyBuffer, PyIterReturn,
PyMappingMethods,
@@ -521,11 +523,11 @@ mod array {
}
fn f32_try_into_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<f32> {
IntoPyFloat::try_from_object(vm, obj).map(|x| x.to_f64() as f32)
ArgFloatLike::try_from_object(vm, obj).map(|x| x.to_f64() as f32)
}
fn f64_try_into_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<f64> {
IntoPyFloat::try_from_object(vm, obj).map(|x| x.to_f64())
ArgFloatLike::try_from_object(vm, obj).map(|x| x.to_f64())
}
impl ArrayElement for WideChar {

View File

@@ -6,8 +6,7 @@ pub(crate) use cmath::make_module;
#[pymodule]
mod cmath {
use crate::vm::{
builtins::IntoPyFloat,
function::{ArgComplexLike, OptionalArg},
function::{ArgComplexLike, ArgFloatLike, OptionalArg},
PyResult, VirtualMachine,
};
use num_complex::Complex64;
@@ -38,7 +37,7 @@ mod cmath {
/// Convert from polar coordinates to rectangular coordinates.
#[pyfunction]
fn rect(r: IntoPyFloat, phi: IntoPyFloat) -> Complex64 {
fn rect(r: ArgFloatLike, phi: ArgFloatLike) -> Complex64 {
Complex64::from_polar(r.to_f64(), phi.to_f64())
}
@@ -172,9 +171,9 @@ mod cmath {
#[pyarg(positional)]
b: ArgComplexLike,
#[pyarg(named, optional)]
rel_tol: OptionalArg<IntoPyFloat>,
rel_tol: OptionalArg<ArgFloatLike>,
#[pyarg(named, optional)]
abs_tol: OptionalArg<IntoPyFloat>,
abs_tol: OptionalArg<ArgFloatLike>,
}
/// Determine whether two complex numbers are close in value.

View File

@@ -4,9 +4,9 @@ pub(crate) use math::make_module;
mod math {
use crate::vm::{
builtins::{
try_bigint_to_f64, try_f64_to_bigint, IntoPyFloat, PyFloatRef, PyInt, PyIntRef,
try_bigint_to_f64, try_f64_to_bigint, PyFloatRef, PyInt, PyIntRef,
},
function::{ArgIterable, OptionalArg, PosArgs},
function::{ArgFloatLike, ArgIterable, OptionalArg, PosArgs},
utils::Either,
PyObjectRef, PyResult, PySequence, TypeProtocol, VirtualMachine,
};
@@ -43,35 +43,35 @@ mod math {
// Number theory functions:
#[pyfunction]
fn fabs(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn fabs(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(abs, x, vm)
}
#[pyfunction]
fn isfinite(x: IntoPyFloat) -> bool {
fn isfinite(x: ArgFloatLike) -> bool {
x.to_f64().is_finite()
}
#[pyfunction]
fn isinf(x: IntoPyFloat) -> bool {
fn isinf(x: ArgFloatLike) -> bool {
x.to_f64().is_infinite()
}
#[pyfunction]
fn isnan(x: IntoPyFloat) -> bool {
fn isnan(x: ArgFloatLike) -> bool {
x.to_f64().is_nan()
}
#[derive(FromArgs)]
struct IsCloseArgs {
#[pyarg(positional)]
a: IntoPyFloat,
a: ArgFloatLike,
#[pyarg(positional)]
b: IntoPyFloat,
b: ArgFloatLike,
#[pyarg(named, optional)]
rel_tol: OptionalArg<IntoPyFloat>,
rel_tol: OptionalArg<ArgFloatLike>,
#[pyarg(named, optional)]
abs_tol: OptionalArg<IntoPyFloat>,
abs_tol: OptionalArg<ArgFloatLike>,
}
#[allow(clippy::float_cmp)]
@@ -110,7 +110,7 @@ mod math {
}
#[pyfunction]
fn copysign(x: IntoPyFloat, y: IntoPyFloat) -> f64 {
fn copysign(x: ArgFloatLike, y: ArgFloatLike) -> f64 {
let a = x.to_f64();
let b = y.to_f64();
if a.is_nan() || b.is_nan() {
@@ -122,37 +122,37 @@ mod math {
// Power and logarithmic functions:
#[pyfunction]
fn exp(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn exp(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(exp, x, vm)
}
#[pyfunction]
fn expm1(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn expm1(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(exp_m1, x, vm)
}
#[pyfunction]
fn log(x: IntoPyFloat, base: OptionalArg<IntoPyFloat>) -> f64 {
fn log(x: ArgFloatLike, base: OptionalArg<ArgFloatLike>) -> f64 {
base.map_or_else(|| x.to_f64().ln(), |base| x.to_f64().log(base.to_f64()))
}
#[pyfunction]
fn log1p(x: IntoPyFloat) -> f64 {
fn log1p(x: ArgFloatLike) -> f64 {
(x.to_f64() + 1.0).ln()
}
#[pyfunction]
fn log2(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn log2(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(log2, x, vm)
}
#[pyfunction]
fn log10(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn log10(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(log10, x, vm)
}
#[pyfunction]
fn pow(x: IntoPyFloat, y: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn pow(x: ArgFloatLike, y: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
let x = x.to_f64();
let y = y.to_f64();
@@ -170,7 +170,7 @@ mod math {
}
#[pyfunction]
fn sqrt(value: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn sqrt(value: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
let value = value.to_f64();
if value.is_sign_negative() {
return Err(vm.new_value_error("math domain error".to_owned()));
@@ -191,7 +191,7 @@ mod math {
// Trigonometric functions:
#[pyfunction]
fn acos(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn acos(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
let x = x.to_f64();
if x.is_nan() || (-1.0_f64..=1.0_f64).contains(&x) {
Ok(x.acos())
@@ -201,7 +201,7 @@ mod math {
}
#[pyfunction]
fn asin(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn asin(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
let x = x.to_f64();
if x.is_nan() || (-1.0_f64..=1.0_f64).contains(&x) {
Ok(x.asin())
@@ -211,23 +211,23 @@ mod math {
}
#[pyfunction]
fn atan(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn atan(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(atan, x, vm)
}
#[pyfunction]
fn atan2(y: IntoPyFloat, x: IntoPyFloat) -> f64 {
fn atan2(y: ArgFloatLike, x: ArgFloatLike) -> f64 {
y.to_f64().atan2(x.to_f64())
}
#[pyfunction]
fn cos(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn cos(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(cos, x, vm)
}
#[pyfunction]
fn hypot(coordinates: PosArgs<IntoPyFloat>) -> f64 {
let mut coordinates = IntoPyFloat::vec_into_f64(coordinates.into_vec());
fn hypot(coordinates: PosArgs<ArgFloatLike>) -> f64 {
let mut coordinates = ArgFloatLike::vec_into_f64(coordinates.into_vec());
let mut max = 0.0;
let mut has_nan = false;
for f in &mut coordinates {
@@ -267,15 +267,15 @@ mod math {
#[pyfunction]
fn dist(
p: PySequence<IntoPyFloat>,
q: PySequence<IntoPyFloat>,
p: PySequence<ArgFloatLike>,
q: PySequence<ArgFloatLike>,
vm: &VirtualMachine,
) -> PyResult<f64> {
let mut max = 0.0;
let mut has_nan = false;
let p = IntoPyFloat::vec_into_f64(p.into_vec());
let q = IntoPyFloat::vec_into_f64(q.into_vec());
let p = ArgFloatLike::vec_into_f64(p.into_vec());
let q = ArgFloatLike::vec_into_f64(q.into_vec());
let mut diffs = vec![];
if p.len() != q.len() {
@@ -309,29 +309,29 @@ mod math {
}
#[pyfunction]
fn sin(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn sin(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(sin, x, vm)
}
#[pyfunction]
fn tan(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn tan(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(tan, x, vm)
}
#[pyfunction]
fn degrees(x: IntoPyFloat) -> f64 {
fn degrees(x: ArgFloatLike) -> f64 {
x.to_f64() * (180.0 / std::f64::consts::PI)
}
#[pyfunction]
fn radians(x: IntoPyFloat) -> f64 {
fn radians(x: ArgFloatLike) -> f64 {
x.to_f64() * (std::f64::consts::PI / 180.0)
}
// Hyperbolic functions:
#[pyfunction]
fn acosh(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn acosh(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
let x = x.to_f64();
if x.is_sign_negative() || x.is_zero() {
Err(vm.new_value_error("math domain error".to_owned()))
@@ -341,33 +341,33 @@ mod math {
}
#[pyfunction]
fn asinh(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn asinh(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(asinh, x, vm)
}
#[pyfunction]
fn atanh(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn atanh(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(atanh, x, vm)
}
#[pyfunction]
fn cosh(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn cosh(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(cosh, x, vm)
}
#[pyfunction]
fn sinh(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn sinh(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(sinh, x, vm)
}
#[pyfunction]
fn tanh(x: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn tanh(x: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
call_math_func!(tanh, x, vm)
}
// Special functions:
#[pyfunction]
fn erf(x: IntoPyFloat) -> f64 {
fn erf(x: ArgFloatLike) -> f64 {
let x = x.to_f64();
if x.is_nan() {
x
@@ -377,7 +377,7 @@ mod math {
}
#[pyfunction]
fn erfc(x: IntoPyFloat) -> f64 {
fn erfc(x: ArgFloatLike) -> f64 {
let x = x.to_f64();
if x.is_nan() {
x
@@ -387,7 +387,7 @@ mod math {
}
#[pyfunction]
fn gamma(x: IntoPyFloat) -> f64 {
fn gamma(x: ArgFloatLike) -> f64 {
let x = x.to_f64();
if x.is_finite() {
puruspe::gamma(x)
@@ -399,7 +399,7 @@ mod math {
}
#[pyfunction]
fn lgamma(x: IntoPyFloat) -> f64 {
fn lgamma(x: ArgFloatLike) -> f64 {
let x = x.to_f64();
if x.is_finite() {
puruspe::ln_gamma(x)
@@ -451,7 +451,7 @@ mod math {
}
#[pyfunction]
fn frexp(x: IntoPyFloat) -> (f64, i32) {
fn frexp(x: ArgFloatLike) -> (f64, i32) {
let value = x.to_f64();
if value.is_finite() {
let (m, exp) = float_ops::ufrexp(value);
@@ -509,7 +509,7 @@ mod math {
}
#[pyfunction]
fn fsum(seq: ArgIterable<IntoPyFloat>, vm: &VirtualMachine) -> PyResult<f64> {
fn fsum(seq: ArgIterable<ArgFloatLike>, vm: &VirtualMachine) -> PyResult<f64> {
let mut partials = vec![];
let mut special_sum = 0.0;
let mut inf_sum = 0.0;
@@ -700,7 +700,7 @@ mod math {
}
#[pyfunction]
fn modf(x: IntoPyFloat) -> (f64, f64) {
fn modf(x: ArgFloatLike) -> (f64, f64) {
let x = x.to_f64();
if !x.is_finite() {
if x.is_infinite() {
@@ -714,12 +714,12 @@ mod math {
}
#[pyfunction]
fn nextafter(x: IntoPyFloat, y: IntoPyFloat) -> f64 {
fn nextafter(x: ArgFloatLike, y: ArgFloatLike) -> f64 {
float_ops::nextafter(x.to_f64(), y.to_f64())
}
#[pyfunction]
fn ulp(x: IntoPyFloat) -> f64 {
fn ulp(x: ArgFloatLike) -> f64 {
float_ops::ulp(x.to_f64())
}
@@ -732,7 +732,7 @@ mod math {
}
#[pyfunction(name = "fmod")]
fn py_fmod(x: IntoPyFloat, y: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn py_fmod(x: ArgFloatLike, y: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
let x = x.to_f64();
let y = y.to_f64();
@@ -746,7 +746,7 @@ mod math {
}
#[pyfunction]
fn remainder(x: IntoPyFloat, y: IntoPyFloat, vm: &VirtualMachine) -> PyResult<f64> {
fn remainder(x: ArgFloatLike, y: ArgFloatLike, vm: &VirtualMachine) -> PyResult<f64> {
let x = x.to_f64();
let y = y.to_f64();

View File

@@ -545,33 +545,6 @@ pub(crate) fn get_value(obj: &PyObjectRef) -> f64 {
obj.payload::<PyFloat>().unwrap().value
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(transparent)]
pub struct IntoPyFloat {
value: f64,
}
impl IntoPyFloat {
pub fn to_f64(self) -> f64 {
self.value
}
pub fn vec_into_f64(v: Vec<Self>) -> Vec<f64> {
// TODO: Vec::into_raw_parts once stabilized
let mut v = std::mem::ManuallyDrop::new(v);
let (p, l, c) = (v.as_mut_ptr(), v.len(), v.capacity());
// SAFETY: IntoPyFloat is repr(transparent) over f64
unsafe { Vec::from_raw_parts(p.cast(), l, c) }
}
}
impl TryFromObject for IntoPyFloat {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
let value = try_float(&obj, vm)?;
Ok(IntoPyFloat { value })
}
}
#[rustfmt::skip] // to avoid line splitting
pub fn init(context: &PyContext) {
PyFloat::extend_class(context, &context.types.float_type);

View File

@@ -22,7 +22,7 @@ pub use enumerate::PyEnumerate;
pub(crate) mod filter;
pub use filter::PyFilter;
pub(crate) mod float;
pub use float::{IntoPyFloat, PyFloat, PyFloatRef};
pub use float::{PyFloat, PyFloatRef};
pub(crate) mod frame;
pub(crate) mod function;
pub use function::{PyBoundMethod, PyFunction};

View File

@@ -3,7 +3,8 @@
use crate::common::float_ops;
use crate::{
builtins::{try_f64_to_bigint, tuple, IntoPyFloat, PyBytes, PyFloat, PyInt, PyStr},
builtins::{try_f64_to_bigint, tuple, PyBytes, PyFloat, PyInt, PyStr},
function::ArgFloatLike,
protocol::PyBuffer,
ItemProtocol, PyObjectRef, PyResult, TryFromBorrowedObject, TryFromObject, TypeProtocol,
VirtualMachine,
@@ -431,7 +432,7 @@ impl CFormatSpec {
}
},
CFormatType::Float(_) => {
let value = IntoPyFloat::try_from_object(vm, obj)?.to_f64();
let value = ArgFloatLike::try_from_object(vm, obj)?.to_f64();
Ok(self.format_float(value).into_bytes())
}
CFormatType::Character => {
@@ -504,7 +505,7 @@ impl CFormatSpec {
}
},
CFormatType::Float(_) => {
let value = IntoPyFloat::try_from_object(vm, obj)?.to_f64();
let value = ArgFloatLike::try_from_object(vm, obj)?.to_f64();
Ok(self.format_float(value))
}
CFormatType::Character => {

View File

@@ -15,7 +15,7 @@ use std::ops::RangeInclusive;
pub use argument::{ArgCallable, ArgIterable};
pub use byteslike::{ArgBytesLike, ArgMemoryBuffer, ArgStrOrBytesLike};
pub use numlike::ArgComplexLike;
pub use numlike::{ArgComplexLike, ArgFloatLike};
/// Implemented by any type that can be returned from a built-in Python function.
///

View File

@@ -22,3 +22,32 @@ impl TryFromObject for ArgComplexLike {
Ok(ArgComplexLike { value })
}
}
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(transparent)]
pub struct ArgFloatLike {
value: f64,
}
impl ArgFloatLike {
pub fn to_f64(self) -> f64 {
self.value
}
pub fn vec_into_f64(v: Vec<Self>) -> Vec<f64> {
// TODO: Vec::into_raw_parts once stabilized
let mut v = std::mem::ManuallyDrop::new(v);
let (p, l, c) = (v.as_mut_ptr(), v.len(), v.capacity());
// SAFETY: IntoPyFloat is repr(transparent) over f64
unsafe { Vec::from_raw_parts(p.cast(), l, c) }
}
}
impl TryFromObject for ArgFloatLike {
fn try_from_object(vm: &VirtualMachine, obj: PyObjectRef) -> PyResult<Self> {
let value = obj.try_to_f64(vm)?.ok_or_else(|| {
vm.new_type_error(format!("must be real number, not {}", obj.class().name()))
})?;
Ok(ArgFloatLike { value })
}
}