diff --git a/contracts/feature-tests/basic-features/src/big_num_methods.rs b/contracts/feature-tests/basic-features/src/big_num_methods.rs index 086f89761b..c30b8cb56e 100644 --- a/contracts/feature-tests/basic-features/src/big_num_methods.rs +++ b/contracts/feature-tests/basic-features/src/big_num_methods.rs @@ -48,7 +48,7 @@ pub trait BigIntMethods { } #[endpoint] - fn biguint_overwrite_u64(&self, bu: BigUint, small: u64) -> BigUint { + fn biguint_overwrite_u64(&self, mut bu: BigUint, small: u64) -> BigUint { bu.overwrite_u64(small); bu } diff --git a/framework/base/src/types/managed/basic/big_uint.rs b/framework/base/src/types/managed/basic/big_uint.rs index 7f9f6bb17b..110a6b46d1 100644 --- a/framework/base/src/types/managed/basic/big_uint.rs +++ b/framework/base/src/types/managed/basic/big_uint.rs @@ -10,9 +10,12 @@ use crate::{ DecodeErrorHandler, EncodeErrorHandler, NestedDecode, NestedDecodeInput, NestedEncode, NestedEncodeOutput, TopDecode, TopDecodeInput, TopEncode, TopEncodeOutput, TryStaticCast, }, + contract_base::ErrorHelper, formatter::{hex_util::encode_bytes_as_hex, FormatBuffer, FormatByteReceiver, SCDisplay}, - proxy_imports::ManagedRef, - types::{heap::BoxedBytes, ManagedBuffer, ManagedBufferCachedBuilder, ManagedType}, + types::{ + heap::BoxedBytes, ConstDecimals, Decimals, ManagedBuffer, ManagedBufferCachedBuilder, + ManagedDecimal, ManagedRef, ManagedType, + }, }; use super::cast_to_i64::cast_to_i64; @@ -176,7 +179,7 @@ impl BigUint { } #[inline] - pub fn overwrite_u64(&self, value: u64) { + pub fn overwrite_u64(&mut self, value: u64) { Self::set_value(self.handle.clone(), value); } @@ -236,6 +239,48 @@ impl BigUint { let api = M::managed_type_impl(); api.bi_log2(self.handle.clone()) } + + pub fn ln(&self) -> ManagedDecimal> { + let bit_log2 = self.log2(); // aproximate, based on position of the most significant bit + let scaling_factor_9 = ConstDecimals::<9>.scaling_factor(); + let divisor = BigUint::from(1u64 << bit_log2); + let normalized = self * &*scaling_factor_9 / divisor; + + let x = normalized + .to_u64() + .unwrap_or_else(|| ErrorHelper::::signal_error_with_message("ln internal error")) + as i64; + + const DENOMINATOR: i64 = 1_000_000_000; + + // x normalized to [1.0, 2.0] + debug_assert!(x >= DENOMINATOR); + debug_assert!(x <= 2 * DENOMINATOR); + + let mut result: i64 = -56570851; // -0.056570851 + result *= x; + result /= DENOMINATOR; + result += 447179550; // 0.44717955 + result *= x; + result /= DENOMINATOR; + result += -1469956800; // -1.4699568 + result *= x; + result /= DENOMINATOR; + result += 2821202600; // 2.8212026 + result *= x; + result /= DENOMINATOR; + result += -1741793900; // -1.7417939 + + const LN_OF_2_SCALE_9: i64 = 693147180; // 0.69314718 + result += bit_log2 as i64 * LN_OF_2_SCALE_9; + + debug_assert!(result > 0); + + let mut result_bi = normalized; // reuse handle + result_bi.overwrite_u64(result as u64); + + ManagedDecimal::const_decimals_from_raw(result_bi) + } } impl Clone for BigUint { diff --git a/framework/scenario/tests/big_uint_test.rs b/framework/scenario/tests/big_uint_test.rs new file mode 100644 index 0000000000..d31eba9346 --- /dev/null +++ b/framework/scenario/tests/big_uint_test.rs @@ -0,0 +1,10 @@ +use multiversx_sc::types::BigUint; +use multiversx_sc_scenario::api::StaticApi; + +#[test] +fn test_big_uint_ln() { + // ln(23) = 3.1354942159291497 + let x = BigUint::::from(23u32); + let ln_x = x.ln(); + assert_eq!(ln_x.to_string(), "3.135514649"); // first 6 decimals are ok +}