rune/
floatfns.rs

1//! Operations on floats.
2use std::ops::{AddAssign, BitAnd, Div, Rem, SubAssign};
3
4use crate::{
5    arith::NumberValue,
6    core::{
7        cons::Cons,
8        gc::Context,
9        object::{Number, NumberType, Object},
10    },
11};
12use anyhow::Result;
13use anyhow::anyhow;
14use num_bigint::BigInt;
15use num_integer::Integer;
16use num_traits::{FromPrimitive, Signed, ToPrimitive, Zero};
17
18use rune_macros::defun;
19
20#[inline(always)]
21fn coerce(arg: Number) -> f64 {
22    match arg.untag() {
23        NumberType::Int(i) => i as f64,
24        NumberType::Float(f) => **f,
25        NumberType::Big(b) => b.to_f64().unwrap(), // TODO: Handle big integers
26    }
27}
28
29/* Return the integer exponent E such that D * FLT_RADIX**E (i.e.,
30scalbn (D, E)) is an integer that has precision equal to D and is
31representable as a double.
32
33Return DBL_MANT_DIG - DBL_MIN_EXP (the maximum possible valid
34scale) if D is zero or tiny.  Return one greater than that if
35D is infinite, and two greater than that if D is a NaN.  */
36fn double_integer_scale(d: f64) -> i64 {
37    let exponent = frexp_f(d).1 - 1;
38
39    if f64::MIN_EXP as i64 - 1 <= exponent && exponent < i64::MAX {
40        f64::MANTISSA_DIGITS as i64 - 1 - exponent
41    } else {
42        let x = f64::MANTISSA_DIGITS as i64 - f64::MIN_EXP as i64;
43        let c = if exponent == i64::MAX { 1 } else { 0 };
44        if d.is_nan() { x + 2 } else { x + c }
45    }
46}
47
48fn rescale_for_division(n: NumberValue, nscale: i64, dscale: i64) -> Result<BigInt> {
49    let mut result = match n {
50        NumberValue::Int(i) => BigInt::from(i),
51        NumberValue::Float(f) => {
52            if (f64::MANTISSA_DIGITS as i64 - f64::MIN_EXP as i64) < nscale {
53                return Err(anyhow!("Overflow error"));
54            }
55            BigInt::from_f64(libm::scalbn(f, nscale as i32)).expect("Conversion error")
56        }
57        NumberValue::Big(b) => b,
58    };
59
60    if nscale < dscale {
61        let power = (dscale - nscale) * f64::RADIX.ilog2() as i64;
62        result *= BigInt::from(2).pow(power as u32);
63    }
64
65    Ok(result)
66}
67
68fn rounding_driver(
69    n: NumberValue,
70    d: Option<NumberValue>,
71    double_round: fn(f64) -> f64,
72    int_divide: fn(i64, i64) -> i64,
73    bigum_divide: fn(BigInt, BigInt) -> BigInt,
74) -> Result<NumberValue> {
75    let d = match d {
76        None => {
77            return Ok(match n {
78                NumberValue::Float(f) => NumberValue::Float(double_round(f)).coerce_integer(),
79                other => other,
80            });
81        }
82        Some(d) if d.is_zero() => {
83            return Err(anyhow!("(arith-error)"));
84        }
85        Some(d) => d,
86    };
87
88    match (n, d) {
89        (NumberValue::Int(n), NumberValue::Int(d)) => Ok(NumberValue::Int(int_divide(n, d))),
90        (n, d) => {
91            let dscale = match d {
92                NumberValue::Float(f) => double_integer_scale(f),
93                _ => 0,
94            };
95
96            let nscale = match n {
97                NumberValue::Float(f) => double_integer_scale(f),
98                _ => 0,
99            };
100
101            /* If the numerator is finite and the denominator infinite, the
102            quotient is zero and there is no need to try the impossible task
103            of rescaling the denominator.  */
104            if dscale == f64::MANTISSA_DIGITS as i64 - f64::MIN_EXP as i64 + 1 && nscale < dscale {
105                return Ok(NumberValue::Int(0));
106            }
107
108            let num = rescale_for_division(n, nscale, dscale)?;
109            let denom = rescale_for_division(d, dscale, nscale)?;
110            Ok(NumberValue::Big(bigum_divide(num, denom)).coerce_integer())
111        }
112    }
113}
114
115fn round2<T>(num: T, den: T) -> T
116where
117    T: Div<Output = T>
118        + Rem<Output = T>
119        + PartialOrd
120        + num_traits::Zero
121        + num_traits::Signed
122        + BitAnd<Output = T>
123        + AddAssign
124        + SubAssign
125        + Clone,
126{
127    // The C language's division operator gives us the remainder R
128    // corresponding to truncated division, but we want the remainder R1
129    // on the other side of 0 if R1 is closer to 0 than R is; because we
130    // want to round to even, we also want R1 if R and R1 are the same
131    // distance from 0 and if the truncated quotient is odd.
132    let mut q: T = num.clone() / den.clone();
133    let r: T = num.clone() % den.clone();
134    let neg_d = den < T::zero();
135    let neg_r = r < T::zero();
136    let abs_r: T = r.abs();
137    let abs_r1: T = den.abs() - abs_r.clone();
138
139    let increment = if (q.clone() & T::one()) == T::one() { T::zero() } else { T::one() };
140    if abs_r1 < abs_r + increment {
141        if neg_d == neg_r {
142            q += T::one();
143        } else {
144            q -= T::one();
145        }
146    }
147    q
148}
149
150const LIMBS_LIMIT: usize = 2147483642;
151
152fn checked_pow(base: BigInt, exp: u32) -> Result<BigInt> {
153    // Check base size (number of limbs)
154    let nbase = base.bits() as usize; // Number of bits used by the base
155    let n = nbase * exp as usize; // Approximate number of limbs required for the result
156
157    // Check overflow condition
158    if n > LIMBS_LIMIT {
159        return Err(anyhow!("Overflow error".to_string()));
160    }
161    Ok(base.pow(exp))
162}
163
164#[defun]
165fn floor(num: Number, divisor: Option<Number>) -> Result<NumberValue> {
166    rounding_driver(
167        num.val(),
168        divisor.map(|d| d.val()),
169        |f| f.floor(),
170        |n, d| num_integer::Integer::div_floor(&n, &d),
171        |n, d| n.div_floor(&d),
172    )
173}
174
175#[defun]
176fn ceiling(num: Number, divisor: Option<Number>) -> Result<NumberValue> {
177    rounding_driver(
178        num.val(),
179        divisor.map(|d| d.val()),
180        |f| f.ceil(),
181        |n, d| num_integer::Integer::div_ceil(&n, &d),
182        |n, d| n.div_ceil(&d),
183    )
184}
185
186#[defun]
187fn round(num: Number, divisor: Option<Number>) -> Result<NumberValue> {
188    rounding_driver(
189        num.val(),
190        divisor.map(|d| d.val()), //
191        |f| f.round(),
192        round2,
193        round2,
194    )
195}
196
197#[defun]
198fn truncate(num: Number, divisor: Option<Number>) -> Result<NumberValue> {
199    rounding_driver(
200        num.val(),
201        divisor.map(|d| d.val()), //
202        |f| f,
203        |n, d| n.div(&d),
204        |n, d| n.div(&d),
205    )
206}
207
208#[defun]
209fn fceiling(num: f64) -> f64 {
210    num.ceil()
211}
212
213#[defun]
214fn ffloor(num: f64) -> f64 {
215    num.floor()
216}
217
218#[defun]
219fn fround(num: f64) -> f64 {
220    num.round()
221}
222
223#[defun]
224fn ftruncate(num: f64) -> f64 {
225    num.trunc()
226}
227
228#[defun]
229fn float(arg: Number) -> NumberValue {
230    NumberValue::Float(coerce(arg))
231}
232
233#[defun]
234fn asin(arg: Number) -> f64 {
235    coerce(arg).asin()
236}
237
238#[defun]
239fn acos(arg: Number) -> f64 {
240    coerce(arg).acos()
241}
242
243#[defun]
244fn atan(arg: Number, x: Option<f64>) -> f64 {
245    if let Some(x) = x { coerce(arg).atan2(x) } else { coerce(arg).atan() }
246}
247
248#[defun]
249fn cos(arg: Number) -> f64 {
250    coerce(arg).cos()
251}
252
253#[defun]
254fn sin(arg: Number) -> f64 {
255    coerce(arg).sin()
256}
257
258#[defun]
259fn tan(arg: Number) -> f64 {
260    coerce(arg).tan()
261}
262
263#[defun]
264fn isnan(arg: Number) -> bool {
265    match arg.untag() {
266        NumberType::Float(f) => f.is_nan(),
267        _ => false,
268    }
269}
270
271#[defun]
272fn copysign(x: f64, y: f64) -> f64 {
273    x.copysign(y)
274}
275
276#[defun]
277fn exp(arg: Number) -> f64 {
278    coerce(arg).exp()
279}
280
281#[defun]
282fn expt(x: Number, y: Number) -> NumberValue {
283    // If either is a float, we use the float version
284    match (x.untag(), y.untag()) {
285        (NumberType::Float(_), _) | (_, NumberType::Float(_)) => {
286            return NumberValue::Float(coerce(x).powf(coerce(y)));
287        }
288        (_, _) => {}
289    };
290
291    // Otherwise, we use the integer version
292    let bx = match x.untag() {
293        NumberType::Int(x) => BigInt::from(x),
294        NumberType::Big(b) => (*b).clone(),
295        NumberType::Float(x) => {
296            return NumberValue::Float(x.powf(coerce(y)));
297        }
298    };
299
300    let by = match y.untag() {
301        NumberType::Int(x) => u32::try_from(x).ok(),
302        NumberType::Big(b) => b.to_u32(),
303        NumberType::Float(y) => {
304            return NumberValue::Float(coerce(x).powf(**y));
305        }
306    };
307
308    if let Some(y) = by {
309        let result = checked_pow(bx, y).unwrap();
310        return NumberValue::Big(result).coerce_integer();
311    }
312    NumberValue::Float(coerce(x).powf(coerce(y)))
313}
314
315#[defun]
316fn log(arg: Number, base: Option<f64>) -> f64 {
317    if let Some(base) = base {
318        coerce(arg).log(base)
319    } else {
320        coerce(arg).log(std::f64::consts::E)
321    }
322}
323
324#[defun]
325fn sqrt(arg: Number) -> f64 {
326    coerce(arg).sqrt()
327}
328
329#[defun]
330fn abs(arg: Number) -> NumberValue {
331    match arg.untag() {
332        NumberType::Int(i) => NumberValue::Int(i.abs()),
333        NumberType::Float(f) => NumberValue::Float(f.abs()),
334        NumberType::Big(b) => NumberValue::Big(b.abs()),
335    }
336}
337
338#[defun]
339fn ldexp(s: Number, e: i64) -> f64 {
340    coerce(s) * 2f64.powi(e as i32)
341}
342
343#[defun]
344fn logb(arg: Number) -> i64 {
345    let l2 = coerce(arg).log2();
346    // Round down to an integer
347    l2.floor() as i64
348}
349
350#[defun]
351fn frexp<'ob>(x: Number, cx: &'ob Context) -> Object<'ob> {
352    let f = coerce(x);
353    let (significand, exponent) = frexp_f(f);
354    Cons::new(significand, exponent, cx).into()
355}
356
357fn frexp_f(f: f64) -> (f64, i64) {
358    let (x, exp) = libm::frexp(f);
359    (x, exp as i64)
360}