这与如何实现这些功能/它们如何在后台工作确实是同一个问题。我只想谈谈这个答案中的输入;我不确定哪些算法对 float->string 有好处。
操作系统提供的功能让您可以读取/写入(打印)字符,一次一个或一个块。问题中有趣的 / FP-specific 部分只是 float->string 和 string->float 部分。其他一切都与读取/打印整数相同(模调用约定差异:浮点数通常在 FP 寄存器中返回)。
如果您希望结果始终正确四舍五入到最接近的可表示 FP 值,则正确实现 strtod (string to double) 和单精度等效项是非常重要的,特别是如果您希望它也高效并适用于输入直到double 可以容纳的最大有限值的极限。
一旦您了解了算法的详细信息(就查看单个数字和执行 FP 乘法/除法/加法,或对 FP 位模式的整数运算而言),您可以在 asm 中为您的任何平台实现它像。出于某种原因,您在示例中使用了 x87 finit 指令。
请参阅http://www.exploringbinary.com/how-glibc-strtod-works/ 了解 glibc 实现的详细信息,http://www.exploringbinary.com/how-strtod-works-and-sometimes-doesnt/ 了解另一种广泛使用的实现。
概述第一篇文章,glibc 的strtod 使用扩展精度整数 算术。它解析输入的十进制字符串以确定整数部分和小数部分。例如456.833e2(科学记数法)的整数部分为45683,小数部分为0.3。
它将两个部分分别转换为浮点数。整数部分很简单,因为已经有硬件支持将整数转换为浮点数。例如x87 fild 或 SSE2 cvtsi2sd,或其他架构上的其他任何东西。但是如果整数部分大于最大64位整数,就没有那么简单了,需要将BigInteger转成float/double,硬件不支持。
请注意,即使是 IEEE binary32 的 FLT_MAX(单精度)float 也是 (2 − 2^−23) × 2^127,它只是 略 低于 2^128,因此您可以使用 128 位整数字符串->float,如果是这样,那么正确的float 结果是+Infinity。 FLT_MAX bit pattern is 0x7f7fffff:尾数全一 = 1.999... 最大指数。十进制是~3.4 × 10^38。
但是,如果您不关心效率,我认为您可以将每个数字转换为 float(或索引已转换的 float 值的数组),然后执行通常的 total = total*10 + digit,或这个案例total = total*10.0 + digit_values[digit]。 FP mul / add 精确到两个相邻可表示值相距大于 1.0 的整数(即当 nextafter(total, +Infinity) 为 total+2.0 时),即当 1 ulp 大于 1.0 时。
实际上,要获得正确的四舍五入,您需要先添加小值,否则它们各自单独向下四舍五入,当它们加在一起时,它们可能会将一个大值增加到下一个可表示的值。 p>
因此,如果您小心操作,您可能可以使用 FPU,例如以 8 位数字块工作并缩放 10^8 或其他东西,并从最小的开始添加。您可以将每个 8 位字符串转换为整数并使用硬件int->float。
小数部分更复杂,尤其是如果您想避免重复除以 10 来获得位置值,您应该避免这种情况,因为它很慢并且因为 1/10 在二进制浮点,因此如果您以“明显”的方式进行操作,所有位值都会出现舍入错误。
但如果整数部分非常大,double 的所有 53 个尾数位可能已经由整数部分确定。所以 glibc 会进行检查,并且只进行大整数除法以从小数部分获取所需的位数(如果有的话)。
无论如何,我强烈建议您阅读这两篇文章。
顺便说一句,如果您不熟悉 IEEE754 binary64(又名 double)用来表示数字的位模式,请参阅 https://en.wikipedia.org/wiki/Double-precision_floating-point_format。您需要编写一个简单的实现,但它确实有助于理解 float。而对于 x86 SSE,您需要知道符号位在哪里来实现绝对值 (ANDPS) 或否定 (XORPS)。 Fastest way to compute absolute value using SSE。 abs 或 neg 没有特殊说明,您只需使用布尔运算来操作符号位。 (比从零中减去要高效得多。)
如果您不关心是否准确到最后一个 ULP(最后一位的单位 = 尾数的最低位),那么您可以执行一个更简单的算法,即乘以 10 并添加类似字符串 -> 整数,最后按 10 的幂进行缩放。
但是一个健壮的库函数不能做到这一点,因为创建一个比最终结果大很多倍的临时值意味着对于@987654362范围内的某些输入,它将溢出(到+/- Infinity) @ 可以代表。或者如果您创建较小的临时值,可能会下溢到+/- 0.0。
分别处理整数和小数部分可以避免溢出问题。
请参阅this C implementation on codereview.SE 以获取一个可能会溢出的非常简单的乘法/加法方法的示例。我只是快速浏览了一下,但我没有看到它拆分整数/小数部分。它只处理科学记数法E99 或末尾的任何内容,重复乘以或除以 10。