英文:
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 < 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;
}
}
(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'sformat
macro:format!("{:<20}", "John Doe")
use rand::Rng;
#[derive(Debug)]
struct MoneyAmount(u64);
impl MoneyAmount {
fn as_value_string(&self) -> String {
let mut s = format! {"{:>018}", self.0};
s.insert(10, '.');
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: "John Doe".to_string(),
account_number: 12345678912345,
letter: rng.gen_range('C'..='D'),
amount: MoneyAmount(rng.gen_range(100100000000..999999999999999999)),
};
println!(
"{:<20}{}{}{}",
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
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论