Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zero values with certain scales are formatted with E #132

Open
aexoden opened this issue Jul 24, 2024 · 6 comments
Open

Zero values with certain scales are formatted with E #132

aexoden opened this issue Jul 24, 2024 · 6 comments
Labels

Comments

@aexoden
Copy link

aexoden commented Jul 24, 2024

I'm not sure if I'm just doing something horribly wrong or if this is a bug, but consider the following program:

use bigdecimal::BigDecimal;
use std::str::FromStr;

fn main() {
    let zero = BigDecimal::from_str("0.0000000").unwrap();

    println!("{zero:.12}");
}

This currently generates as output:

0.000000000000E-7

It only happens once the scale is large enough. I am parsing numbers passed via an API that are guaranteed to have 12 decimal places, so that's how it came up.

@akubera
Copy link
Owner

akubera commented Jul 24, 2024

These formatting issues are rough... So you would expect followed by twelve zeros, right? 0.000000000000

@akubera
Copy link
Owner

akubera commented Jul 24, 2024

Try setting this environment variable at build time.

RUST_BIGDECIMAL_FMT_EXPONENTIAL_LOWER_THRESHOLD = 12

@aexoden
Copy link
Author

aexoden commented Jul 24, 2024

Yes, that would be what I'd expect. And setting that environment variable does result in the correct value it looks like.

@akubera
Copy link
Owner

akubera commented Jul 24, 2024

Yeah, zero is a special case that needs to be dealt with here.

Here's the formatting given 0.0000000 and 0.1000000 in BigDecimal and f64

           | BigDecimal                  | f64
           +------------                 +-----
0.0000000
      {} : 0E-7                           0
   {:.0} : 0E-7                           0
   {:.1} : 0.0E-7                         0.0
   {:.2} : 0.00E-7                        0.00
   {:.3} : 0.000E-7                       0.000
   {:.4} : 0.0000E-7                      0.0000
   {:.5} : 0.00000E-7                     0.00000
   {:.6} : 0.000000E-7                    0.000000
   {:.7} : 0.0000000E-7                   0.0000000
   {:.8} : 0.00000000E-7                  0.00000000
   {:.9} : 0.000000000E-7                 0.000000000
  {:.10} : 0.0000000000E-7                0.0000000000
  {:.11} : 0.00000000000E-7               0.00000000000
  {:.12} : 0.000000000000E-7              0.000000000000
  {:.13} : 0.0000000000000E-7             0.0000000000000
  {:.14} : 0.00000000000000E-7            0.00000000000000
  {:.15} : 0.000000000000000E-7           0.000000000000000
  {:.16} : 0.0000000000000000E-7          0.0000000000000000
  {:.17} : 0.00000000000000000E-7         0.00000000000000000
  {:.18} : 0.000000000000000000E-7        0.000000000000000000
  {:.19} : 0.0000000000000000000E-7       0.0000000000000000000

0.1000000
      {} : 0.1000000                      0.1
   {:.0} : 0                              0
   {:.1} : 0.1                            0.1
   {:.2} : 0.10                           0.10
   {:.3} : 0.100                          0.100
   {:.4} : 0.1000                         0.1000
   {:.5} : 0.10000                        0.10000
   {:.6} : 0.100000                       0.100000
   {:.7} : 0.1000000                      0.1000000
   {:.8} : 0.10000000                     0.10000000
   {:.9} : 0.100000000                    0.100000000
  {:.10} : 0.1000000000                   0.1000000000
  {:.11} : 0.10000000000                  0.10000000000
  {:.12} : 0.100000000000                 0.100000000000
  {:.13} : 0.1000000000000                0.1000000000000
  {:.14} : 0.10000000000000               0.10000000000000
  {:.15} : 0.100000000000000              0.100000000000000
  {:.16} : 0.1000000000000000             0.1000000000000000
  {:.17} : 0.10000000000000000            0.10000000000000001
  {:.18} : 0.100000000000000000           0.100000000000000006
  {:.19} : 0.1000000000000000000          0.1000000000000000056

I consider this a bug that zero formats in exponential format given a requested precision.

I don't have time to work on it for a few weeks. But I'm glad there's an escape hatch in the meantime. When 0.4.6 is out you can drop the environment variable.

@akubera akubera added the bug label Jul 24, 2024
@akubera
Copy link
Owner

akubera commented Jul 24, 2024

Note to myself: a zero value with scale > leading-zero threshold triggers exponential format here: https://github.com/akubera/bigdecimal-rs/blob/trunk/src/impl_fmt.rs#L109C8-L109C30

@akubera
Copy link
Owner

akubera commented Jul 24, 2024

To those interested: we have to handle formatting numbers with leading zeros (between decimal place and first nonzero digit) specially, because a value of 1e-999999999999999 would print about 909 TiB of zeros.

As a default I used max 5 leading zeros before switching to exponential, following Python's example

>>> from decimal import Decimal
>>> for exp in range(11): print(Decimal(f"1e{-exp}"))
...
1
0.1
0.01
0.001
0.0001
0.00001
0.000001
1E-7
1E-8
1E-9
1E-10

There's no special case for zero, so any zero with a scale (ex "0.000000" or "0e-6") will trigger the code to deal with "too many leading-zeros". The threshold is configurable with that environment variable, so you could bump up to whatever value you want if you're sure you're always requesting 12 digits of precision, or you aren't dealing with potentially malicious users/numbers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

2 participants