Remove lossy casting in logspace

Currently `logspace` does a lossy cast from `F::Int` to `usize`. This
could be problematic in the rare cases that this is called with a step
count exceeding what is representable in `usize`.

Resolve this by instead adding bounds so the float's integer type itself
can be iterated.
This commit is contained in:
Trevor Gross
2024-12-30 05:55:41 +00:00
committed by Trevor Gross
parent e8c501861a
commit cf58a7ce90
2 changed files with 10 additions and 3 deletions

View File

@@ -1,5 +1,7 @@
//! A generator that produces logarithmically spaced values within domain bounds. //! A generator that produces logarithmically spaced values within domain bounds.
use std::ops::RangeInclusive;
use libm::support::{IntTy, MinInt}; use libm::support::{IntTy, MinInt};
use crate::domain::HasDomain; use crate::domain::HasDomain;
@@ -34,6 +36,7 @@ pub fn get_test_cases<Op>(_ctx: &CheckCtx) -> impl Iterator<Item = (Op::FTy,)>
where where
Op: MathOp + HasDomain<Op::FTy>, Op: MathOp + HasDomain<Op::FTy>,
IntTy<Op::FTy>: TryFrom<usize>, IntTy<Op::FTy>: TryFrom<usize>,
RangeInclusive<IntTy<Op::FTy>>: Iterator,
{ {
let domain = Op::DOMAIN; let domain = Op::DOMAIN;
let start = domain.range_start(); let start = domain.range_start();

View File

@@ -1,8 +1,9 @@
//! Helpful numeric operations. //! Helpful numeric operations.
use std::cmp::min; use std::cmp::min;
use std::ops::RangeInclusive;
use libm::support::{CastInto, Float}; use libm::support::Float;
use crate::{Int, MinInt}; use crate::{Int, MinInt};
@@ -214,7 +215,10 @@ fn as_ulp_steps<F: Float>(x: F) -> Option<F::SignedInt> {
/// to logarithmic spacing of their values. /// to logarithmic spacing of their values.
/// ///
/// Note that this tends to skip negative zero, so that needs to be checked explicitly. /// Note that this tends to skip negative zero, so that needs to be checked explicitly.
pub fn logspace<F: FloatExt>(start: F, end: F, steps: F::Int) -> impl Iterator<Item = F> { pub fn logspace<F: FloatExt>(start: F, end: F, steps: F::Int) -> impl Iterator<Item = F>
where
RangeInclusive<F::Int>: Iterator,
{
assert!(!start.is_nan()); assert!(!start.is_nan());
assert!(!end.is_nan()); assert!(!end.is_nan());
assert!(end >= start); assert!(end >= start);
@@ -225,7 +229,7 @@ pub fn logspace<F: FloatExt>(start: F, end: F, steps: F::Int) -> impl Iterator<I
steps = steps.min(between); // At maximum, one step per ULP steps = steps.min(between); // At maximum, one step per ULP
let mut x = start; let mut x = start;
(0..=steps.cast()).map(move |_| { (F::Int::ZERO..=steps).map(move |_| {
let ret = x; let ret = x;
x = x.n_up(spacing); x = x.n_up(spacing);
ret ret