scratch_test_value/arithmetic/division.rs
1use crate::{
2 QuirkSink, SNumber, SValue,
3 quirks::{SNumberToFloatQ, SValueToNumberQ},
4};
5
6impl SValue {
7 /// See [`SNumber::q_div_numbers`]
8 pub fn q_div_numbers<Q>(&self, other: &SValue, sink: &mut Q) -> SValue
9 where
10 Q: QuirkSink<SValueToNumberQ> + QuirkSink<SNumberToFloatQ>,
11 {
12 self.q_as_number(sink)
13 .q_div_numbers(&other.q_as_number(sink), sink)
14 .svalue()
15 }
16}
17
18impl SNumber {
19 /// Integers are automatically divided as floats (if the remainder != 0) but I think this
20 /// shouldn't be considered a quirk as some people would actually like it (?)
21 ///
22 /// The division rules are not necessarily identical to IEEE 754.
23 ///
24 /// - n·m / m → n (integer division without remainder)
25 ///
26 /// If both numbers are finite integers and the division would have a remainder
27 /// of 0 the numbers are divided as integers.
28 /// Otherwise, the below table applys. p and q are arbitraty finite floats.
29 ///
30 /// (line / column is shown in cell)
31 ///
32 /// | `/` | | `NaN` | `+∞` | `-∞` | +q | -q | `+0` | `-0` |
33 /// | ----- | -- | ----- | ----- | ----- | ---- | ---- | ----- | ----- |
34 /// | | | | | | | | | |
35 /// | `NaN` | | `NaN` | `+0` | `-0` | `+0` | `-0` | `NaN` | `NaN` |
36 /// | `+∞` | | `+∞` | `NaN` | `NaN` | `+∞` | `-∞` | `+∞` | `-∞` |
37 /// | `-∞` | | `-∞` | `NaN` | `NaN` | `-∞` | `+∞` | `-∞` | `+∞` |
38 /// | +p | | `+∞` | `+0` | `-0` | p/q | -p/q | `+∞` | `-∞` |
39 /// | -p | | `-∞` | `-0` | `+0` | -p/q | p/q | `-∞` | `+∞` |
40 /// | `+0` | | `NaN` | `+0` | `-0` | `+0` | `-0` | `NaN` | `NaN` |
41 /// | `-0` | | `NaN` | `-0` | `+0` | `-0` | `+0` | `NaN` | `NaN` |
42 pub fn q_div_numbers<Q>(&self, other: &SNumber, sink: &mut Q) -> SNumber
43 where
44 Q: QuirkSink<SNumberToFloatQ>,
45 {
46 if let (SNumber::Int(a), SNumber::Int(b)) = (self, other)
47 && a.checked_rem(*b) == Some(0)
48 && let Some(res) = a.checked_div(*b)
49 {
50 return SNumber::Int(res);
51 }
52
53 let a = self.q_as_float(sink);
54 let b = other.q_as_float(sink);
55
56 if a.is_nan() && b.is_nan() {
57 return SNumber::NAN;
58 }
59
60 if (a == 0.0 || a == -0.0) && b.is_nan() {
61 return Self::NAN;
62 }
63 if a.is_nan() && (b == 0.0 || b == -0.0) {
64 return Self::NAN;
65 }
66
67 if b.is_nan() {
68 return if a.is_sign_positive() {
69 SNumber::INFINITY
70 } else {
71 SNumber::NEG_INFINITY
72 };
73 }
74 if a.is_nan() && b.is_infinite() {
75 return SNumber::Float(if b.is_sign_positive() { 0.0 } else { -0.0 });
76 }
77 if a.is_nan() && b.is_finite() {
78 return SNumber::Float(if b.is_sign_positive() { 0.0 } else { -0.0 });
79 }
80
81 SNumber::Float(a / b)
82 }
83}