如何将有符号整数格式化为带符号的十六进制表示形式?

7

我的初衷是将有符号的基本数字转换为它的十六进制表示方式,同时保留数字的符号。但事实证明,当前有关带符号的基本整数的 LowerHexUpperHex 及其相关实现只会将它们视为无符号整数来处理。无论我添加了哪些额外的格式化标志,这些实现似乎都会将数字重新解释为其无符号对应项进行格式化。(Playground

println!("{:X}", 15i32);           // F
println!("{:X}", -15i32);          // FFFFFFF1   (expected "-F")
println!("{:X}", -0x80000000i32);  // 80000000   (expected "-80000000")
println!("{:+X}", -0x80000000i32); // +80000000
println!("{:+o}", -0x8000i16);     // +100000
println!("{:+b}", -0x8000i16);     // +1000000000000000

std::fmt文档中并没有明确说明这是否应该发生,或者是否有效,而且UpperHex(或任何其他格式化特性)也没有提到有符号整数的实现将数字解释为无符号数。Rust的GitHub仓库中似乎也没有相关问题。(附加通知:1.24.0开始,文档已经得到改进,以正确处理这些问题,请参见问题#42860
最终,可以实现特定任务的特定函数(如下),但不幸的是,它们与格式化程序API的兼容性不是很好。
fn to_signed_hex(n: i32) -> String {
    if n < 0 {
        format!("-{:X}", -n)
    } else {
        format!("{:X}", n)
    }
}

assert_eq!(to_signed_hex(-15i32), "-F".to_string());

这种有符号整数类型的行为是故意的吗?在遵循标准Formatter的情况下,有没有一种方法执行此格式化过程?请注意保留HTML标记。
2个回答

7

在遵循标准的Formatter的情况下,是否有一种方法可以执行此格式化过程?

是的,但您需要创建一个新类型,以便提供UpperHex的不同实现。这是一种尊重+#0标志(可能还有更多,我没有测试)的实现:

use std::fmt::{self, Formatter, UpperHex};

struct ReallySigned(i32);

impl UpperHex for ReallySigned {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let prefix = if f.alternate() { "0x" } else { "" };
        let bare_hex = format!("{:X}", self.0.abs());
        f.pad_integral(self.0 >= 0, prefix, &bare_hex)
    }
}

fn main() {
    for &v in &[15, -15] {
        for &v in &[&v as &UpperHex, &ReallySigned(v) as &UpperHex] {
            println!("Value: {:X}", v);
            println!("Value: {:08X}", v);
            println!("Value: {:+08X}", v);
            println!("Value: {:#08X}", v);
            println!("Value: {:+#08X}", v);
            println!();
        }
    }
}

我发誓以前没有访问格式化选项的权限,但是看起来自从1.5.0版本以来它们就一直在那里了,所以我不知道当时我在想什么。 - Shepmaster
flags()一直存在,只是“哪个位代表什么”并没有真正记录下来... - Francis Gagné

0

这就像Francis Gagné的答案一样,但是通用化以处理i8i128

use std::fmt::{self, Formatter, UpperHex};
use num_traits::Signed;

struct ReallySigned<T: PartialOrd + Signed + UpperHex>(T);

impl<T: PartialOrd + Signed + UpperHex> UpperHex for ReallySigned<T> {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        let prefix = if f.alternate() { "0x" } else { "" };
        let bare_hex = format!("{:X}", self.0.abs());
        f.pad_integral(self.0 >= T::zero(), prefix, &bare_hex)
    }
}

fn main() {
    println!("{:#X}", -0x12345678);
    println!("{:#X}", ReallySigned(-0x12345678));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接