From a8ef6292821428bad11ad4291e6039b91ec4d051 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 9 Dec 2023 10:21:12 -0800 Subject: [PATCH 1/3] Add tests for pow5_factor --- src/d2s_intrinsics.rs | 2 +- tests/d2s_intrinsics_test.rs | 72 ++++++++++++++++++++++++++++++++++++ 2 files changed, 73 insertions(+), 1 deletion(-) create mode 100644 tests/d2s_intrinsics_test.rs diff --git a/src/d2s_intrinsics.rs b/src/d2s_intrinsics.rs index f244a4d..a8e38d5 100644 --- a/src/d2s_intrinsics.rs +++ b/src/d2s_intrinsics.rs @@ -36,7 +36,7 @@ pub fn div100(x: u64) -> u64 { } #[cfg_attr(feature = "no-panic", inline)] -fn pow5_factor(mut value: u64) -> u32 { +pub(crate) fn pow5_factor(mut value: u64) -> u32 { let mut count = 0u32; loop { debug_assert!(value != 0); diff --git a/tests/d2s_intrinsics_test.rs b/tests/d2s_intrinsics_test.rs new file mode 100644 index 0000000..0ac80c9 --- /dev/null +++ b/tests/d2s_intrinsics_test.rs @@ -0,0 +1,72 @@ +// Translated from C to Rust. The original C code can be found at +// https://github.com/ulfjack/ryu and carries the following license: +// +// Copyright 2018 Ulf Adams +// +// The contents of this file may be used under the terms of the Apache License, +// Version 2.0. +// +// (See accompanying file LICENSE-Apache or copy at +// http://www.apache.org/licenses/LICENSE-2.0) +// +// Alternatively, the contents of this file may be used under the terms of +// the Boost Software License, Version 1.0. +// (See accompanying file LICENSE-Boost or copy at +// https://www.boost.org/LICENSE_1_0.txt) +// +// Unless required by applicable law or agreed to in writing, this software +// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +// KIND, either express or implied. + +#![allow(dead_code)] +#![allow( + clippy::cast_lossless, + clippy::cast_possible_truncation, + clippy::unreadable_literal +)] + +#[path = "../src/d2s_intrinsics.rs"] +mod d2s_intrinsics; + +use d2s_intrinsics::pow5_factor; + +#[test] +fn test_pow5_factor() { + assert_eq!(0, pow5_factor(1)); + assert_eq!(0, pow5_factor(2)); + assert_eq!(0, pow5_factor(3)); + assert_eq!(0, pow5_factor(4)); + assert_eq!(1, pow5_factor(5)); + assert_eq!(0, pow5_factor(6)); + assert_eq!(0, pow5_factor(7)); + assert_eq!(0, pow5_factor(8)); + assert_eq!(0, pow5_factor(9)); + assert_eq!(1, pow5_factor(10)); + + assert_eq!(0, pow5_factor(12)); + assert_eq!(0, pow5_factor(14)); + assert_eq!(0, pow5_factor(16)); + assert_eq!(0, pow5_factor(18)); + assert_eq!(1, pow5_factor(20)); + + assert_eq!(2, pow5_factor(5 * 5)); + assert_eq!(3, pow5_factor(5 * 5 * 5)); + assert_eq!(4, pow5_factor(5 * 5 * 5 * 5)); + assert_eq!(5, pow5_factor(5 * 5 * 5 * 5 * 5)); + assert_eq!(6, pow5_factor(5 * 5 * 5 * 5 * 5 * 5)); + assert_eq!(7, pow5_factor(5 * 5 * 5 * 5 * 5 * 5 * 5)); + assert_eq!(8, pow5_factor(5 * 5 * 5 * 5 * 5 * 5 * 5 * 5)); + assert_eq!(9, pow5_factor(5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5)); + assert_eq!(10, pow5_factor(5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5 * 5)); + + assert_eq!(0, pow5_factor(42)); + assert_eq!(1, pow5_factor(42 * 5)); + assert_eq!(2, pow5_factor(42 * 5 * 5)); + assert_eq!(3, pow5_factor(42 * 5 * 5 * 5)); + assert_eq!(4, pow5_factor(42 * 5 * 5 * 5 * 5)); + assert_eq!(5, pow5_factor(42 * 5 * 5 * 5 * 5 * 5)); + + assert_eq!(27, pow5_factor(7450580596923828125)); // 5^27, largest power of 5 < 2^64. + assert_eq!(1, pow5_factor(18446744073709551615)); // 2^64 - 1, largest multiple of 5 < 2^64. + assert_eq!(0, pow5_factor(18446744073709551614)); // 2^64 - 2, largest non-multiple of 5 < 2^64. +} From 2ded0d1dbd63244dde5a702396c3f4efbae702fc Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 9 Dec 2023 10:22:33 -0800 Subject: [PATCH 2/3] Reimplement pow5_factor using modular inverse --- src/d2s_intrinsics.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/d2s_intrinsics.rs b/src/d2s_intrinsics.rs index a8e38d5..a4e1fb1 100644 --- a/src/d2s_intrinsics.rs +++ b/src/d2s_intrinsics.rs @@ -37,15 +37,15 @@ pub fn div100(x: u64) -> u64 { #[cfg_attr(feature = "no-panic", inline)] pub(crate) fn pow5_factor(mut value: u64) -> u32 { + const M_INV_5: u64 = 14757395258967641293; // 5 * m_inv_5 = 1 (mod 2^64) + const N_DIV_5: u64 = 3689348814741910323; // #{ n | n = 0 (mod 2^64) } = 2^64 / 5 let mut count = 0u32; loop { debug_assert!(value != 0); - let q = div5(value); - let r = (value as u32).wrapping_sub(5u32.wrapping_mul(q as u32)); - if r != 0 { + value = value.wrapping_mul(M_INV_5); + if value > N_DIV_5 { break; } - value = q; count += 1; } count From 2e482fcee1642febceff9d412d1797252a8dc076 Mon Sep 17 00:00:00 2001 From: David Tolnay Date: Sat, 9 Dec 2023 10:23:39 -0800 Subject: [PATCH 3/3] Sync to ulfjack/ryu@77e767f --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0abd71f..998ea3e 100644 --- a/README.md +++ b/README.md @@ -19,7 +19,7 @@ C, [https://github.com/ulfjack/ryu][upstream]. uses nothing from the Rust standard library so is usable from no_std crates.* [paper]: https://dl.acm.org/citation.cfm?id=3192369 -[upstream]: https://github.com/ulfjack/ryu/tree/abf76d252bc97300354857e64e80d4a2bf664291 +[upstream]: https://github.com/ulfjack/ryu/tree/77e767f5e056bab96e895072fc21618ecff2f44b ```toml [dependencies]