Skip to content

Commit 343e091

Browse files
committed
Draft: Fix {f16,f32,f64,f128}::div_euclid
The current implementation of `div_euclid` for floating point numbers violates the invariant, stated in the documentation, that: ```rust a.rem_euclid(b) ~= a.div_euclid(b).mul_add(-b, a) ``` This fixes that problem. When the magnitude of the exact quotient is greater than or equal to the maximum integer that can be represented precisely in the type, that invariant is not generally possible to uphold -- and without control over the rounding mode it would be difficult to calculate in any case -- so we return `NaN` in those instances.
1 parent 1b3fb31 commit 343e091

File tree

4 files changed

+234
-22
lines changed

4 files changed

+234
-22
lines changed

library/std/src/f128.rs

Lines changed: 61 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -236,15 +236,19 @@ impl f128 {
236236
/// Calculates Euclidean division, the matching method for `rem_euclid`.
237237
///
238238
/// This computes the integer `n` such that
239-
/// `self = n * rhs + self.rem_euclid(rhs)`.
240-
/// In other words, the result is `self / rhs` rounded to the integer `n`
241-
/// such that `self >= n * rhs`.
239+
/// `self.rem_euclid(rhs) = self.div_euclid(rhs).mul_add(-rhs, self)`.
240+
/// In other words, the result is `self / rhs` rounded to the largest
241+
/// integer `n` such that `self >= n * rhs`.
242242
///
243243
/// # Precision
244244
///
245245
/// The result of this operation is guaranteed to be the rounded
246246
/// infinite-precision result.
247247
///
248+
/// If the magnitude of the exact quotient `self / rhs` is greater than or
249+
/// equal to the maximum integer that can be represented precisely with this
250+
/// type, then `NaN` may be returned.
251+
///
248252
/// # Examples
249253
///
250254
/// ```
@@ -259,16 +263,67 @@ impl f128 {
259263
/// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0
260264
/// # }
261265
/// ```
266+
///
267+
/// ```
268+
/// #![feature(f128)]
269+
/// # #[cfg(reliable_f128_math)] {
270+
///
271+
/// let a: f32 = 11.0;
272+
/// let b = 1.1;
273+
/// assert_eq!(a.div_euclid(b), 9.0);
274+
/// assert_eq!(a.rem_euclid(b), a.div_euclid(b).mul_add(-b, a));
275+
/// # }
276+
/// ```
262277
#[inline]
263278
#[rustc_allow_incoherent_impl]
264279
#[unstable(feature = "f128", issue = "116909")]
265280
#[must_use = "method returns a new number and does not mutate the original value"]
266281
pub fn div_euclid(self, rhs: f128) -> f128 {
267-
let q = (self / rhs).trunc();
282+
use core::ops::ControlFlow;
283+
284+
fn xor_sign(x: f128, y: f128, v: f128) -> f128 {
285+
match () {
286+
_ if x.is_sign_positive() == y.is_sign_positive() => v,
287+
_ => -v,
288+
}
289+
}
290+
291+
fn div_special(x: f128, y: f128) -> ControlFlow<f128> {
292+
ControlFlow::Break(match (x, y) {
293+
(x, y) if x.is_nan() || y.is_nan() => f128::NAN,
294+
(x, y) if x == 0.0 && y == 0.0 => f128::NAN,
295+
(x, y) if x == 0.0 => xor_sign(x, y, 0.0),
296+
(x, y) if y == 0.0 => xor_sign(x, y, f128::INFINITY),
297+
(x, y) if x.is_infinite() && y.is_infinite() => f128::NAN,
298+
(x, y) if x.is_infinite() => xor_sign(x, y, f128::INFINITY),
299+
(x, y) if y.is_infinite() => xor_sign(x, y, 0.0),
300+
_ => return ControlFlow::Continue(()),
301+
})
302+
}
303+
304+
fn div_trunc(x: f128, y: f128) -> f128 {
305+
const MAX_EXACT_Q: f128 = ((1u128 << f128::MANTISSA_DIGITS) - 1) as f128;
306+
if let ControlFlow::Break(q) = div_special(x, y) {
307+
return q;
308+
}
309+
let sign = xor_sign(x, y, 1.0);
310+
let (x, y) = (x.abs(), y.abs());
311+
let q = x / y;
312+
if q > MAX_EXACT_Q {
313+
return f128::NAN;
314+
}
315+
let qt = q.trunc();
316+
if qt.mul_add(-y, x).is_sign_negative() { qt - 1.0 } else { qt }.copysign(sign)
317+
}
318+
319+
if let ControlFlow::Break(q) = div_special(self, rhs) {
320+
return q;
321+
}
322+
let qt = div_trunc(self, rhs);
268323
if self % rhs < 0.0 {
269-
return if rhs > 0.0 { q - 1.0 } else { q + 1.0 };
324+
return if rhs > 0.0 { qt - 1.0 } else { qt + 1.0 };
270325
}
271-
q
326+
qt
272327
}
273328

274329
/// Calculates the least nonnegative remainder of `self (mod rhs)`.

library/std/src/f16.rs

Lines changed: 59 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -236,7 +236,7 @@ impl f16 {
236236
/// Calculates Euclidean division, the matching method for `rem_euclid`.
237237
///
238238
/// This computes the integer `n` such that
239-
/// `self = n * rhs + self.rem_euclid(rhs)`.
239+
/// `self.rem_euclid(rhs) = self.div_euclid(rhs).mul_add(-rhs, self)`.
240240
/// In other words, the result is `self / rhs` rounded to the integer `n`
241241
/// such that `self >= n * rhs`.
242242
///
@@ -245,6 +245,10 @@ impl f16 {
245245
/// The result of this operation is guaranteed to be the rounded
246246
/// infinite-precision result.
247247
///
248+
/// If the magnitude of the exact quotient `self / rhs` is greater than or
249+
/// equal to the maximum integer that can be represented precisely with this
250+
/// type, then `NaN` may be returned.
251+
///
248252
/// # Examples
249253
///
250254
/// ```
@@ -259,16 +263,67 @@ impl f16 {
259263
/// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0
260264
/// # }
261265
/// ```
266+
///
267+
/// ```
268+
/// #![feature(f16)]
269+
/// # #[cfg(reliable_f16_math)] {
270+
///
271+
/// let a: f16 = 11.0;
272+
/// let b = 1.1;
273+
/// assert_eq!(a.div_euclid(b), 9.0);
274+
/// assert_eq!(a.rem_euclid(b), a.div_euclid(b).mul_add(-b, a));
275+
/// # }
276+
/// ```
262277
#[inline]
263278
#[rustc_allow_incoherent_impl]
264279
#[unstable(feature = "f16", issue = "116909")]
265280
#[must_use = "method returns a new number and does not mutate the original value"]
266281
pub fn div_euclid(self, rhs: f16) -> f16 {
267-
let q = (self / rhs).trunc();
282+
use core::ops::ControlFlow;
283+
284+
fn xor_sign(x: f16, y: f16, v: f16) -> f16 {
285+
match () {
286+
_ if x.is_sign_positive() == y.is_sign_positive() => v,
287+
_ => -v,
288+
}
289+
}
290+
291+
fn div_special(x: f16, y: f16) -> ControlFlow<f16> {
292+
ControlFlow::Break(match (x, y) {
293+
(x, y) if x.is_nan() || y.is_nan() => f16::NAN,
294+
(x, y) if x == 0.0 && y == 0.0 => f16::NAN,
295+
(x, y) if x == 0.0 => xor_sign(x, y, 0.0),
296+
(x, y) if y == 0.0 => xor_sign(x, y, f16::INFINITY),
297+
(x, y) if x.is_infinite() && y.is_infinite() => f16::NAN,
298+
(x, y) if x.is_infinite() => xor_sign(x, y, f16::INFINITY),
299+
(x, y) if y.is_infinite() => xor_sign(x, y, 0.0),
300+
_ => return ControlFlow::Continue(()),
301+
})
302+
}
303+
304+
fn div_trunc(x: f16, y: f16) -> f16 {
305+
const MAX_EXACT_Q: f16 = ((1u64 << f16::MANTISSA_DIGITS) - 1) as f16;
306+
if let ControlFlow::Break(q) = div_special(x, y) {
307+
return q;
308+
}
309+
let sign = xor_sign(x, y, 1.0);
310+
let (x, y) = (x.abs(), y.abs());
311+
let q = x / y;
312+
if q > MAX_EXACT_Q {
313+
return f16::NAN;
314+
}
315+
let qt = q.trunc();
316+
if qt.mul_add(-y, x).is_sign_negative() { qt - 1.0 } else { qt }.copysign(sign)
317+
}
318+
319+
if let ControlFlow::Break(q) = div_special(self, rhs) {
320+
return q;
321+
}
322+
let qt = div_trunc(self, rhs);
268323
if self % rhs < 0.0 {
269-
return if rhs > 0.0 { q - 1.0 } else { q + 1.0 };
324+
return if rhs > 0.0 { qt - 1.0 } else { qt + 1.0 };
270325
}
271-
q
326+
qt
272327
}
273328

274329
/// Calculates the least nonnegative remainder of `self (mod rhs)`.

library/std/src/f32.rs

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -220,15 +220,19 @@ impl f32 {
220220
/// Calculates Euclidean division, the matching method for `rem_euclid`.
221221
///
222222
/// This computes the integer `n` such that
223-
/// `self = n * rhs + self.rem_euclid(rhs)`.
224-
/// In other words, the result is `self / rhs` rounded to the integer `n`
225-
/// such that `self >= n * rhs`.
223+
/// `self.rem_euclid(rhs) = self.div_euclid(rhs).mul_add(-rhs, self)`.
224+
/// In other words, the result is `self / rhs` rounded to the largest
225+
/// integer `n` such that `self >= n * rhs`.
226226
///
227227
/// # Precision
228228
///
229229
/// The result of this operation is guaranteed to be the rounded
230230
/// infinite-precision result.
231231
///
232+
/// If the magnitude of the exact quotient `self / rhs` is greater than or
233+
/// equal to the maximum integer that can be represented precisely with this
234+
/// type, then `NaN` may be returned.
235+
///
232236
/// # Examples
233237
///
234238
/// ```
@@ -239,16 +243,63 @@ impl f32 {
239243
/// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0
240244
/// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0
241245
/// ```
246+
///
247+
/// ```
248+
/// let a: f32 = 11.0;
249+
/// let b = 1.1;
250+
/// assert_eq!(a.div_euclid(b), 9.0);
251+
/// assert_eq!(a.rem_euclid(b), a.div_euclid(b).mul_add(-b, a));
252+
/// ```
242253
#[rustc_allow_incoherent_impl]
243254
#[must_use = "method returns a new number and does not mutate the original value"]
244255
#[inline]
245256
#[stable(feature = "euclidean_division", since = "1.38.0")]
246257
pub fn div_euclid(self, rhs: f32) -> f32 {
247-
let q = (self / rhs).trunc();
258+
use core::ops::ControlFlow;
259+
260+
fn xor_sign(x: f32, y: f32, v: f32) -> f32 {
261+
match () {
262+
_ if x.is_sign_positive() == y.is_sign_positive() => v,
263+
_ => -v,
264+
}
265+
}
266+
267+
fn div_special(x: f32, y: f32) -> ControlFlow<f32> {
268+
ControlFlow::Break(match (x, y) {
269+
(x, y) if x.is_nan() || y.is_nan() => f32::NAN,
270+
(x, y) if x == 0.0 && y == 0.0 => f32::NAN,
271+
(x, y) if x == 0.0 => xor_sign(x, y, 0.0),
272+
(x, y) if y == 0.0 => xor_sign(x, y, f32::INFINITY),
273+
(x, y) if x.is_infinite() && y.is_infinite() => f32::NAN,
274+
(x, y) if x.is_infinite() => xor_sign(x, y, f32::INFINITY),
275+
(x, y) if y.is_infinite() => xor_sign(x, y, 0.0),
276+
_ => return ControlFlow::Continue(()),
277+
})
278+
}
279+
280+
fn div_trunc(x: f32, y: f32) -> f32 {
281+
const MAX_EXACT_Q: f32 = ((1u64 << f32::MANTISSA_DIGITS) - 1) as f32;
282+
if let ControlFlow::Break(q) = div_special(x, y) {
283+
return q;
284+
}
285+
let sign = xor_sign(x, y, 1.0);
286+
let (x, y) = (x.abs(), y.abs());
287+
let q = x / y;
288+
if q > MAX_EXACT_Q {
289+
return f32::NAN;
290+
}
291+
let qt = q.trunc();
292+
if qt.mul_add(-y, x).is_sign_negative() { qt - 1.0 } else { qt }.copysign(sign)
293+
}
294+
295+
if let ControlFlow::Break(q) = div_special(self, rhs) {
296+
return q;
297+
}
298+
let qt = div_trunc(self, rhs);
248299
if self % rhs < 0.0 {
249-
return if rhs > 0.0 { q - 1.0 } else { q + 1.0 };
300+
return if rhs > 0.0 { qt - 1.0 } else { qt + 1.0 };
250301
}
251-
q
302+
qt
252303
}
253304

254305
/// Calculates the least nonnegative remainder of `self (mod rhs)`.

library/std/src/f64.rs

Lines changed: 57 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -220,15 +220,19 @@ impl f64 {
220220
/// Calculates Euclidean division, the matching method for `rem_euclid`.
221221
///
222222
/// This computes the integer `n` such that
223-
/// `self = n * rhs + self.rem_euclid(rhs)`.
224-
/// In other words, the result is `self / rhs` rounded to the integer `n`
225-
/// such that `self >= n * rhs`.
223+
/// `self.rem_euclid(rhs) = self.div_euclid(rhs).mul_add(-rhs, self)`.
224+
/// In other words, the result is `self / rhs` rounded to the largest
225+
/// integer `n` such that `self >= n * rhs`.
226226
///
227227
/// # Precision
228228
///
229229
/// The result of this operation is guaranteed to be the rounded
230230
/// infinite-precision result.
231231
///
232+
/// If the magnitude of the exact quotient `self / rhs` is greater than or
233+
/// equal to the maximum integer that can be represented precisely with this
234+
/// type, then `NaN` may be returned.
235+
///
232236
/// # Examples
233237
///
234238
/// ```
@@ -239,16 +243,63 @@ impl f64 {
239243
/// assert_eq!(a.div_euclid(-b), -1.0); // 7.0 >= -4.0 * -1.0
240244
/// assert_eq!((-a).div_euclid(-b), 2.0); // -7.0 >= -4.0 * 2.0
241245
/// ```
246+
///
247+
/// ```
248+
/// let a: f64 = 11.0;
249+
/// let b = 1.1;
250+
/// assert_eq!(a.div_euclid(b), 9.0);
251+
/// assert_eq!(a.rem_euclid(b), a.div_euclid(b).mul_add(-b, a));
252+
/// ```
242253
#[rustc_allow_incoherent_impl]
243254
#[must_use = "method returns a new number and does not mutate the original value"]
244255
#[inline]
245256
#[stable(feature = "euclidean_division", since = "1.38.0")]
246257
pub fn div_euclid(self, rhs: f64) -> f64 {
247-
let q = (self / rhs).trunc();
258+
use core::ops::ControlFlow;
259+
260+
fn xor_sign(x: f64, y: f64, v: f64) -> f64 {
261+
match () {
262+
_ if x.is_sign_positive() == y.is_sign_positive() => v,
263+
_ => -v,
264+
}
265+
}
266+
267+
fn div_special(x: f64, y: f64) -> ControlFlow<f64> {
268+
ControlFlow::Break(match (x, y) {
269+
(x, y) if x.is_nan() || y.is_nan() => f64::NAN,
270+
(x, y) if x == 0.0 && y == 0.0 => f64::NAN,
271+
(x, y) if x == 0.0 => xor_sign(x, y, 0.0),
272+
(x, y) if y == 0.0 => xor_sign(x, y, f64::INFINITY),
273+
(x, y) if x.is_infinite() && y.is_infinite() => f64::NAN,
274+
(x, y) if x.is_infinite() => xor_sign(x, y, f64::INFINITY),
275+
(x, y) if y.is_infinite() => xor_sign(x, y, 0.0),
276+
_ => return ControlFlow::Continue(()),
277+
})
278+
}
279+
280+
fn div_trunc(x: f64, y: f64) -> f64 {
281+
const MAX_EXACT_Q: f64 = ((1u64 << f64::MANTISSA_DIGITS) - 1) as f64;
282+
if let ControlFlow::Break(q) = div_special(x, y) {
283+
return q;
284+
}
285+
let sign = xor_sign(x, y, 1.0);
286+
let (x, y) = (x.abs(), y.abs());
287+
let q = x / y;
288+
if q > MAX_EXACT_Q {
289+
return f64::NAN;
290+
}
291+
let qt = q.trunc();
292+
if qt.mul_add(-y, x).is_sign_negative() { qt - 1.0 } else { qt }.copysign(sign)
293+
}
294+
295+
if let ControlFlow::Break(q) = div_special(self, rhs) {
296+
return q;
297+
}
298+
let qt = div_trunc(self, rhs);
248299
if self % rhs < 0.0 {
249-
return if rhs > 0.0 { q - 1.0 } else { q + 1.0 };
300+
return if rhs > 0.0 { qt - 1.0 } else { qt + 1.0 };
250301
}
251-
q
302+
qt
252303
}
253304

254305
/// Calculates the least nonnegative remainder of `self (mod rhs)`.

0 commit comments

Comments
 (0)