float编号以Std中的字符串转换实现

发布于 2025-02-13 17:19:58 字数 708 浏览 0 评论 0 原文

我面临一个好奇的问题。查看此简单的代码:

int main(int argc, char **argv) {
    char buf[1000];
    snprintf_l(buf, sizeof(buf), _LIBCPP_GET_C_LOCALE, "%.17f", 0.123e30f);
    std::cout << "WTF?: " << buf << std::endl;
}

输出看起来Quire Quire:

123000004117574256822262431744.00000000000000000

我的问题是如何实现?有人可以告诉我原始代码吗?我没有找到。也许这对我来说太复杂了。

我试图用Java代码重新将相同的转换重新输入到字符串,但失败了。即使我试图分别获取指数和分数零件并在周期中总结分数,我也总是会得到零数而不是这些数字“ ... 8222262431744”。当我尝试在23位(对于浮点数)之后继续总结分数时,我面对其他问题 - 我需要收集多少个部分?为什么原始代码在左侧停止而直到量表结束之前才继续? 因此,我真的不了解基本逻辑,如何实现。我尝试定义数字非常大(例如0.123E127F)。它以小数格式产生了大量。该数字的精度比浮子要高得多。看起来这是一个问题,因为字符串表示包含浮点数不能的东西。

I faced with a curious issue. Look at this simple code:

int main(int argc, char **argv) {
    char buf[1000];
    snprintf_l(buf, sizeof(buf), _LIBCPP_GET_C_LOCALE, "%.17f", 0.123e30f);
    std::cout << "WTF?: " << buf << std::endl;
}

The output looks quire wired:

123000004117574256822262431744.00000000000000000

My question is how it's implemented? Can someone show me the original code? I did not find it. Or maybe it's too complicated for me.

I've tried to reimplement the same transformation double to string with Java code but was failed. Even when I tried to get exponent and fraction parts separately and summarize fractions in cycle I always get zeros instead of these numbers "...822262431744". When I tried to continue summarizing fractions after the 23 bits (for float number) I faced with other issue - how many fractions I need to collect? Why the original code stops on left part and does not continue until the scale is end?
So, I really do not understand the basic logic, how it implemented. I've tried to define really big numbers (e.g. 0.123e127f). And it generates huge number in decimal format. The number has much higher precision than float can be. Looks like this is an issue, because the string representation contains something which float number cannot.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

拥有 2025-02-20 17:19:58

请阅读文档:

printf,fprintf,sprintf,snprintf,snprintf,printf_s,printf_s,fprintf_s,fprintf_s,sprintf_s,snprintf_s,snprintf_s-s-s sn snprintf_s-- cppreference.com

格式字符串由普通的多键字符组成(),这些字符被复制到输出流中和转换规范中。每个转换规范具有以下格式:

  • 介绍性字符
  • ...
  • (可选)后面是整数或*,或者均未指定转换的 precision 。在使用*时如果提供一个宽度。如果该参数的价值为负,则将忽略。如果既不使用数字也不使用*,则将精度视为零。有关 Precision
  • 的确切影响,请参见下表

....

转换说明器 解释 预期参数类型
f f 浮点数转换为样式 [ - ] ddd.ddd 的小数表示法。 精度指定小数点字符之后出现的确切数字。默认的精度为6。在替代实现中,即使没有数字遵循数字,也写了十进制点字符。对于无限和非数字转换样式,请参见注释。

而使用 f 您强制表格 ddd.ddd (无exponent)和 .17 您已被迫在小数分离器后显示17位数字。如此大的价值印刷结果看起来很奇怪。

Please read documentation:

printf, fprintf, sprintf, snprintf, printf_s, fprintf_s, sprintf_s, snprintf_s - cppreference.com

The format string consists of ordinary multibyte characters (except %), which are copied unchanged into the output stream, and conversion specifications. Each conversion specification has the following format:

  • introductory % character
  • ...
  • (optional) . followed by integer number or *, or neither that specifies precision of the conversion. In the case when * is used, the precision is specified by an additional argument of type int, which appears before the argument to be converted, but after the argument supplying minimum field width if one is supplied. If the value of this argument is negative, it is ignored. If neither a number nor * is used, the precision is taken as zero. See the table below for exact effects of precision.

....

Conversion Specifier Explanation Expected Argument Type
f F converts floating-point number to the decimal notation in the style [-]ddd.ddd. Precision specifies the exact number of digits to appear after the decimal point character. The default precision is 6. In the alternative implementation decimal point character is written even if no digits follow it. For infinity and not-a-number conversion style see notes. double

So with f you forced form ddd.ddd (no exponent) and with .17 you have forced to show 17 digits after decimal separator. With such big value printed outcome looks that odd.

鞋纸虽美,但不合脚ㄋ〞 2025-02-20 17:19:58

最后,我发现了Java Float - &gt;小数 - &gt;字符串转换和C ++ float-&gt;字符串(十进制)转换。我没有找到原始的源代码,但是我在Java中复制了相同的代码以使其清晰。我认为代码解释了一切:

    // the context size might be calculated properly by getting maximum 
    // float number (including exponent value) - its 40 + scale, 17 for me
    MathContext context = new MathContext(57, RoundingMode.HALF_UP);
    BigDecimal divisor = BigDecimal.valueOf(2);
    int tmp = Float.floatToRawIntBits(1.23e30f)
    boolean sign = tmp < 0;
    tmp <<= 1;
    // there might be NaN value, this code does not support it
    int exponent = (tmp >>> 24) - 127;
    tmp <<= 8;
    int mask = 1 << 23;
    int fraction = mask | (tmp >>> 9);
    // at this line we have all parts of the float: sign, exponent and fractions. Let's build mantissa
    BigDecimal mantissa = BigDecimal.ZERO;
    for (int i = 0; i < 24; i ++) {
        if ((fraction & mask) == mask) {
            // i'm not sure about speed, maybe division at each iteration might be faster than pow
            mantissa = mantissa.add(divisor.pow(-i, context));
        }
        mask >>>= 1;
    }

    // it was the core line where I was losing accuracy, because of context
    BigDecimal decimal = mantissa.multiply(divisor.pow(exponent, context), context);
    String str = decimal.setScale(17, RoundingMode.HALF_UP).toPlainString();
    // add minus manually, because java lost it if after the scale value become 0, C++ version of code doesn't do it
    if (sign) {
        str = "-" + str;
    }
    return str;

