Skip to content

Commit

Permalink
Updated ExactExponent.
Browse files Browse the repository at this point in the history
- Added documentation for exponent_limit
- Added documentation for mantissa_limit
- Added sample code to auto-generate both.
- Fixed some mantissa_limit values that were too low (this would not
  have affected accuracy, just made the fast_path a little too slow in
some cases).
  • Loading branch information
Alexhuszagh committed May 3, 2021
1 parent eaf41a1 commit 8887a01
Showing 1 changed file with 99 additions and 15 deletions.
114 changes: 99 additions & 15 deletions lexical-core/src/table/decimal.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,95 @@ pub(crate) const DIGIT_TO_BASE10_SQUARED: [u8; 200] = [
// EXACT EXPONENT
// --------------

// Calculating the exponent limit requires determining the largest exponent
// we can calculate for a radix that can be **exactly** store in the
// float type. If the value is a power-of-two, then we simply
// need to scale the minimum, denormal exp and maximum exp to the type
// size. Otherwise, we need to calculate the number of digits
// that can fit into the type's precision, after removing a power-of-two
// (since these values can be represented exactly).
//
// The mantissa limit is the number of digits we can remove from
// the exponent into the mantissa, and is therefore is the
// `⌊ precision / log2(radix) ⌋`, where precision does not include
// the hidden bit.
//
// The algorithm for calculating both `exponent_limit` and `mantissa_limit`,
// in Python, can be done as follows:
//
// ```python
// import math
//
// def is_pow2(value):
// '''Calculate if a value is a power of 2.'''
//
// floor = int(math.log2(value))
// return value == 2**floor
//
//
// def remove_pow2(value):
// '''Remove a power of 2 from the value.'''
//
// while math.floor(value / 2) == value / 2:
// value //= 2
// return value
//
//
// def exponent_limit(radix, mantissa_size, min_exp, max_exp):
// '''
// Calculate the exponent limit for a float, for a given
// float type, where `radix` is the numerical base
// for the float type, and mantissa size is the length
// of the mantissa in bits. min_exp is the minimum,
// denormal binary exponent, and max_exp is the maximum
// binary exponent.
// '''
//
// if is_pow2(radix):
// # Can always be exactly represented, calculate relative
// # to min and max exp.
// scaled_min_exp = int(min_exp / math.log2(radix))
// scaled_max_exp = int(max_exp / math.log2(radix))
// return (scaled_min_exp, scaled_max_exp)
// else:
// # Positive and negative should be the same,
// # since we need to find the maximum digit
// # representable with mantissa digits.
// # We first need to remove the highest power-of-
// # two from the radix, since these will be represented
// # with exponent digits.
// base = remove_pow2(radix)
// precision = mantissa_size + 1
// exp_limit = int(precision / math.log2(base))
// return (-exp_limit, exp_limit)
//
//
// def mantissa_limit(radix, mantissa_size):
// '''
// Calculate mantissa limit for a float type, given
// the radix and the length of the mantissa in bits.
// '''
//
// precision = mantissa_size + 1
// return int(precision / math.log2(radix))
//
//
// def all_limits(mantissa_size, min_exp, max_exp):
// '''Print limits for all radixes.'''
//
// print('match radix.as_i32() {')
// for radix in range(2, 37):
// exp_limit = exponent_limit(radix, mantissa_size, min_exp, max_exp)
// print(f' {radix} => {exp_limit},')
// print('}')
//
// print('match radix.as_i32() {')
// for radix in range(2, 37):
// mant_limit = mantissa_limit(radix, mantissa_size)
// print(f' {radix} => {mant_limit},')
// print('}')
// ```

/// Get exact exponent limit for radix.
pub trait ExactExponent {
/// Get min and max exponent limits (exact) from radix.
Expand Down Expand Up @@ -154,11 +243,11 @@ impl ExactExponent for f32 {
#[cfg(all(feature = "binary", not(feature = "radix")))]
{
match radix.as_i32() {
2 => 23,
4 => 11,
8 => 7,
2 => 24,
4 => 12,
8 => 8,
10 => 7,
16 => 5,
16 => 6,
32 => 4,
// Invalid radix
_ => unreachable!(),
Expand All @@ -168,21 +257,21 @@ impl ExactExponent for f32 {
#[cfg(feature = "radix")]
{
match radix.as_i32() {
2 => 23,
2 => 24,
3 => 15,
4 => 11,
4 => 12,
5 => 10,
6 => 9,
7 => 8,
8 => 7,
8 => 8,
9 => 7,
10 => 7,
11 => 6,
12 => 6,
13 => 6,
14 => 6,
15 => 6,
16 => 5,
16 => 6,
17 => 5,
18 => 5,
19 => 5,
Expand Down Expand Up @@ -210,11 +299,6 @@ impl ExactExponent for f32 {
}
}

/// Precalculated min and max exponents for values exactly representable as f64.
///
/// Table of values where `radix**min` and `radix**max` are the limits of types
/// exactly representable as an f64.
impl ExactExponent for f64 {
#[inline]
fn exponent_limit<T: Integer>(radix: T) -> (i32, i32) {
Expand Down Expand Up @@ -292,7 +376,7 @@ impl ExactExponent for f64 {
#[cfg(all(feature = "binary", not(feature = "radix")))]
{
match radix.as_i32() {
2 => 52,
2 => 53,
4 => 26,
8 => 17,
10 => 15,
Expand All @@ -306,7 +390,7 @@ impl ExactExponent for f64 {
#[cfg(feature = "radix")]
{
match radix.as_i32() {
2 => 52,
2 => 53,
3 => 33,
4 => 26,
5 => 22,
Expand Down

0 comments on commit 8887a01

Please sign in to comment.