如何在Haxe中可靠地将浮点数格式化为指定小数位数

3
我想知道是否有一种可靠的方法将 haxe 中的 Float x 转换为指定小数位数(例如 3)的 String。一种简单的方法是先将 x 转换为字符串表示形式,然后裁剪小数点 "." 后给定数量的字符,如 here 所示。但是,如果某些平台将数字显示为 "1.11e-2",会发生什么呢?
我在 haxe 中似乎也找不到 printf,这将允许我以正确的方式格式化数字。我想知道在 haxe 中做到这一点的最佳实践是什么。
我希望得到结果中的字符串,因为在某些系统中,浮点数会产生例如 "0.19999" 的结果,从 "2/10.0" 得出。仅截断小数点后的某些数字是不准确的。

你尝试过使用 https://github.com/polygonal/printf 吗? - npretto
@lordkryss 谢谢。我会看一下的。这段代码有1000行。我想找一个更轻量级的解决方案,只是为了正确地舍入数字,因为我打算基于Haxe将此函数移植到其他语言中。 - thor
3个回答

6
public static function floatToStringPrecision(n:Float, prec:Int){
  n = Math.round(n * Math.pow(10, prec));
  var str = ''+n;
  var len = str.length;
  if(len <= prec){
    while(len < prec){
      str = '0'+str;
      len++;
    }
    return '0.'+str;
  }
  else{
    return str.substr(0, str.length-prec) + '.'+str.substr(str.length-prec);
  }
}

在一些平台上,当处理大于最大整数(MAX_INT)的数字时,round函数可能会失败,因此在这种情况下,您需要自己编写一个round函数。


最佳实践仍然是使用其中一个Haxe printf实现。但是,如果您不想添加大量代码(=大小),那么使用自己的函数就是您的选择。 - stroncium
谢谢你的回答。它有效。我最终也写了自己的函数。 - thor

4
如果您只需要将数字保留到特定精度,那么可以使用以下简单的方法进行四舍五入。
/**
    Uses Math.round to fix a floating point number to a set precision.
**/
public static function round(number:Float, ?precision=2): Float
{
    number *= Math.pow(10, precision);
    return Math.round(number) / Math.pow(10, precision);
}

从Franco Ponticelli的THX库中提取:https://github.com/fponticelli/thx/blob/master/src/Floats.hx#L206 如果你想要像PrintF一样但更轻量级的东西,也许可以看一下相关代码,并仅提取与将浮点数打印到特定精度有关的部分。如果你只需要这个功能,则可以安全地忽略其中的许多其他功能。

1
谢谢。我遇到的一个问题是使用乘法和除法方法时,从2/10.0得到类似于0.19999..的数字。这在转换为字符串时会导致问题。 - thor

1
以下是优化版本。一般函数速度提高约20%,针对3个小数位的特定版本速度提高50%。尽可能避免创建字符串和字符串拼接,并且避免因精度问题而进行除法运算。
public static function floatToStringPrecision(n:Float,prec:Int)
{
    if(n==0)
        return "0." + ([for(i in 0...prec) "0"].join("")); //quick return

    var minusSign:Bool = (n<0.0);
    n = Math.abs(n);
    var intPart:Int = Math.floor(n);
    var p = Math.pow(10, prec);
    var fracPart = Math.round( p*(n - intPart) );
    var buf:StringBuf = new StringBuf();

    if(minusSign)
        buf.addChar("-".code);
    buf.add(Std.string(intPart));

    if(fracPart==0)
    {
        buf.addChar(".".code);
        for(i in 0...prec)
            buf.addChar("0".code);
    }
    else 
    {
        buf.addChar(".".code);
        p = p/10;
        var nZeros:Int = 0;
        while(fracPart<p)
        {
            p = p/10;
            buf.addChar("0".code);
        }
        buf.add(fracPart);
    }
    return buf.toString();
}

对于prec=3的情况,有一个特定的函数。

public static function floatToStringPrecision3(n:Float)
{
    if(n==0)
        return "0.000";

    var minusSign:Bool = (n<0.0);
    n = Math.abs(n);
    var intPart:Int = Math.floor(n);
    var p:Float = 1000.0; //pow(10, 3)
    var fracPart = Math.round( p*(n - intPart) );
    var buf:StringBuf = new StringBuf();

    if(minusSign)
        buf.addChar("-".code);
    buf.add(Std.string(intPart));
    if(fracPart==0)
         buf.add(".000");
    else 
    {
        if(fracPart<10)
            buf.add(".00");  
        else if(fracPart<100)
            buf.add(".0"); 
        else
            buf.add("."); 
        buf.add(fracPart);
    }
    return buf.toString();
}

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