也许主题是没有用的。谁真的需要像C ++一样具有相同的实现?但是至少此代码与最流行的方式将float转换为十进制字符串的浮点数保持了所有精度:

    return BigDecimal.valueOf(1.23e30f).setScale(17, RoundingMode.HALF_UP).toPlainString();

Finally I've found out what the difference between Java float -> decimal -> string conversion and c++ float -> string (decimal) conversion. I did not find the original source code, but I replicated the same code in Java to make it clear. I think the code explains everything:

    // the context size might be calculated properly by getting maximum 
    // float number (including exponent value) - its 40 + scale, 17 for me
    MathContext context = new MathContext(57, RoundingMode.HALF_UP);
    BigDecimal divisor = BigDecimal.valueOf(2);
    int tmp = Float.floatToRawIntBits(1.23e30f)
    boolean sign = tmp < 0;
    tmp <<= 1;
    // there might be NaN value, this code does not support it
    int exponent = (tmp >>> 24) - 127;
    tmp <<= 8;
    int mask = 1 << 23;
    int fraction = mask | (tmp >>> 9);
    // at this line we have all parts of the float: sign, exponent and fractions. Let's build mantissa
    BigDecimal mantissa = BigDecimal.ZERO;
    for (int i = 0; i < 24; i ++) {
        if ((fraction & mask) == mask) {
            // i'm not sure about speed, maybe division at each iteration might be faster than pow
            mantissa = mantissa.add(divisor.pow(-i, context));
        }
        mask >>>= 1;
    }

    // it was the core line where I was losing accuracy, because of context
    BigDecimal decimal = mantissa.multiply(divisor.pow(exponent, context), context);
    String str = decimal.setScale(17, RoundingMode.HALF_UP).toPlainString();
    // add minus manually, because java lost it if after the scale value become 0, C++ version of code doesn't do it
    if (sign) {
        str = "-" + str;
    }
    return str;

Maybe topic is useless. Who really need to have the same implementation like C++ has? But at least this code keeps all precision for float number comparing to the most popular way converting float to decimal string:

    return BigDecimal.valueOf(1.23e30f).setScale(17, RoundingMode.HALF_UP).toPlainString();
一枫情书 2025-02-20 17:19:58

您使用的C ++实现使用了 float 的IEEE-754 binary32格式。以这种格式,壁橱的代表值为0.123•10 30 为123,000,004,117,117,574,256,822,262,262,431,744,以binary32格式表示为 +13,023,132•2 73 。因此 0.123E30F 在源代码中产生的编号为123,000,004,117,574,256,822,262,262,431,744。 (因为数字表示为 +13,023,132•2 73 ,我们知道它的价值正是123,000,004,117,117,574,256,256,822,262,262,262,431,744,即使Digit

'您使用%。17F格式化,您的C ++实现忠实地打印了确切的值,产生“ 12300000411757425682222222222222222431744.00000000000000000”。 C ++标准不需要这种准确性,并且某些C ++实现不会准确地进行转换。

Java规范还不需要浮点值的格式化,至少在某些格式化操作中。 (我在这里从内存和一些假设转移;我没有引用。)它允许,甚至可能需要,只能产生一定数量的正确数字小数点或要求的格式。

这个数字的精度比浮子要高得多。

对于 float 格式中表示的任何值,该值具有无限的精度。数量 +13,023,132•2 73 正好 +13,023,132•2 73 ,恰好是123,000,004,004,1174,1174,574,256,256,822,262,262,262,262,262,262,431,744444444444444 4。该格式表示数字的精度仅影响它可以表示的数字,而不是它准确地代表其所代表的数字。

The C++ implementation you are using uses the IEEE-754 binary32 format for float. In this format, the closet representable value to 0.123•1030 is 123,000,004,117,574,256,822,262,431,744, which is represented in the binary32 format as +13,023,132•273. So 0.123e30f in the source code yields the number 123,000,004,117,574,256,822,262,431,744. (Because the number is represented as +13,023,132•273, we know its value is that exactly, which is 123,000,004,117,574,256,822,262,431,744, even though the digits “123000004117574256822262431744” are not stored directly.)

Then, when you format it with %.17f, your C++ implementation prints the exact value faithfully, yielding “123000004117574256822262431744.00000000000000000”. This accuracy is not required by the C++ standard, and some C++ implementations will not do the conversion exactly.

The Java specification also does not require formatting of floating-point values to be exact, at least in some formatting operations. (I am going from memory and some supposition here; I do not have a citation at hand.) It allows, perhaps even requires, that only a certain number of correct digits be produced, after which zeros are used if needed for positioning relative to the decimal point or for the requested format.

The number has much higher precision than float can be.

For any value represented in the float format, that value has infinite precision. The number +13,023,132•273 is exactly +13,023,132•273, which is exactly 123,000,004,117,574,256,822,262,431,744, to infinite precision. The precision the format has for representing numbers affects only which numbers it can represent, not how precisely it represents the numbers that it does represent.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文