生成带前导零的浮点数和带尾随空格的字符串

huangapple go评论70阅读模式
英文:

generate float with leading zeroes and string with trailing space

问题

我试图打印出字符串 "name" 长度固定为 20(尾随空格)的行。

然后,我想生成一个带有 10 个整数和 8 位小数的浮点数(amount),问题是我无法弄清楚如何格式化 amount/float,使它们具有相同的长度,而且出于某种原因,当前所有小数位都变成了零。

我想要的输出:

John Doe            D4356557654354645634564563.15343534
John Doe            C5674543545645634565456345.34535767
John Doe            C0000000000000000000000000.44786756
John Doe            D0000000000000000000865421.12576545

当前输出的样子:

John Doe            12345678912345C390571360.00000000
John Doe            12345678912345D5000080896.00000000
John Doe            12345678912345C4320145.50000000
John Doe            12345678912345C1073856384.00000000

代码

use rand::Rng;
use pad::PadStr;

struct Report {
    name: String,
    account_number: i64,
    letter: char,
    amount: f32,
}

fn main() {

  let mut n = 1;
  let mut rng = rand::thread_rng();

  while n < 101 {
    let acc = Report {
        name: String::from("John Doe").pad_to_width(20),
        account_number: 12345678912345,
        letter: rng.gen_range('C'..='D'),
        amount: rng.gen_range(100.1..9999999999.9),
    };

    println!("{}{}{}{:.8}\n", acc.name, acc.account_number, acc.letter, acc.amount);
    n += 1;
  }
}

(由于某种原因,"pad" 在 Playground 上不起作用)

您可以在上述代码中看到,我只翻译了您要求翻译的部分。

英文:

I'm trying to print out rows where the string "name" has a fixed length of 20 (trailing spaces).

I then want to generate a float number (amount) with 10 whole numbers and 8 decimals, problem is I cant figure out how to format the amount/float with leading zeroes making them the same length, also for some reason currently all the decimals becomes zero.

The output I want:

John Doe            D4356557654354645634564563.15343534
John Doe            C5674543545645634565456345.34535767
John Doe            C0000000000000000000000000.44786756
John Doe            D0000000000000000000865421.12576545

What the output currently looks like:

John Doe            12345678912345C390571360.00000000
John Doe            12345678912345D5000080896.00000000
John Doe            12345678912345C4320145.50000000
John Doe            12345678912345C1073856384.00000000

Code

use rand::Rng;
use pad::PadStr;

struct Report {
    name: String,
    account_number: i64,
    letter: char,
    amount: f32,
}

