首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >双到工程符号的IFormatProvider

双到工程符号的IFormatProvider
EN

Code Review用户
提问于 2022-07-14 17:04:21
回答 1查看 605关注 0票数 5

一种c#格式提供程序,它使用工程表示法规则将双重格式格式化为字符串。如果该值在定义的符号之外,它将默认使用科学符号。

我编写这段代码是为了更好地处理以前处理遗留项目的工程符号格式的乱糟糟的case语句。该项目预先对被测试的产品进行电气测量,例如电压、电流和电阻。如果用工程符号表示,这些测量对测试技术人员来说要容易得多。

代码语言:javascript
复制
class EngNotationFormatter : IFormatProvider, ICustomFormatter
{
    private readonly Dictionary notationSymbols = new Dictionary
    {
        {double.NegativeInfinity, ""},
        {-24, "y"},
        {-21, "z"},
        {-18, "a"},
        {-15, "f"},
        {-12, "p"},
        {-9, "n"},
        {-6, "μ"},
        {-3, "m"},
        {0, ""},
        {3, "k"},
        {6, "M"},
        {9, "G"},
        {12, "T"},
        {15, "P"},
        {18, "E"},
        {21, "Z"},
        {24, "Y"},
    };
    public string Format(string format, object arg, IFormatProvider formatProvider)
    {
        double value = Convert.ToDouble(arg);

        double exponent = Math.Log10(Math.Abs(value));
        double engExponent = Math.Floor(exponent / 3) * 3;

        string symbol = notationSymbols.ContainsKey(engExponent) ? notationSymbols[engExponent] : "e" + engExponent;

        return (value * Math.Pow(10, -(int)engExponent)).ToString("0.########") + symbol;
    }

    public object GetFormat(Type formatType)
    {
        if (formatType == typeof(ICustomFormatter))
            return this;
        else
            return null;
    }
}

示例使用:

代码语言:javascript
复制
Console.WriteLine(String.Format(new EngNotationFormatter(), "{0}Ω", 0.01234)); //result: 12.34mΩ

原始case语句是在代码中放置的,这里的dat是要格式化的两个,精度是要显示的数字数(这个值总是相同的)。

代码语言:javascript
复制
Function formatData(ByVal dat As Single, ByVal precision As String) As String
    Select Case System.Math.Abs(dat)
        Case 0
            formatData = "0"
        Case Is < 0.000000000001
            formatData = Format(dat * 1.0E+15, precision & "f")
        Case Is < 0.000000001
            formatData = Format(dat * 1000000000000.0#, precision & "p")
        Case Is < 0.000001
            formatData = Format(dat * 1000000000.0#, precision & "n")
        Case Is < 0.001
            formatData = Format(dat * 1000000.0#, precision & "u")
        Case Is < 0.99
            formatData = Format(dat * 1000.0#, precision & "m")
            'Case Is < 1101#
        Case Is < 1000.0#
            formatData = Format(dat, precision & "")
        Case Is < 1000000.0#
            formatData = Format(dat / 1000.0#, precision & "k")
        Case Is < 1000000000.0#
            formatData = Format(dat / 1000000.0#, precision & "M")
        Case Is < 1000000000000.0#
            formatData = Format(dat / 1000000000.0#, precision & "G")
        Case Else
            formatData = ""
    End Select
End Function
EN

回答 1

Code Review用户

回答已采纳

发布于 2022-07-14 20:51:16

我不知道您为什么选择使用IFormatProvider,以及为什么手动添加使用string.Format的符号。

尽管如此,如果您使用notationSymbols来避免不必要的内存分配,那么使用switch会更好。如果您正在计划扩展它,或者您不认为switch很适合您的用例,那么请保留它,但也要缓存它以供重用。

代码语言:javascript
复制
double value = Convert.ToDouble(arg);

如果arg不是有效整数(例如null),这将引发一个exception.So,相反,您可以这样做:

代码语言:javascript
复制
if(arg is double value) 
{
    // code
}

或者这个:

代码语言:javascript
复制
if(double.TryParse(arg?.ToString(), out var value))
{
    // code 
}

最后一个注意事项是,当从较低的精度转换到更高的精度时,需要确保它们之间的值一致性,这是一个非常常见的问题。所以,有一个计划来确保旧的精确性和新的精确性之间的兼容性,就可以保证你的麻烦。

最后,您可以实现一个用于符号的抽象类,而不是实现IFormatProvider,然后使用扩展方法来格式化数字。

例子:

代码语言:javascript
复制
public abstract class EngineeringNotation
{
    private readonly char _notationSymbol;
    
    protected EngineeringNotation(char notationSymbol) {
        _notationSymbol = notationSymbol;
    }
    
    public string Format(double number)
    {
        double exponent = Math.Log10(Math.Abs(number));

        double engExponent = Math.Floor(exponent / 3) * 3);

        double result = number * Math.Pow(10, -(int)engExponent);
        
        string symbol = GetSymbol(engExponent);

        return $"{result:0.########}{symbol}{_notationSymbol}";
    }
    
    private static string GetSymbol(double exponent)
    {
        switch (exponent)
        {
            case double.NegativeInfinity:
            case 0:
                return string.Empty;
            case -24:
                return "y";
            case -21:
                return "z";
            case -18:
                return "a";
            case -15:
                return "f";
            case -12:
                return "p";
            case -9:
                return "n";
            case -6:
                return "μ";
            case -3:
                return "m";
            case 3:
                return "k";
            case 6:
                return "M";
            case 9:
                return "G";
            case 12:
                return "T";
            case 15:
                return "P";
            case 18:
                return "E";
            case 21:
                return "Z";
            case 24:
                return "Y";
            default:
                return $"e{exponent}";
        }
    }   
}

public class VoltNotation : EngineeringNotation
{
    public VoltNotation() : base('V') { }
}

public class OhmNotation : EngineeringNotation
{
    public OhmNotation() : base('Ω') { }
}

public static class NotationExtensions
{
    public static string FormatNotation(this double value, EngineeringNotation notation)
    {
        return notation.Format(value);
    }
    
    public static string FormatAsVoltNotation(this double value)
    {
        return FormatNotation(value, new VoltNotation());
    }
    
    public static string FormatAsOhmNotation(this double value)
    {
        return FormatNotation(value, new OhmNotation());
    }   
}

现在你可以这样做了:

代码语言:javascript
复制
double number   = 0.01234;
string formated = number.FormatAsOhmNotation();
票数 6
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/278074

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档