fn main() {

  let mut n = 1;
  let mut rng = rand::thread_rng();

  while n &lt; 101 {
    let acc = Report {
        name: String::from(&quot;John Doe&quot;).pad_to_width(20),
        account_number: 12345678912345,
        letter: rng.gen_range(&#39;C&#39;..=&#39;D&#39;),
        amount: rng.gen_range(100.1..9999999999.9),
    };


    println!(&quot;{}{}{}{:.8}\n&quot;, acc.name, acc.account_number, acc.letter, acc.amount);
    n += 1;
  }

  }

https://play.rust-lang.org/?version=stable&amp;mode=debug&amp;edition=2021&amp;gist=e229dbd212a94cd9cc0be507568c48d5

(for some reason "pad" is not working at playground)

答案1

得分: 1

由于 f32 不具备足够的精度来表示您想要打印的值,所以您看不到小数点后面的值。实际上,没有常用的基于2的浮点数具有您在这里需要的精度,将带小数的数字从十进制转换为二进制然后再转换回来总会导致四舍五入误差。

例如,如果您在 IEEE-754 32位浮点数 中(其中 f32 属于的类型)表示 '9876543210.12345678'(您的字符串可以表示的数字),您会得到以下结果:

  • 期望值:9876543210.12345678
  • 最准确的表示:9876543488.00000000
  • 由于转换导致的误差:277.87654322
  • 二进制表示:01010000 00010011 00101100 00000110
  • 十六进制表示:0x50132c06

即使是 f64 仍然存在误差

  • 期望值:9876543210.12345678
  • 实际存储在双精度浮点数中的值:9876543210.1234569549560546875
  • 由于转换导致的误差:0.0000001749560546854
  • 二进制表示:01000010 00000010 01100101 10000000 10110111 01010000 11111100 11010111
  • 十六进制表示:0x42026580B750FCD7

不要在这个任务中使用浮点数。而是使用整数。例如,可以使用两个整数,然后在它们之间加上点来打印。

如果您真的需要实际值,请改用十进制浮点数,以便它可以实际表示您尝试编码的值。

另一种选择是使用固定小数点整数。换句话说,将您的数字的所有值都左移8个小数位,并将它们表示为整数。然后在打印时再次添加点。在您的情况下,您很幸运;如果将 9876543210.12345678 存储为整数 987654321012345678,它仍然适合64位,因此可以使用 u64 来表示它。实际上,鉴于您的示例似乎是关于一笔资金,这很可能是其预期的方式。

英文:

The reason you don't see any after-comma values is that f32 does not have enough precision to represent the value you would like to print. In fact, no commonly used base-2 float has the precision you need here, converting from base 10 to base 2 back and forth with fractional numbers will always result in rounding errors.

For example, if you represent '9876543210.12345678' (a number your string can represent) in IEEE-754 32-bit floats (which f32 is), you get:

  • Desired value: 9876543210.12345678
  • Most accurate representation: 9876543488.00000000
  • Error due to conversion: 277.87654322
  • Binary Representation: 01010000 00010011 00101100 00000110
  • Hexadecimal Representation: 0x50132c06

Even f64 still makes errors:

  • Desired value: 9876543210.12345678
  • Value actually stored in double: 9876543210.1234569549560546875
  • Error due to conversion: 0.0000001749560546854
  • Binary Representation: 01000010 00000010 01100101 10000000
    10110111 01010000 11111100 11010111
  • Hexadecimal Representation: 0x42026580B750FCD7

Don't use floats for this task. Use integers instead. For example two integers that can then be printed with a dot in between.

If you really do need the actual value, use a decimal float instead, so it can actually represent the values you are trying to encode.

Another alternative would be to use fixed point integers. In other words, shift all values of your numbers by 8 decimals and represent them as an integer. Then, add the dot again when printing them. In your case you are lucky; if you store 9876543210.12345678 as the integer 987654321012345678, it still fits into 64 bits, so you can use a u64 to represent it. In fact, given that your example seems to be talking about an amount of money, this is most likely how it was intended.


Example:

  • Don't use pad for padding. It's already built into Rust's format macro: format!(&quot;{:&lt;20}&quot;, &quot;John Doe&quot;)
use rand::Rng;

#[derive(Debug)]
struct MoneyAmount(u64);

impl MoneyAmount {
    fn as_value_string(&amp;self) -&gt; String {
        let mut s = format! {&quot;{:&gt;018}&quot;, self.0};
        s.insert(10, &#39;.&#39;);
        s
    }
}

struct Report {
    name: String,
    account_number: i64,
    letter: char,
    amount: MoneyAmount,
}

fn main() {
    let mut rng = rand::thread_rng();

    for _ in 0..10 {
        let acc = Report {
            name: &quot;John Doe&quot;.to_string(),
            account_number: 12345678912345,
            letter: rng.gen_range(&#39;C&#39;..=&#39;D&#39;),
            amount: MoneyAmount(rng.gen_range(100100000000..999999999999999999)),
        };

        println!(
            &quot;{:&lt;20}{}{}{}&quot;,
            acc.name,
            acc.account_number,
            acc.letter,
            acc.amount.as_value_string()
        );
    }
}
John Doe            12345678912345D3628299098.68538932
John Doe            12345678912345C5874745565.85000457
John Doe            12345678912345C6337870441.26543580
John Doe            12345678912345D1215442576.70454002
John Doe            12345678912345C3402018622.70996714
John Doe            12345678912345C7999783867.43749281
John Doe            12345678912345D5797682336.45356635
John Doe            12345678912345D1707577080.35404025
John Doe            12345678912345D5813907399.04925935
John Doe            12345678912345C0611246390.19108372

huangapple
  • 本文由 发表于 2023年1月8日 23:33:03
  • 转载请务必保留本文链接:https://go.coder-hub.com/75049039.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定